]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9616 Add public key in Component of CE
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 30 Aug 2017 09:50:05 +0000 (11:50 +0200)
committerJanos Gyerik <janos.gyerik@sonarsource.com>
Tue, 12 Sep 2017 09:34:55 +0000 (11:34 +0200)
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/Component.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentRootBuilder.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilder.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/BuildComponentTreeStep.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilderTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ReportComponent.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ViewsComponent.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/BuildComponentTreeStepTest.java

index 624d16b58b99b000b234bbafa36b9a2f1aff53c8..de4cc6ee2c839cb1a4a0e1feac814e61746dd983 100644 (file)
@@ -58,7 +58,7 @@ public interface MutableAnalysisMetadataHolder extends AnalysisMetadataHolder {
   /**
    * @throws IllegalStateException if branch has already been set
    */
-  MutableAnalysisMetadataHolder setBranch(@Nullable  Branch branch);
+  MutableAnalysisMetadataHolder setBranch(@Nullable Branch branch);
 
   /**
    * @throws IllegalStateException if project has already been set
index a2be3b3a09764a121b09cf3a80a4eba6984b249b..20b43ed1ae9855178445335cf99aeefbabf40028 100644 (file)
@@ -78,6 +78,14 @@ public interface Component {
    */
   String getKey();
 
+  /**
+   * Returns the key as it will be displayed in the ui.
+   * If legacy branch feature is used, the key will contain the branch name
+   * If new branch feature is used, the key will not contain the branch name
+   */
+  // TODO to be renamed getKey() and rename existing getKey to getDbKey
+  String getPublicKey();
+
   /**
    * The component name.
    */
index e7686fa9d2ff1087f6c687cea640a279e8f65c15..100c92cdae99e787c22d72e7e2a69f18b05ab30f 100644 (file)
@@ -38,6 +38,7 @@ public class ComponentImpl implements Component {
   private final Status status;
   private final String name;
   private final String key;
+  private final String publicKey;
   private final String uuid;
 
   @CheckForNull
@@ -52,6 +53,7 @@ public class ComponentImpl implements Component {
     this.type = builder.type;
     this.status = builder.status;
     this.key = builder.key;
+    this.publicKey = builder.publicKey;
     this.name = builder.name;
     this.description = builder.description;
     this.uuid = builder.uuid;
@@ -80,6 +82,11 @@ public class ComponentImpl implements Component {
     return key;
   }
 
+  @Override
+  public String getPublicKey() {
+    return publicKey;
+  }
+
   @Override
   public String getName() {
     return this.name;
@@ -139,6 +146,7 @@ public class ComponentImpl implements Component {
     private ReportAttributes reportAttributes;
     private String uuid;
     private String key;
+    private String publicKey;
     private String name;
     private String description;
     private FileAttributes fileAttributes;
@@ -173,6 +181,11 @@ public class ComponentImpl implements Component {
       return this;
     }
 
+    public Builder setPublicKey(String publicKey) {
+      this.publicKey = requireNonNull(publicKey);
+      return this;
+    }
+
     public Builder setName(String name) {
       this.name = requireNonNull(name, NAME_CANNOT_BE_NULL);
       return this;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentRootBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentRootBuilder.java
deleted file mode 100644 (file)
index 75a964a..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.computation.task.projectanalysis.component;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Optional;
-import com.google.common.base.Supplier;
-import java.util.function.Function;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.SnapshotDto;
-import org.sonar.scanner.protocol.output.ScannerReport;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.toArray;
-import static java.lang.String.format;
-import static org.apache.commons.lang.StringUtils.trimToNull;
-import static org.sonar.core.component.ComponentKeys.createEffectiveKey;
-import static org.sonar.core.component.ComponentKeys.createKey;
-import static org.sonar.core.util.stream.MoreCollectors.toList;
-
-public class ComponentRootBuilder {
-  private static final String DEFAULT_PROJECT_VERSION = "not provided";
-
-  /**
-   * Will supply the UUID for any component in the tree, given it's key.
-   * <p>
-   * The String argument of the {@link Function#apply(Object)} method is the component's key.
-   * </p>
-   */
-  private final Function<String, String> uuidSupplier;
-  /**
-   * Will supply the {@link ScannerReport.Component} of all the components in the component tree as we crawl it from the
-   * root.
-   * <p>
-   * The Integer argument of the {@link Function#apply(Object)} method is the component's ref.
-   * </p>
-   */
-  private final Function<Integer, ScannerReport.Component> scannerComponentSupplier;
-  /**
-   * Will supply the ComponentDto of the project (if it exists) if we need it to get the name of the project
-   *
-   * @see #nameOfProject(ScannerReport.Component, String, Supplier)
-   */
-  private final Supplier<Optional<ComponentDto>> projectDtoSupplier;
-  /**
-   * Will supply the SnapshotDto of the base analysis of the project (if it exists) if we need it to get the version
-   * of the project.
-   * <p>
-   * The String argument of the {@link Function#apply(Object)} method is the project's UUID.
-   * </p>
-   *
-   * @see #createProjectVersion(ScannerReport.Component, String, Function)
-   */
-  private final Function<String, Optional<SnapshotDto>> analysisSupplier;
-  @CheckForNull
-  private final String branch;
-
-  public ComponentRootBuilder(@Nullable String branch,
-    Function<String, String> uuidSupplier,
-    Function<Integer, ScannerReport.Component> scannerComponentSupplier,
-    Supplier<Optional<ComponentDto>> projectDtoSupplier,
-    Function<String, Optional<SnapshotDto>> analysisSupplier) {
-    this.uuidSupplier = uuidSupplier;
-    this.scannerComponentSupplier = scannerComponentSupplier;
-    this.projectDtoSupplier = projectDtoSupplier;
-    this.branch = branch;
-    this.analysisSupplier = analysisSupplier;
-  }
-
-  public Component build(ScannerReport.Component reportProject, String projectKey) {
-    return buildComponent(reportProject, projectKey);
-  }
-
-  private ComponentImpl buildComponent(ScannerReport.Component reportComponent, String latestModuleKey) {
-    switch (reportComponent.getType()) {
-      case PROJECT:
-        return buildProjectComponent(reportComponent, latestModuleKey);
-      case MODULE:
-        String moduleKey = createKey(reportComponent.getKey(), branch);
-        return buildOtherComponent(reportComponent, moduleKey, moduleKey);
-      case DIRECTORY:
-      case FILE:
-        return buildOtherComponent(reportComponent, createEffectiveKey(latestModuleKey, reportComponent.getPath()), latestModuleKey);
-      default:
-        throw new IllegalArgumentException(format("Unsupported component type '%s'", reportComponent.getType()));
-    }
-  }
-
-  private ComponentImpl buildProjectComponent(ScannerReport.Component reportComponent, String latestModuleKey) {
-    ComponentImpl.Builder builder = createCommonBuilder(reportComponent, latestModuleKey, latestModuleKey);
-    return builder
-      .setName(nameOfProject(reportComponent, latestModuleKey, projectDtoSupplier))
-      .setReportAttributes(createProjectReportAttributes(reportComponent, builder.getUuid(), analysisSupplier))
-      .build();
-  }
-
-  private ComponentImpl buildOtherComponent(ScannerReport.Component reportComponent, String componentKey, String latestModuleKey) {
-    return createCommonBuilder(reportComponent, componentKey, latestModuleKey)
-      .setName(nameOfOthers(reportComponent, componentKey))
-      .setReportAttributes(createOtherReportAttributes(reportComponent))
-      .build();
-  }
-
-  private ComponentImpl.Builder createCommonBuilder(ScannerReport.Component reportComponent, String componentKey, String latestModuleKey) {
-    return ComponentImpl.builder(convertType(reportComponent.getType()))
-      .setUuid(uuidSupplier.apply(componentKey))
-      .setKey(componentKey)
-      .setStatus(convertStatus(reportComponent.getStatus()))
-      .setDescription(trimToNull(reportComponent.getDescription()))
-      .setFileAttributes(createFileAttributes(reportComponent))
-      .addChildren(toArray(buildChildren(reportComponent, latestModuleKey), Component.class));
-  }
-
-  private Iterable<Component> buildChildren(ScannerReport.Component component, String latestModuleKey) {
-    return component.getChildRefList()
-      .stream()
-      .map(componentRef -> buildComponent(scannerComponentSupplier.apply(componentRef), latestModuleKey))
-      .collect(toList(component.getChildRefList().size()));
-  }
-
-  private static String nameOfProject(ScannerReport.Component project, String projectKey, Supplier<Optional<ComponentDto>> projectDtoSupplier) {
-    String name = trimToNull(project.getName());
-    if (name == null) {
-      return projectDtoSupplier.get().transform(ComponentDto::name).or(projectKey);
-    }
-    return name;
-  }
-
-  private static String nameOfOthers(ScannerReport.Component reportComponent, String componentKey) {
-    String name = trimToNull(reportComponent.getName());
-    return name == null ? componentKey : name;
-  }
-
-  @VisibleForTesting
-  static ReportAttributes createProjectReportAttributes(ScannerReport.Component component,
-    String projectUuid, Function<String, Optional<SnapshotDto>> analysisSupplier) {
-    return createCommonBuilder(component)
-      .setVersion(createProjectVersion(component, projectUuid, analysisSupplier))
-      .build();
-  }
-
-  private static String createProjectVersion(ScannerReport.Component component,
-    String projectUuid, Function<String, Optional<SnapshotDto>> analysisSupplier) {
-    String version = trimToNull(component.getVersion());
-    if (version != null) {
-      return version;
-    }
-    Optional<SnapshotDto> snapshotDto = analysisSupplier.apply(projectUuid);
-    if (snapshotDto.isPresent()) {
-      return MoreObjects.firstNonNull(snapshotDto.get().getVersion(), DEFAULT_PROJECT_VERSION);
-    }
-    return DEFAULT_PROJECT_VERSION;
-  }
-
-  @VisibleForTesting
-  static ReportAttributes createOtherReportAttributes(ScannerReport.Component component) {
-    return createCommonBuilder(component)
-      .setVersion(trimToNull(component.getVersion()))
-      .build();
-  }
-
-  private static ReportAttributes.Builder createCommonBuilder(ScannerReport.Component component) {
-    return ReportAttributes.newBuilder(component.getRef())
-      .setPath(trimToNull(component.getPath()));
-  }
-
-  @VisibleForTesting
-  @CheckForNull
-  static FileAttributes createFileAttributes(ScannerReport.Component component) {
-    if (component.getType() != ScannerReport.Component.ComponentType.FILE) {
-      return null;
-    }
-
-    checkArgument(component.getLines() > 0, "File '%s' has no line", component.getPath());
-    return new FileAttributes(
-      component.getIsTest(),
-      trimToNull(component.getLanguage()),
-      component.getLines());
-  }
-
-  static Component.Status convertStatus(ScannerReport.Component.FileStatus status) {
-    switch(status) {
-      case ADDED:
-        return Component.Status.ADDED;
-      case SAME:
-        return Component.Status.SAME;
-      case CHANGED:
-        return Component.Status.CHANGED;
-      case UNAVAILABLE:
-        return Component.Status.UNAVAILABLE;
-      case UNRECOGNIZED:
-      default:
-        throw new IllegalArgumentException("Unsupported ComponentType value " + status);
-    }
-  }
-
-  @VisibleForTesting
-  static Component.Type convertType(ScannerReport.Component.ComponentType type) {
-    switch (type) {
-      case PROJECT:
-        return Component.Type.PROJECT;
-      case MODULE:
-        return Component.Type.MODULE;
-      case DIRECTORY:
-        return Component.Type.DIRECTORY;
-      case FILE:
-        return Component.Type.FILE;
-      default:
-        throw new IllegalArgumentException("Unsupported ComponentType value " + type);
-    }
-  }
-}
index 3b8439a95334e978277de6d81dbff86403dc893a..ec277ceac4f1212878f1cfbbab16a950cf2163eb 100644 (file)
@@ -37,6 +37,7 @@ public class ComponentTreeBuilder {
   private static final String DEFAULT_PROJECT_VERSION = "not provided";
 
   private final ComponentKeyGenerator keyGenerator;
+  private final ComponentKeyGenerator publicKeyGenerator;
   /**
    * Will supply the UUID for any component in the tree, given it's key.
    * <p>
@@ -61,12 +62,14 @@ public class ComponentTreeBuilder {
 
   public ComponentTreeBuilder(
     ComponentKeyGenerator keyGenerator,
+    ComponentKeyGenerator publicKeyGenerator,
     Function<String, String> uuidSupplier,
     Function<Integer, ScannerReport.Component> scannerComponentSupplier,
     Project project,
     @Nullable SnapshotDto baseAnalysis) {
 
     this.keyGenerator = keyGenerator;
+    this.publicKeyGenerator = publicKeyGenerator;
     this.uuidSupplier = uuidSupplier;
     this.scannerComponentSupplier = scannerComponentSupplier;
     this.project = project;
@@ -93,6 +96,7 @@ public class ComponentTreeBuilder {
         return ComponentImpl.builder(Component.Type.PROJECT)
           .setUuid(uuid)
           .setKey(projectKey)
+          .setPublicKey(publicKeyGenerator.generateKey(component, null))
           .setName(nameOfProject(component))
           .setStatus(convertStatus(component.getStatus()))
           .setDescription(trimToNull(component.getDescription()))
@@ -107,6 +111,7 @@ public class ComponentTreeBuilder {
         return ComponentImpl.builder(Component.Type.MODULE)
           .setUuid(uuidSupplier.apply(moduleKey))
           .setKey(moduleKey)
+          .setPublicKey(publicKeyGenerator.generateKey(component, null))
           .setName(nameOfOthers(component, moduleKey))
           .setStatus(convertStatus(component.getStatus()))
           .setDescription(trimToNull(component.getDescription()))
@@ -120,6 +125,7 @@ public class ComponentTreeBuilder {
         return ComponentImpl.builder(convertDirOrFileType(component.getType()))
           .setUuid(uuidSupplier.apply(key))
           .setKey(key)
+          .setPublicKey(publicKeyGenerator.generateKey(closestModule, component))
           .setName(nameOfOthers(component, key))
           .setStatus(convertStatus(component.getStatus()))
           .setDescription(trimToNull(component.getDescription()))
index 11a5e840fae7c6fbafa3f822d607dfc3daa8e064..6a029818e4f6645c94c2ca0892a7898ad9d4e490 100644 (file)
@@ -23,25 +23,23 @@ import java.util.Optional;
 import java.util.stream.Stream;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
-import org.sonar.core.component.ComponentKeys;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.component.SnapshotQuery;
 import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.server.computation.task.projectanalysis.analysis.Analysis;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
 import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolder;
 import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReader;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.component.ComponentKeyGenerator;
 import org.sonar.server.computation.task.projectanalysis.component.ComponentTreeBuilder;
 import org.sonar.server.computation.task.projectanalysis.component.ComponentUuidFactory;
+import org.sonar.server.computation.task.projectanalysis.component.DefaultBranchImpl;
 import org.sonar.server.computation.task.projectanalysis.component.MutableTreeRootHolder;
 import org.sonar.server.computation.task.step.ComputationStep;
 
-import static org.apache.commons.lang.StringUtils.isEmpty;
-import static org.apache.commons.lang.StringUtils.trimToNull;
-
 /**
  * Populates the {@link MutableTreeRootHolder} and {@link MutableAnalysisMetadataHolder} from the {@link BatchReportReader}
  */
@@ -70,6 +68,7 @@ public class BuildComponentTreeStep implements ComputationStep {
     try (DbSession dbSession = dbClient.openSession(false)) {
       ScannerReport.Component reportProject = reportReader.readComponent(analysisMetadataHolder.getRootComponentRef());
       ComponentKeyGenerator keyGenerator = loadKeyGenerator();
+      ComponentKeyGenerator publicKeyGenerator = loadPublicKeyGenerator();
 
       // root key of branch, not necessarily of project
       String rootKey = keyGenerator.generateKey(reportProject, null);
@@ -80,7 +79,7 @@ public class BuildComponentTreeStep implements ComputationStep {
       String rootUuid = componentUuidFactory.getOrCreateForKey(rootKey);
       SnapshotDto baseAnalysis = loadBaseAnalysis(dbSession, rootUuid);
 
-      ComponentTreeBuilder builder = new ComponentTreeBuilder(keyGenerator,
+      ComponentTreeBuilder builder = new ComponentTreeBuilder(keyGenerator, publicKeyGenerator,
         componentUuidFactory::getOrCreateForKey,
         reportReader::readComponent,
         analysisMetadataHolder.getProject(),
@@ -93,7 +92,7 @@ public class BuildComponentTreeStep implements ComputationStep {
   }
 
   private ComponentKeyGenerator loadKeyGenerator() {
-    return Stream.of(analysisMetadataHolder.getBranch(), Optional.of(new DefaultKeyGenerator()))
+    return Stream.of(analysisMetadataHolder.getBranch(), Optional.of(new DefaultBranchImpl()))
       // TODO pull request generator will be added here
       .filter(Optional::isPresent)
       .flatMap(x -> x.map(Stream::of).orElseGet(Stream::empty))
@@ -101,6 +100,17 @@ public class BuildComponentTreeStep implements ComputationStep {
       .get();
   }
 
+  private ComponentKeyGenerator loadPublicKeyGenerator() {
+    Optional<Branch> branch = analysisMetadataHolder.getBranch();
+    if (!branch.isPresent()) {
+      // Used for pull request
+      return new DefaultBranchImpl();
+    }
+    return branch.filter(Branch::isLegacyFeature)
+      .map(b -> new DefaultBranchImpl(b.getName().orElse(null)))
+      .orElseGet(DefaultBranchImpl::new);
+  }
+
   @CheckForNull
   private SnapshotDto loadBaseAnalysis(DbSession dbSession, String rootUuid) {
     return dbClient.snapshotDao().selectAnalysisByQuery(
@@ -122,14 +132,4 @@ public class BuildComponentTreeStep implements ComputationStep {
     return null;
   }
 
-  private static class DefaultKeyGenerator implements ComponentKeyGenerator {
-    @Override
-    public String generateKey(ScannerReport.Component module, @Nullable ScannerReport.Component fileOrDir) {
-      String moduleKey = module.getKey();
-      if (fileOrDir == null || isEmpty(fileOrDir.getPath())) {
-        return moduleKey;
-      }
-      return ComponentKeys.createEffectiveKey(moduleKey, trimToNull(fileOrDir.getPath()));
-    }
-  }
 }
index 6697a44b2666d3fc99afc49de9919efe50a32e96..ce41606865c29e5121cad2375a28586eb5bc2196 100644 (file)
@@ -38,18 +38,20 @@ import org.sonar.server.computation.task.projectanalysis.analysis.Project;
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
+import static org.sonar.scanner.protocol.output.ScannerReport.Component.newBuilder;
 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.DIRECTORY;
 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.FILE;
 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.MODULE;
 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.PROJECT;
 import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.UNRECOGNIZED;
-import static org.sonar.scanner.protocol.output.ScannerReport.Component.newBuilder;
 import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
 
 public class ComponentTreeBuilderTest {
 
   private static final ComponentKeyGenerator KEY_GENERATOR = (module, component) -> "generated_"
     + ComponentKeys.createEffectiveKey(module.getKey(), component != null ? component.getPath() : null);
+  private static final ComponentKeyGenerator PUBLIC_KEY_GENERATOR = (module, component) -> "public_"
+    + ComponentKeys.createEffectiveKey(module.getKey(), component != null ? component.getPath() : null);
   private static final Function<String, String> UUID_SUPPLIER = (componentKey) -> componentKey + "_uuid";
   private static final EnumSet<ScannerReport.Component.ComponentType> REPORT_TYPES = EnumSet.of(PROJECT, MODULE, DIRECTORY, FILE);
 
@@ -93,6 +95,7 @@ public class ComponentTreeBuilderTest {
 
     assertThat(root.getUuid()).isEqualTo("generated_K1_uuid");
     assertThat(root.getKey()).isEqualTo("generated_K1");
+    assertThat(root.getPublicKey()).isEqualTo("public_K1");
     assertThat(root.getType()).isEqualTo(Component.Type.PROJECT);
     assertThat(root.getName()).isEqualTo(nameInReport);
     assertThat(root.getDescription()).isEqualTo(descriptionInReport);
@@ -176,23 +179,27 @@ public class ComponentTreeBuilderTest {
 
     Component root = call(project);
     assertThat(root.getKey()).isEqualTo("generated_" + projectInDb.getKey());
+    assertThat(root.getPublicKey()).isEqualTo("public_" + projectInDb.getKey());
     assertThat(root.getChildren()).hasSize(1);
 
     Component module = root.getChildren().iterator().next();
     assertThat(module.getKey()).isEqualTo("generated_M");
+    assertThat(module.getPublicKey()).isEqualTo("public_M");
     assertThat(module.getChildren()).hasSize(1);
 
     Component directory = module.getChildren().iterator().next();
     assertThat(directory.getKey()).isEqualTo("generated_M:src/js");
+    assertThat(directory.getPublicKey()).isEqualTo("public_M:src/js");
     assertThat(directory.getChildren()).hasSize(1);
 
     Component file = directory.getChildren().iterator().next();
     assertThat(file.getKey()).isEqualTo("generated_M:src/js/Foo.js");
+    assertThat(file.getPublicKey()).isEqualTo("public_M:src/js/Foo.js");
     assertThat(file.getChildren()).isEmpty();
   }
 
   @Test
-  public void names_of_module_directory_and_file_are_keys_if_names_are_absent_from_report() {
+  public void names_of_module_directory_and_file_are_public_keys_if_names_are_absent_from_report() {
     ScannerReport.Component project = newBuilder()
       .setType(PROJECT)
       .setKey(projectInDb.getKey())
@@ -276,7 +283,7 @@ public class ComponentTreeBuilderTest {
   }
 
   @Test
-  public void name_of_module_directory_and_files_includes_name_of_closest_module() {
+  public void keys_of_module_directory_and_files_includes_name_of_closest_module() {
     ScannerReport.Component project = newBuilder()
       .setType(PROJECT)
       .setKey("project 1")
@@ -301,20 +308,35 @@ public class ComponentTreeBuilderTest {
     Component root = call(project);
     Map<Integer, Component> componentsByRef = indexComponentByRef(root);
     assertThat(componentsByRef.get(11).getKey()).isEqualTo("generated_module 1");
+    assertThat(componentsByRef.get(11).getPublicKey()).isEqualTo("public_module 1");
     assertThat(componentsByRef.get(12).getKey()).isEqualTo("generated_module 2");
+    assertThat(componentsByRef.get(12).getPublicKey()).isEqualTo("public_module 2");
     assertThat(componentsByRef.get(13).getKey()).isEqualTo("generated_module 3");
+    assertThat(componentsByRef.get(13).getPublicKey()).isEqualTo("public_module 3");
     assertThat(componentsByRef.get(21).getKey()).startsWith("generated_project 1:");
+    assertThat(componentsByRef.get(21).getPublicKey()).startsWith("public_project 1:");
     assertThat(componentsByRef.get(22).getKey()).startsWith("generated_module 1:");
+    assertThat(componentsByRef.get(22).getPublicKey()).startsWith("public_module 1:");
     assertThat(componentsByRef.get(23).getKey()).startsWith("generated_module 2:");
+    assertThat(componentsByRef.get(23).getPublicKey()).startsWith("public_module 2:");
     assertThat(componentsByRef.get(24).getKey()).startsWith("generated_module 3:");
+    assertThat(componentsByRef.get(24).getPublicKey()).startsWith("public_module 3:");
     assertThat(componentsByRef.get(31).getKey()).startsWith("generated_project 1:");
+    assertThat(componentsByRef.get(31).getPublicKey()).startsWith("public_project 1:");
     assertThat(componentsByRef.get(32).getKey()).startsWith("generated_module 1:");
+    assertThat(componentsByRef.get(32).getPublicKey()).startsWith("public_module 1:");
     assertThat(componentsByRef.get(33).getKey()).startsWith("generated_module 2:");
+    assertThat(componentsByRef.get(33).getPublicKey()).startsWith("public_module 2:");
     assertThat(componentsByRef.get(34).getKey()).startsWith("generated_module 3:");
+    assertThat(componentsByRef.get(34).getPublicKey()).startsWith("public_module 3:");
     assertThat(componentsByRef.get(35).getKey()).startsWith("generated_project 1:");
+    assertThat(componentsByRef.get(35).getPublicKey()).startsWith("public_project 1:");
     assertThat(componentsByRef.get(36).getKey()).startsWith("generated_module 1:");
+    assertThat(componentsByRef.get(36).getPublicKey()).startsWith("public_module 1:");
     assertThat(componentsByRef.get(37).getKey()).startsWith("generated_module 2:");
+    assertThat(componentsByRef.get(37).getPublicKey()).startsWith("public_module 2:");
     assertThat(componentsByRef.get(38).getKey()).startsWith("generated_module 3:");
+    assertThat(componentsByRef.get(38).getPublicKey()).startsWith("public_module 3:");
   }
 
   @Test
@@ -718,7 +740,7 @@ public class ComponentTreeBuilderTest {
   }
 
   private ComponentTreeBuilder newUnderTest(@Nullable SnapshotDto baseAnalysis) {
-    return new ComponentTreeBuilder(KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, projectInDb, baseAnalysis);
+    return new ComponentTreeBuilder(KEY_GENERATOR, PUBLIC_KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, projectInDb, baseAnalysis);
   }
 
   private static Map<Integer, Component> indexComponentByRef(Component root) {
index f4f276798030c681b183b0ac28509d7a31e6e1c9..e3baec1c1882ad60b7674669d46fbd45cfea4b21 100644 (file)
@@ -22,13 +22,13 @@ package org.sonar.server.computation.task.projectanalysis.component;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 import static java.util.Arrays.asList;
+import static java.util.Objects.requireNonNull;
 
 /**
  * Implementation of {@link Component} to unit test report components.
@@ -45,6 +45,7 @@ public class ReportComponent implements Component {
   @CheckForNull
   private final String description;
   private final String key;
+  private final String publicKey;
   private final String uuid;
   private final ReportAttributes reportAttributes;
   private final FileAttributes fileAttributes;
@@ -54,6 +55,7 @@ public class ReportComponent implements Component {
     this.type = builder.type;
     this.status = builder.status;
     this.key = builder.key;
+    this.publicKey = builder.publicKey;
     this.name = builder.name == null ? String.valueOf(builder.key) : builder.name;
     this.description = builder.description;
     this.uuid = builder.uuid;
@@ -91,6 +93,14 @@ public class ReportComponent implements Component {
     return key;
   }
 
+  @Override
+  public String getPublicKey() {
+    if (publicKey == null) {
+      throw new UnsupportedOperationException(String.format("Component key of ref '%d' has not be fed yet", this.reportAttributes.getRef()));
+    }
+    return publicKey;
+  }
+
   @Override
   public String getName() {
     return this.name;
@@ -169,6 +179,7 @@ public class ReportComponent implements Component {
     private Status status;
     private String uuid;
     private String key;
+    private String publicKey;
     private String name;
     private String version;
     private String description;
@@ -183,12 +194,12 @@ public class ReportComponent implements Component {
     }
 
     public Builder setStatus(Status s) {
-      this.status = Objects.requireNonNull(s);
+      this.status = requireNonNull(s);
       return this;
     }
 
     public Builder setUuid(String s) {
-      this.uuid = Objects.requireNonNull(s);
+      this.uuid = requireNonNull(s);
       return this;
     }
 
@@ -198,7 +209,12 @@ public class ReportComponent implements Component {
     }
 
     public Builder setKey(String s) {
-      this.key = Objects.requireNonNull(s);
+      this.key = requireNonNull(s);
+      return this;
+    }
+
+    public Builder setPublicKey(String publicKey) {
+      this.publicKey = requireNonNull(publicKey);
       return this;
     }
 
index b9fbf9890251ce269d415c2f230917d351b95642..5c249e3cad955ef3dfa96c81e699bd785c6d5b1f 100644 (file)
@@ -152,6 +152,14 @@ public class ViewsComponent implements Component {
     return key;
   }
 
+  /**
+   * Views has no branch feature, the public key is the same as the key
+   */
+  @Override
+  public String getPublicKey() {
+    return getKey();
+  }
+
   @Override
   public String getName() {
     checkState(this.name != null, "No name has been set");
index 072d0ae3170921ec73fd314feb5a9c20a9c1049c..dc330355cd29f5053db75966c176974513eea761 100644 (file)
@@ -24,6 +24,7 @@ import com.tngtech.java.junit.dataprovider.DataProviderRunner;
 import com.tngtech.java.junit.dataprovider.UseDataProvider;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
@@ -37,10 +38,12 @@ import org.sonar.db.organization.OrganizationDto;
 import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType;
 import org.sonar.scanner.protocol.output.ScannerReport.Component.FileStatus;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
 import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule;
 import org.sonar.server.computation.task.projectanalysis.analysis.Project;
 import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReaderRule;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.server.computation.task.projectanalysis.component.DefaultBranchImpl;
 import org.sonar.server.computation.task.projectanalysis.component.MutableTreeRootHolderRule;
 
 import static com.google.common.base.Predicates.in;
@@ -48,6 +51,9 @@ import static com.google.common.base.Predicates.not;
 import static com.google.common.collect.FluentIterable.from;
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 import static org.sonar.db.component.ComponentTesting.newDirectory;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newModuleDto;
@@ -83,21 +89,23 @@ public class BuildComponentTreeStepTest {
   @Rule
   public DbTester dbTester = DbTester.create(System2.INSTANCE);
   @Rule
-  public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+  public BatchReportReaderRule reportReader = new BatchReportReaderRule()
+    .setMetadata(ScannerReport.Metadata.newBuilder()
+      .setProjectKey(REPORT_PROJECT_KEY)
+      .setRootComponentRef(ROOT_REF)
+      .build());
   @Rule
   public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule();
   @Rule
-  public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule()
-    .setRootComponentRef(ROOT_REF)
-    .setAnalysisDate(ANALYSIS_DATE)
-    .setBranch(null)
-    .setProject(new Project("U1", REPORT_PROJECT_KEY, REPORT_PROJECT_KEY));
+  public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule();
 
   private DbClient dbClient = dbTester.getDbClient();
   private BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
 
   @Test(expected = NullPointerException.class)
   public void fails_if_root_component_does_not_exist_in_reportReader() {
+    setAnalysisMetadataHolder();
+
     underTest.execute();
   }
 
@@ -115,6 +123,7 @@ public class BuildComponentTreeStepTest {
   @Test
   @UseDataProvider("allComponentTypes")
   public void verify_ref_and_type(ComponentType componentType) {
+    setAnalysisMetadataHolder();
     int componentRef = 1;
     reportReader.putComponent(component(componentRef, componentType));
 
@@ -129,6 +138,7 @@ public class BuildComponentTreeStepTest {
 
   @Test
   public void verify_tree_is_correctly_built() {
+    setAnalysisMetadataHolder();
     reportReader.putComponent(component(ROOT_REF, PROJECT, MODULE_REF));
     reportReader.putComponent(component(MODULE_REF, MODULE, DIR_REF_1, DIR_REF_2));
     reportReader.putComponent(component(DIR_REF_1, DIRECTORY, FILE_1_REF, FILE_2_REF));
@@ -155,6 +165,7 @@ public class BuildComponentTreeStepTest {
 
   @Test
   public void compute_keys_and_uuids() {
+    setAnalysisMetadataHolder();
     reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF));
     reportReader.putComponent(componentWithKey(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1));
     reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_KEY_1, FILE_1_REF));
@@ -170,6 +181,7 @@ public class BuildComponentTreeStepTest {
 
   @Test
   public void return_existing_uuids() {
+    setAnalysisMetadataHolder();
     OrganizationDto organizationDto = dbTester.organizations().insert();
     ComponentDto project = insertComponent(newPrivateProjectDto(organizationDto, "ABCD").setDbKey(REPORT_PROJECT_KEY));
     ComponentDto module = insertComponent(newModuleDto("BCDE", project).setDbKey(REPORT_MODULE_KEY));
@@ -190,30 +202,99 @@ public class BuildComponentTreeStepTest {
     verifyComponent(FILE_1_REF, REPORT_MODULE_KEY + ":" + REPORT_FILE_KEY_1, "DEFG");
   }
 
-//  @Test
-//  public void use_branch_to_generate_keys() {
-//    MutableAnalysisMetadataHolder analysisMetadataHolder = new AnalysisMetadataHolderImpl()
-//      .setRootComponentRef(ROOT_REF)
-//      .setAnalysisDate(ANALYSIS_DATE)
-//      .setBranch("origin/master");
-//
-//    BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
-//
-//    reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF));
-//    reportReader.putComponent(componentWithKey(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1));
-//    reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_KEY_1, FILE_1_REF));
-//    reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_KEY_1));
-//
-//    underTest.execute();
-//
-//    verifyComponent(ROOT_REF, REPORT_PROJECT_KEY + ":origin/master");
-//    verifyComponent(MODULE_REF, REPORT_MODULE_KEY + ":origin/master");
-//    verifyComponent(DIR_REF_1, REPORT_MODULE_KEY + ":origin/master:" + REPORT_DIR_KEY_1);
-//    verifyComponent(FILE_1_REF, REPORT_MODULE_KEY + ":origin/master:" + REPORT_FILE_KEY_1);
-//  }
+  @Test
+  public void generate_keys_when_using_branch() {
+    Branch branch = mock(Branch.class);
+    when(branch.getName()).thenReturn(Optional.of("origin/feature"));
+    when(branch.isMain()).thenReturn(false);
+    when(branch.isLegacyFeature()).thenReturn(false);
+    when(branch.generateKey(any(ScannerReport.Component.class), any(ScannerReport.Component.class))).thenReturn("generated");
+    analysisMetadataHolder.setRootComponentRef(ROOT_REF)
+      .setAnalysisDate(ANALYSIS_DATE)
+      .setProject(new Project("U1", REPORT_PROJECT_KEY, REPORT_PROJECT_KEY))
+      .setBranch(branch);
+    BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
+    reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF));
+    reportReader.putComponent(componentWithKey(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1));
+    reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_KEY_1, FILE_1_REF));
+    reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_KEY_1));
+
+    underTest.execute();
+
+    verifyComponent(ROOT_REF, "generated", REPORT_PROJECT_KEY, null);
+    verifyComponent(MODULE_REF, "generated", REPORT_MODULE_KEY, null);
+    verifyComponent(DIR_REF_1, "generated", REPORT_MODULE_KEY + ":" + REPORT_DIR_KEY_1, null);
+    verifyComponent(FILE_1_REF, "generated", REPORT_MODULE_KEY + ":" + REPORT_FILE_KEY_1, null);
+  }
+
+  @Test
+  public void generate_keys_when_using_main_branch() {
+    Branch branch = mock(Branch.class);
+    when(branch.getName()).thenReturn(Optional.of("origin/master"));
+    when(branch.isMain()).thenReturn(true);
+    when(branch.isLegacyFeature()).thenReturn(false);
+    when(branch.generateKey(any(ScannerReport.Component.class), any(ScannerReport.Component.class))).thenReturn("generated");
+    analysisMetadataHolder.setRootComponentRef(ROOT_REF)
+      .setAnalysisDate(ANALYSIS_DATE)
+      .setProject(new Project("U1", REPORT_PROJECT_KEY, REPORT_PROJECT_KEY))
+      .setBranch(branch);
+    BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
+    reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF));
+    reportReader.putComponent(componentWithKey(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1));
+    reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_KEY_1, FILE_1_REF));
+    reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_KEY_1));
+
+    underTest.execute();
+
+    verifyComponent(ROOT_REF, "generated", REPORT_PROJECT_KEY, null);
+    verifyComponent(MODULE_REF, "generated", REPORT_MODULE_KEY, null);
+    verifyComponent(DIR_REF_1, "generated", REPORT_MODULE_KEY + ":" + REPORT_DIR_KEY_1, null);
+    verifyComponent(FILE_1_REF, "generated", REPORT_MODULE_KEY + ":" + REPORT_FILE_KEY_1, null);
+  }
+
+  @Test
+  public void generate_keys_when_using_legacy_branch() {
+    analysisMetadataHolder.setRootComponentRef(ROOT_REF)
+      .setAnalysisDate(ANALYSIS_DATE)
+      .setProject(new Project("U1", REPORT_PROJECT_KEY, REPORT_PROJECT_KEY))
+      .setBranch(new DefaultBranchImpl("origin/feature"));
+    BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
+    reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF));
+    reportReader.putComponent(componentWithKey(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1));
+    reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_KEY_1, FILE_1_REF));
+    reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_KEY_1));
+
+    underTest.execute();
+
+    verifyComponent(ROOT_REF, REPORT_PROJECT_KEY + ":origin/feature", null);
+    verifyComponent(MODULE_REF, REPORT_MODULE_KEY + ":origin/feature", null);
+    verifyComponent(DIR_REF_1, REPORT_MODULE_KEY + ":origin/feature:" + REPORT_DIR_KEY_1, null);
+    verifyComponent(FILE_1_REF, REPORT_MODULE_KEY + ":origin/feature:" + REPORT_FILE_KEY_1, null);
+  }
+
+  @Test
+  public void compute_keys_when_no_branch() {
+    analysisMetadataHolder.setRootComponentRef(ROOT_REF)
+      .setAnalysisDate(ANALYSIS_DATE)
+      .setProject(new Project("U1", REPORT_PROJECT_KEY, REPORT_PROJECT_KEY))
+      .setBranch(null);
+    BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
+    reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF));
+    reportReader.putComponent(componentWithKey(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1));
+    reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_KEY_1, FILE_1_REF));
+    reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, REPORT_FILE_KEY_1));
+
+    underTest.execute();
+
+    verifyComponent(ROOT_REF, REPORT_PROJECT_KEY);
+    verifyComponent(MODULE_REF, REPORT_MODULE_KEY);
+    verifyComponent(DIR_REF_1, REPORT_MODULE_KEY + ":" + REPORT_DIR_KEY_1);
+    verifyComponent(FILE_1_REF, REPORT_MODULE_KEY + ":" + REPORT_FILE_KEY_1);
+  }
 
   @Test
   public void compute_keys_and_uuids_on_project_having_module_and_directory() {
+    setAnalysisMetadataHolder();
     reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF, DIR_REF_2));
     reportReader.putComponent(componentWithKey(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1));
     reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_KEY_1, FILE_1_REF));
@@ -233,6 +314,7 @@ public class BuildComponentTreeStepTest {
 
   @Test
   public void compute_keys_and_uuids_on_multi_modules() {
+    setAnalysisMetadataHolder();
     reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF));
     reportReader.putComponent(componentWithKey(MODULE_REF, MODULE, REPORT_MODULE_KEY, 100));
     reportReader.putComponent(componentWithKey(100, MODULE, "SUB_MODULE_KEY", DIR_REF_1));
@@ -250,6 +332,7 @@ public class BuildComponentTreeStepTest {
 
   @Test
   public void return_existing_uuids_when_components_were_removed() {
+    setAnalysisMetadataHolder();
     OrganizationDto organizationDto = dbTester.organizations().insert();
     ComponentDto project = insertComponent(newPrivateProjectDto(organizationDto, "ABCD").setDbKey(REPORT_PROJECT_KEY));
     ComponentDto removedModule = insertComponent(newModuleDto("BCDE", project).setDbKey(REPORT_MODULE_KEY).setEnabled(false));
@@ -273,6 +356,7 @@ public class BuildComponentTreeStepTest {
 
   @Test
   public void set_no_base_project_snapshot_when_no_snapshot() throws Exception {
+    setAnalysisMetadataHolder();
     reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY));
     underTest.execute();
 
@@ -281,6 +365,7 @@ public class BuildComponentTreeStepTest {
 
   @Test
   public void set_no_base_project_snapshot_when_no_last_snapshot() throws Exception {
+    setAnalysisMetadataHolder();
     OrganizationDto organizationDto = dbTester.organizations().insert();
     ComponentDto project = insertComponent(newPrivateProjectDto(organizationDto, "ABCD").setDbKey(REPORT_PROJECT_KEY));
     insertSnapshot(newAnalysis(project).setLast(false));
@@ -293,6 +378,7 @@ public class BuildComponentTreeStepTest {
 
   @Test
   public void set_base_project_snapshot_when_last_snapshot_exist() throws Exception {
+    setAnalysisMetadataHolder();
     OrganizationDto organizationDto = dbTester.organizations().insert();
     ComponentDto project = insertComponent(newPrivateProjectDto(organizationDto, "ABCD").setDbKey(REPORT_PROJECT_KEY));
     insertSnapshot(newAnalysis(project).setLast(true));
@@ -310,13 +396,18 @@ public class BuildComponentTreeStepTest {
   }
 
   private void verifyComponent(int ref, String key) {
-    verifyComponent(ref, key, null);
+    verifyComponent(ref, key, key, null);
   }
 
   private void verifyComponent(int ref, String key, @Nullable String uuid) {
+    verifyComponent(ref, key, key, uuid);
+  }
+
+  private void verifyComponent(int ref, String key, String publicKey, @Nullable String uuid) {
     Map<Integer, Component> componentsByRef = indexAllComponentsInTreeByRef(treeRootHolder.getRoot());
     Component component = componentsByRef.get(ref);
     assertThat(component.getKey()).isEqualTo(key);
+    assertThat(component.getPublicKey()).isEqualTo(publicKey);
     if (uuid != null) {
       assertThat(component.getUuid()).isEqualTo(uuid);
     } else {
@@ -379,4 +470,11 @@ public class BuildComponentTreeStepTest {
     return snapshot;
   }
 
+  private void setAnalysisMetadataHolder() {
+    analysisMetadataHolder.setRootComponentRef(ROOT_REF)
+      .setAnalysisDate(ANALYSIS_DATE)
+      .setBranch(new DefaultBranchImpl(null))
+      .setProject(new Project("U1", REPORT_PROJECT_KEY, REPORT_PROJECT_KEY));
+  }
+
 }