]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9616 compute engine backend to support branches
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 27 Jul 2017 07:12:49 +0000 (09:12 +0200)
committerJanos Gyerik <janos.gyerik@sonarsource.com>
Tue, 12 Sep 2017 08:52:52 +0000 (10:52 +0200)
109 files changed:
server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/property/PropertiesDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/property/PropertiesMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
server/sonar-db-dao/src/main/resources/org/sonar/db/property/PropertiesMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java [new file with mode: 0644]
server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDtoTest.java [new file with mode: 0644]
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/property/PropertiesDaoTest.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/AddBranchColumnToProjectsTable.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/CreateTableProjectBranches.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v66/CreateTableProjectBranchesTest.java
server/sonar-server/src/main/java/org/sonar/server/computation/queue/ReportSubmitter.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolder.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Branch.java [new file with mode: 0644]
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/analysis/Project.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/api/measurecomputer/MeasureComputerContextImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoader.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoaderDelegate.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterDelegate.java [new file with mode: 0644]
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/ComponentKeyGenerator.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilder.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentUuidFactory.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ConfigurationRepository.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ConfigurationRepositoryImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MainBranchImpl.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/TreeRootHolder.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/UuidFactory.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/duplication/CrossProjectDuplicationStatusHolderImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultAssignee.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/filter/IssueFilter.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitygate/QualityGateServiceImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/BuildComponentTreeStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadPeriodsStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistComponentsStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PurgeDatastoresStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ValidateProjectStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/VerifyBillingStep.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTask.java
server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsProxyImpl.java
server/sonar-server/src/main/java/org/sonar/server/permission/PermissionTemplateService.java
server/sonar-server/src/main/java/org/sonar/server/project/ws/BranchesAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsModule.java
server/sonar-server/src/test/java/org/sonar/server/computation/queue/ReportSubmitterTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisImplTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/ProjectTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/api/measurecomputer/MeasureComputerContextImplTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoaderTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentFunctionsTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentRootBuilderTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilderTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentUuidFactoryTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ConfigurationRepositoryTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/MainBranchImplTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/TestSettingsRepository.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/duplication/CrossProjectDuplicationStatusHolderImplTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultAssigneeTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/filter/IssueFilterTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/BuildComponentTreeStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadPeriodsStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStepTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistComponentsStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PurgeDatastoresStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateLoadingStepTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportPersistComponentsStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ValidateProjectStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/VerifyBillingStepTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsPersistComponentsStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTaskTest.java
server/sonar-server/src/test/java/org/sonar/server/permission/PermissionTemplateServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java
sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
sonar-core/src/main/java/org/sonar/core/config/PurgeProperties.java
sonar-core/src/main/java/org/sonar/core/config/ScannerProperties.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/config/WebhookProperties.java
sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java
sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java
sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/viewer/ScannerReportViewerApp.java
sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
sonar-ws/src/main/protobuf/ws-projects.proto
tests/projects/shared/xoo-multi-module-sample-without-project-name-version/.scannerwork/.sonar_lock [new file with mode: 0644]
tests/projects/shared/xoo-multi-module-sample-without-project-name-version/.scannerwork/report-task.txt [new file with mode: 0644]

index a562760d4bbbdb603260a38f4b9eea9ebfa5c7c5..405fbe25e34a448057121dfb2064d1ae0579ed4d 100644 (file)
@@ -148,7 +148,7 @@ public class ComputeEngineContainerImplTest {
           + 25 // level 1
           + 49 // content of DaoModule
           + 3 // content of EsSearchModule
-          + 61 // content of CorePropertyDefinitions
+          + 63 // content of CorePropertyDefinitions
           + 1 // StopFlagContainer
       );
       assertThat(
index c92eb1734e0b4c7062bc2ed39429c50edf832ea6..35c2a91d202702166dca1c1ebbd6b1296ace3603 100644 (file)
@@ -80,10 +80,10 @@ public final class SqTables {
     "perm_tpl_characteristics",
     "plugins",
     "projects",
+    "project_branches",
     "project_links",
     "project_measures",
     "project_qprofiles",
-    "project_branches",
     "properties",
     "qprofile_changes",
     "quality_gates",
index 7f5d672442757aff64eec62235b3fd5584438f13..ec2b6a3ac7995f6443117fb857cc4215fb9bfccc 100644 (file)
@@ -701,7 +701,8 @@ CREATE TABLE "PROJECT_BRANCHES" (
   "BRANCH_TYPE" VARCHAR(5),
   "MERGE_BRANCH_UUID" VARCHAR(50),
   "PULL_REQUEST_TITLE" VARCHAR(4000),
-  "CREATED_AT" BIGINT NOT NULL
+  "CREATED_AT" BIGINT NOT NULL,
+  "UPDATED_AT" BIGINT NOT NULL
 );
 CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID");
 CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE_TYPE", "KEE");
index 246bb15f55925bfc2d73abaf61571777d32f2ae1..f694b925d14154db3c482718e5c8ad83b3120ad5 100644 (file)
@@ -26,6 +26,7 @@ import org.sonar.db.ce.CeQueueDao;
 import org.sonar.db.ce.CeScannerContextDao;
 import org.sonar.db.ce.CeTaskCharacteristicDao;
 import org.sonar.db.ce.CeTaskInputDao;
+import org.sonar.db.component.BranchDao;
 import org.sonar.db.component.ComponentDao;
 import org.sonar.db.component.ComponentKeyUpdaterDao;
 import org.sonar.db.component.ComponentLinkDao;
@@ -75,6 +76,7 @@ public class DbClient {
   private final Database database;
   private final MyBatis myBatis;
   private final DBSessions dbSessions;
+
   private final SchemaMigrationDao schemaMigrationDao;
   private final AuthorizationDao authorizationDao;
   private final OrganizationDao organizationDao;
@@ -123,6 +125,7 @@ public class DbClient {
   private final DefaultQProfileDao defaultQProfileDao;
   private final EsQueueDao esQueueDao;
   private final PluginDao pluginDao;
+  private final BranchDao branchDao;
 
   public DbClient(Database database, MyBatis myBatis, DBSessions dbSessions, Dao... daos) {
     this.database = database;
@@ -181,6 +184,7 @@ public class DbClient {
     defaultQProfileDao = getDao(map, DefaultQProfileDao.class);
     esQueueDao = getDao(map, EsQueueDao.class);
     pluginDao = getDao(map, PluginDao.class);
+    branchDao = getDao(map, BranchDao.class);
   }
 
   public DbSession openSession(boolean batch) {
@@ -383,6 +387,10 @@ public class DbClient {
     return pluginDao;
   }
 
+  public BranchDao branchDao() {
+    return branchDao;
+  }
+
   protected <K extends Dao> K getDao(Map<Class, Dao> map, Class<K> clazz) {
     return (K) map.get(clazz);
   }
index 126dda1ada3df2ea35602a2b8e0184f442372074..6519c318411d289b9849fd7cedfa67ca39a75f7c 100644 (file)
@@ -19,6 +19,9 @@
  */
 package org.sonar.db.component;
 
+import java.util.Collection;
+import java.util.Optional;
+import javax.annotation.Nullable;
 import org.sonar.api.utils.System2;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
@@ -35,6 +38,27 @@ public class BranchDao implements Dao {
     mapper(dbSession).insert(dto, system2.now());
   }
 
+  public void upsert(DbSession dbSession, BranchDto dto) {
+    BranchMapper mapper = mapper(dbSession);
+    long now = system2.now();
+    if (mapper.update(dto, now) == 0) {
+      mapper.insert(dto, now);
+    }
+  }
+
+  public Optional<BranchDto> selectByKey(DbSession dbSession, String projectUuid, BranchKeyType keyType, @Nullable String key) {
+    String keyInDb = BranchDto.convertKeyToDb(key);
+    return Optional.ofNullable(mapper(dbSession).selectByKey(projectUuid, keyType, keyInDb));
+  }
+
+  public Collection<BranchDto> selectByComponent(DbSession dbSession, ComponentDto component) {
+    String projectUuid = component.getMainBranchProjectUuid();
+    if (projectUuid == null) {
+      projectUuid = component.projectUuid();
+    }
+    return mapper(dbSession).selectByProjectUuid(projectUuid);
+  }
+
   private static BranchMapper mapper(DbSession dbSession) {
     return dbSession.getMapper(BranchMapper.class);
   }
index 7192a6d4683269158ea0df81cfd0d757a6009e38..f3a71679f553814a1337fe8f8138989d28462e3e 100644 (file)
  */
 package org.sonar.db.component;
 
+import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static org.apache.commons.lang.StringUtils.repeat;
 
 public class BranchDto {
 
+  /**
+   * Maximum length of column "kee"
+   */
+  public static final int KEE_MAX_LENGTH = 255;
+
   /**
    * Value of {@link #kee} when the name of main branch is not known.
    * Used only if {@link #keeType} is {@link BranchKeyType#BRANCH}.
-   * It does not conflict with names of real branches because the character ':'
+   * It does not conflict with names of real branches because the term ':BRANCH:'
    * is not accepted.
    */
-  public static final String DEFAULT_KEY_OF_MAIN_BRANCH = ":main:";
+  public static final String NULL_KEY = repeat("_", KEE_MAX_LENGTH);
 
   /**
    * Branch UUID is the projects.uuid that reference projects, branches or pull requests
@@ -56,7 +63,7 @@ public class BranchDto {
 
   /**
    * If {@link #keeType} is {@link BranchKeyType#BRANCH}, then name of branch, for example
-   * "feature/foo". Can be {@link #DEFAULT_KEY_OF_MAIN_BRANCH} is the name is not known.
+   * "feature/foo". Can be {@link #NULL_KEY} is the name is not known.
    *
    * If {@link #keeType} is {@link BranchKeyType#PR}, then id of the pull request, for
    * example "1204".
@@ -102,6 +109,10 @@ public class BranchDto {
     this.projectUuid = s;
   }
 
+  public boolean isMain() {
+    return projectUuid.equals(uuid);
+  }
+
   public BranchKeyType getKeeType() {
     return keeType;
   }
@@ -110,15 +121,33 @@ public class BranchDto {
     this.keeType = t;
   }
 
-  public String getKee() {
+  /**
+   * This is the getter used by MyBatis mapper. It does
+   * not handle the special value used to map null field.
+   */
+  private String getKee() {
     return kee;
   }
 
-  public void setKee(String s) {
-    checkArgument(s.length() <= 255, "Maximum length of branch name or pull request id is 255: %s", s);
+  @CheckForNull
+  public String getKey() {
+    return convertKeyFromDb(getKee());
+  }
+
+  /**
+   * This is the setter used by MyBatis mapper. It does
+   * not handle the special value used to map null field.
+   */
+  private void setKee(String s) {
     this.kee = s;
   }
 
+  public void setKey(@Nullable String s) {
+    checkArgument(s == null || s.length() <= KEE_MAX_LENGTH, "Maximum length of branch name or pull request id is %s: %s", KEE_MAX_LENGTH, s);
+    checkArgument(!NULL_KEY.equals(s), "Branch name is not allowed: %s", s);
+    setKee(convertKeyToDb(s));
+  }
+
   @Nullable
   public BranchType getBranchType() {
     return branchType;
@@ -151,9 +180,22 @@ public class BranchDto {
   public String toString() {
     StringBuilder sb = new StringBuilder("BranchDto{");
     sb.append("uuid='").append(uuid).append('\'');
+    sb.append(", projectUuid='").append(projectUuid).append('\'');
+    sb.append(", keeType=").append(keeType);
+    sb.append(", kee='").append(kee).append('\'');
+    sb.append(", branchType=").append(branchType);
     sb.append(", mergeBranchUuid='").append(mergeBranchUuid).append('\'');
     sb.append(", pullRequestTitle='").append(pullRequestTitle).append('\'');
     sb.append('}');
     return sb.toString();
   }
+
+  static String convertKeyToDb(@Nullable String s) {
+    return s == null ? NULL_KEY : s;
+  }
+
+  @CheckForNull
+  static String convertKeyFromDb(String s) {
+    return NULL_KEY.equals(s) ? null : s;
+  }
 }
index 505381a632f31ff1b538ecc30058d3d8e0393955..41cf1360f3dd60f9c488b25768be36ace02af799 100644 (file)
  */
 package org.sonar.db.component;
 
+import java.util.Collection;
 import org.apache.ibatis.annotations.Param;
 
 public interface BranchMapper {
 
   void insert(@Param("dto") BranchDto dto, @Param("now") long now);
 
+  int update(@Param("dto") BranchDto dto, @Param("now") long now);
+
+  BranchDto selectByKey(@Param("projectUuid") String projectUuid,
+    @Param("keyType") BranchKeyType keyType, @Param("key") String key);
+
+  Collection<BranchDto> selectByProjectUuid(@Param("projectUuid") String projectUuid);
 }
index 58a6fe02ed7edee034f8d999f904b09937f16570..9e3b55062fa3fa58c1523da4e3c3430de7a16d87 100644 (file)
@@ -216,9 +216,6 @@ public class ComponentDto {
     return this;
   }
 
-  public String getKey() {
-    return getDbKey();
-  }
 
   public String scope() {
     return scope;
@@ -476,4 +473,34 @@ public class ComponentDto {
       .append("private", isPrivate)
       .toString();
   }
+
+  public ComponentDto copy() {
+    ComponentDto copy = new ComponentDto();
+    copy.projectUuid = projectUuid;
+    copy.id = id;
+    copy.organizationUuid = organizationUuid;
+    copy.kee = kee;
+    copy.uuid = uuid;
+    copy.uuidPath = uuidPath;
+    copy.projectUuid = projectUuid;
+    copy.rootUuid = rootUuid;
+    copy.mainBranchProjectUuid = mainBranchProjectUuid;
+    copy.moduleUuid = moduleUuid;
+    copy.moduleUuidPath = moduleUuidPath;
+    copy.copyComponentUuid = copyComponentUuid;
+    copy.developerUuid = developerUuid;
+    copy.scope = scope;
+    copy.qualifier = qualifier;
+    copy.path = path;
+    copy.deprecatedKey = deprecatedKey;
+    copy.name = name;
+    copy.longName = longName;
+    copy.language = language;
+    copy.description = description;
+    copy.tags = tags;
+    copy.enabled = enabled;
+    copy.isPrivate = isPrivate;
+    copy.createdAt = createdAt;
+    return copy;
+  }
 }
index 966b5a564b0c71d3d455b6a4d4fd4ee2c5f6bbeb..f12f1027cfb3222573315db21da28306c7993ac5 100644 (file)
@@ -153,11 +153,7 @@ public class PropertiesDao implements Dao {
   }
 
   public List<PropertyDto> selectGlobalPropertiesByKeys(DbSession session, Set<String> keys) {
-    return selectByKeys(session, keys, null);
-  }
-
-  public List<PropertyDto> selectPropertiesByKeysAndComponentId(DbSession session, Set<String> keys, long componentId) {
-    return selectByKeys(session, keys, componentId);
+    return executeLargeInputs(keys, partitionKeys -> getMapper(session).selectByKeys(partitionKeys));
   }
 
   public List<PropertyDto> selectPropertiesByKeysAndComponentIds(DbSession session, Set<String> keys, Set<Long> componentIds) {
@@ -169,10 +165,6 @@ public class PropertiesDao implements Dao {
     return executeLargeInputs(componentIds, getMapper(session)::selectByComponentIds);
   }
 
-  private List<PropertyDto> selectByKeys(DbSession session, Set<String> keys, @Nullable Long componentId) {
-    return executeLargeInputs(keys, partitionKeys -> getMapper(session).selectByKeys(partitionKeys, componentId));
-  }
-
   public List<PropertyDto> selectGlobalPropertiesByKeyQuery(DbSession session, String keyQuery) {
     return getMapper(session).selectGlobalPropertiesByKeyQuery(buildLikeValue(keyQuery, WildcardPosition.BEFORE_AND_AFTER));
   }
index b687a121834d9cdd14c54cc05e63af0b0ae3dc02..ac422e3cd1deafb07f650c4b5750f996e4773528 100644 (file)
@@ -35,7 +35,7 @@ public interface PropertiesMapper {
 
   PropertyDto selectByKey(PropertyDto key);
 
-  List<PropertyDto> selectByKeys(@Param("keys") List<String> keys, @Nullable @Param("componentId") Long componentId);
+  List<PropertyDto> selectByKeys(@Param("keys") List<String> keys);
 
   List<PropertyDto> selectByKeysAndComponentIds(@Param("keys") List<String> keys, @Param("componentIds") List<Long> componentIds);
 
index e05ac532f732cc5f9140e4fe8d6d93a5a2ed9896..c5a6de69a93967d5f037c3f65decfc7758ee2f43 100644 (file)
@@ -21,7 +21,8 @@
       branch_type,
       merge_branch_uuid,
       pull_request_title,
-      created_at
+      created_at,
+      updated_at
     ) values (
       #{dto.uuid, jdbcType=VARCHAR},
       #{dto.projectUuid, jdbcType=VARCHAR},
       #{dto.branchType, jdbcType=VARCHAR},
       #{dto.mergeBranchUuid, jdbcType=VARCHAR},
       #{dto.pullRequestTitle, jdbcType=VARCHAR},
+      #{now, jdbcType=BIGINT},
       #{now, jdbcType=BIGINT}
     )
   </insert>
 
+  <update id="update" parameterType="map" useGeneratedKeys="false">
+    update project_branches
+    set
+    merge_branch_uuid = #{dto.mergeBranchUuid, jdbcType=VARCHAR},
+    pull_request_title = #{dto.pullRequestTitle, jdbcType=VARCHAR},
+    updated_at = #{now, jdbcType=BIGINT}
+    where
+    uuid = #{dto.uuid, jdbcType=VARCHAR}
+  </update>
+
+  <select id="selectByKey" resultType="org.sonar.db.component.BranchDto">
+    select <include refid="columns" />
+    from project_branches pb
+    where
+    pb.project_uuid = #{projectUuid, jdbcType=VARCHAR} and
+    pb.kee_type = #{keyType, jdbcType=VARCHAR} and
+    pb.kee = #{key, jdbcType=VARCHAR}
+  </select>
+
+  <select id="selectByProjectUuid" parameterType="string" resultType="org.sonar.db.component.BranchDto">
+    select <include refid="columns" />
+    from project_branches pb
+    where
+    pb.project_uuid = #{projectUuid, jdbcType=VARCHAR}
+  </select>
 </mapper>
index 26dca324c5638cc6222f28c4d67e0ea77e631664..a9a520eb1ee1cae00c0837246bae628235edb775 100644 (file)
         <foreach collection="keys" open="(" close=")" item="key" separator=",">
           #{key}
         </foreach>
-      <if test="componentId == null">
-        and p.resource_id is null
-      </if>
-      <if test="componentId != null">
-        and p.resource_id=#{componentId}
-      </if>
+      and p.resource_id is null
       and p.user_id is null
     order by p.id
   </select>
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java
new file mode 100644 (file)
index 0000000..5133eff
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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.db.component;
+
+import java.util.Map;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+
+import static org.apache.commons.lang.StringUtils.repeat;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+
+public class BranchDaoTest {
+
+  private static final long NOW = 1_000L;
+  private static final String SELECT_FROM = "select project_uuid as \"projectUuid\", uuid as \"uuid\", branch_type as \"branchType\", kee_type as \"keeType\", " +
+    "kee as \"kee\", merge_branch_uuid as \"mergeBranchUuid\", pull_request_title as \"pullRequestTitle\", created_at as \"createdAt\", updated_at as \"updatedAt\" " +
+    "from project_branches ";
+  private System2 system2 = new TestSystem2().setNow(NOW);
+
+  @Rule
+  public DbTester db = DbTester.create(system2);
+
+  private DbSession dbSession = db.getSession();
+  private BranchDao underTest = new BranchDao(system2);
+
+  @Test
+  public void insert_branch_with_only_nonnull_fields() {
+    BranchDto dto = new BranchDto();
+    dto.setProjectUuid("U1");
+    dto.setUuid("U2");
+    dto.setBranchType(BranchType.SHORT);
+    dto.setKeeType(BranchKeyType.BRANCH);
+    dto.setKey("feature/foo");
+
+    underTest.insert(dbSession, dto);
+
+    Map<String, Object> map = db.selectFirst(dbSession, SELECT_FROM + " where uuid='" + dto.getUuid() + "'");
+    assertThat(map).contains(
+      entry("projectUuid", "U1"),
+      entry("uuid", "U2"),
+      entry("branchType", "SHORT"),
+      entry("keeType", "BRANCH"),
+      entry("kee", "feature/foo"),
+      entry("mergeBranchUuid", null),
+      entry("pullRequestTitle", null),
+      entry("createdAt", 1_000L),
+      entry("updatedAt", 1_000L)
+    );
+  }
+
+  @Test
+  public void insert_branch_with_all_fields_and_max_length_values() {
+    BranchDto dto = new BranchDto();
+    dto.setProjectUuid(repeat("a", 50));
+    dto.setUuid(repeat("b", 50));
+    dto.setBranchType(BranchType.SHORT);
+    dto.setKeeType(BranchKeyType.BRANCH);
+    dto.setKey(repeat("c", 255));
+    dto.setMergeBranchUuid(repeat("d", 50));
+    dto.setPullRequestTitle(repeat("e", 4_000));
+
+    underTest.insert(dbSession, dto);
+
+    Map<String, Object> map = db.selectFirst(dbSession, SELECT_FROM + " where uuid='" + dto.getUuid() + "'");
+    assertThat((String)map.get("projectUuid")).contains("a").isEqualTo(dto.getProjectUuid());
+    assertThat((String)map.get("uuid")).contains("b").isEqualTo(dto.getUuid());
+    assertThat((String)map.get("kee")).contains("c").isEqualTo(dto.getKey());
+    assertThat((String)map.get("mergeBranchUuid")).contains("d").isEqualTo(dto.getMergeBranchUuid());
+    assertThat((String)map.get("pullRequestTitle")).contains("e").isEqualTo(dto.getPullRequestTitle());
+  }
+
+  @Test
+  public void null_value_of_kee_column_is_converted_in_db() {
+    BranchDto dto = new BranchDto();
+    dto.setProjectUuid("u1");
+    dto.setUuid("u2");
+    dto.setKeeType(BranchKeyType.BRANCH);
+    dto.setKey(null);
+
+    underTest.insert(dbSession, dto);
+
+    Map<String, Object> map = db.selectFirst(dbSession, SELECT_FROM + " where uuid='" + dto.getUuid() + "'");
+    assertThat(map).contains(entry("kee", BranchDto.NULL_KEY));
+    BranchDto loaded = underTest.selectByKey(dbSession, dto.getProjectUuid(), dto.getKeeType(), null).get();
+    assertThat(loaded.getKey()).isNull();
+  }
+
+  @Test
+  public void upsert() {
+    BranchDto dto = new BranchDto();
+    dto.setProjectUuid("U1");
+    dto.setUuid("U2");
+    dto.setBranchType(BranchType.LONG);
+    dto.setKeeType(BranchKeyType.BRANCH);
+    dto.setKey("foo");
+    underTest.insert(dbSession, dto);
+
+    // the fields that can be updated
+    dto.setMergeBranchUuid("U3");
+    dto.setPullRequestTitle("theTitle");
+
+    // the fields that can't be updated. New values are ignored.
+    dto.setProjectUuid("ignored");
+    dto.setBranchType(BranchType.SHORT);
+    dto.setKeeType(BranchKeyType.PR);
+    underTest.upsert(dbSession, dto);
+
+    BranchDto loaded = underTest.selectByKey(dbSession, "U1", BranchKeyType.BRANCH, "foo").get();
+    assertThat(loaded.getMergeBranchUuid()).isEqualTo("U3");
+    assertThat(loaded.getPullRequestTitle()).isEqualTo("theTitle");
+    assertThat(loaded.getProjectUuid()).isEqualTo("U1");
+    assertThat(loaded.getBranchType()).isEqualTo(BranchType.LONG);
+    assertThat(loaded.getKeeType()).isEqualTo(BranchKeyType.BRANCH);
+  }
+
+  @Test
+  public void selectByKey() {
+    BranchDto mainBranch = new BranchDto();
+    mainBranch.setProjectUuid("U1");
+    mainBranch.setUuid("U1");
+    mainBranch.setBranchType(BranchType.LONG);
+    mainBranch.setKeeType(BranchKeyType.BRANCH);
+    mainBranch.setKey("master");
+    underTest.insert(dbSession, mainBranch);
+
+    BranchDto featureBranch = new BranchDto();
+    featureBranch.setProjectUuid("U1");
+    featureBranch.setUuid("U2");
+    featureBranch.setBranchType(BranchType.SHORT);
+    featureBranch.setKeeType(BranchKeyType.BRANCH);
+    featureBranch.setKey("feature/foo");
+    featureBranch.setMergeBranchUuid("U3");
+    underTest.insert(dbSession, featureBranch);
+
+    // select the feature branch
+    BranchDto loaded = underTest.selectByKey(dbSession, "U1", BranchKeyType.BRANCH, "feature/foo").get();
+    assertThat(loaded.getUuid()).isEqualTo(featureBranch.getUuid());
+    assertThat(loaded.getKey()).isEqualTo(featureBranch.getKey());
+    assertThat(loaded.getProjectUuid()).isEqualTo(featureBranch.getProjectUuid());
+    assertThat(loaded.getBranchType()).isEqualTo(featureBranch.getBranchType());
+    assertThat(loaded.getKeeType()).isEqualTo(featureBranch.getKeeType());
+    assertThat(loaded.getMergeBranchUuid()).isEqualTo(featureBranch.getMergeBranchUuid());
+    assertThat(loaded.getPullRequestTitle()).isEqualTo(featureBranch.getPullRequestTitle());
+
+    // select a pull request with same key than the feature branch
+    assertThat(underTest.selectByKey(dbSession, "U1", BranchKeyType.PR, "feature/foo")).isEmpty();
+
+    // select a branch on another project with same branch name
+    assertThat(underTest.selectByKey(dbSession, "U3", BranchKeyType.BRANCH, "feature/foo")).isEmpty();
+  }
+
+}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDtoTest.java
new file mode 100644 (file)
index 0000000..53e2e21
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.db.component;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BranchDtoTest {
+
+  private BranchDto underTest = new BranchDto();
+
+  @Test
+  public void isMain_is_true_if_branch_uuid_equals_project_uuid() {
+    underTest.setProjectUuid("U1");
+    underTest.setUuid("U1");
+
+    assertThat(underTest.isMain()).isTrue();
+  }
+
+  @Test
+  public void isMain_is_false_if_branch_uuid_does_not_equal_project_uuid() {
+    underTest.setProjectUuid("U1");
+    underTest.setUuid("U2");
+
+    assertThat(underTest.isMain()).isFalse();
+  }
+}
index 2050730e8fd0acdee932dad974260053a67250b4..0f66805fdd43b9b8407fdde93348f70ee302a63a 100644 (file)
@@ -202,7 +202,7 @@ public class ComponentDaoTest {
   public void get_by_key_on_disabled_component() {
     ComponentDto project = db.components().insertPrivateProject(p -> p.setEnabled(false));
 
-    ComponentDto result = underTest.selectOrFailByKey(dbSession, project.getKey());
+    ComponentDto result = underTest.selectOrFailByKey(dbSession, project.getDbKey());
 
     assertThat(result.isEnabled()).isFalse();
   }
@@ -211,7 +211,7 @@ public class ComponentDaoTest {
   public void get_by_key_on_a_root_project() {
     ComponentDto project = db.components().insertPrivateProject();
 
-    ComponentDto result = underTest.selectOrFailByKey(dbSession, project.getKey());
+    ComponentDto result = underTest.selectOrFailByKey(dbSession, project.getDbKey());
 
     assertThat(result.getDbKey()).isEqualTo(project.getDbKey());
     assertThat(result.uuid()).isEqualTo(project.uuid());
@@ -225,13 +225,13 @@ public class ComponentDaoTest {
     ComponentDto project1 = db.components().insertPrivateProject();
     ComponentDto project2 = db.components().insertPrivateProject();
 
-    List<ComponentDto> results = underTest.selectByKeys(dbSession, asList(project1.getKey(), project2.getKey()));
+    List<ComponentDto> results = underTest.selectByKeys(dbSession, asList(project1.getDbKey(), project2.getDbKey()));
 
     assertThat(results)
-      .extracting(ComponentDto::uuid, ComponentDto::getKey)
+      .extracting(ComponentDto::uuid, ComponentDto::getDbKey)
       .containsExactlyInAnyOrder(
-        tuple(project1.uuid(), project1.getKey()),
-        tuple(project2.uuid(), project2.getKey()));
+        tuple(project1.uuid(), project1.getDbKey()),
+        tuple(project2.uuid(), project2.getDbKey()));
 
     assertThat(underTest.selectByKeys(dbSession, singletonList("unknown"))).isEmpty();
   }
@@ -244,10 +244,10 @@ public class ComponentDaoTest {
     List<ComponentDto> results = underTest.selectByIds(dbSession, asList(project1.getId(), project2.getId()));
 
     assertThat(results)
-      .extracting(ComponentDto::uuid, ComponentDto::getKey)
+      .extracting(ComponentDto::uuid, ComponentDto::getDbKey)
       .containsExactlyInAnyOrder(
-        tuple(project1.uuid(), project1.getKey()),
-        tuple(project2.uuid(), project2.getKey()));
+        tuple(project1.uuid(), project1.getDbKey()),
+        tuple(project2.uuid(), project2.getDbKey()));
 
     assertThat(underTest.selectByIds(dbSession, singletonList(0L))).isEmpty();
   }
@@ -260,10 +260,10 @@ public class ComponentDaoTest {
     List<ComponentDto> results = underTest.selectByUuids(dbSession, asList(project1.uuid(), project2.uuid()));
 
     assertThat(results)
-      .extracting(ComponentDto::uuid, ComponentDto::getKey)
+      .extracting(ComponentDto::uuid, ComponentDto::getDbKey)
       .containsExactlyInAnyOrder(
-        tuple(project1.uuid(), project1.getKey()),
-        tuple(project2.uuid(), project2.getKey()));
+        tuple(project1.uuid(), project1.getDbKey()),
+        tuple(project2.uuid(), project2.getDbKey()));
 
     assertThat(underTest.selectByUuids(dbSession, singletonList("unknown"))).isEmpty();
   }
@@ -276,7 +276,7 @@ public class ComponentDaoTest {
     List<ComponentDto> results = underTest.selectByUuids(dbSession, asList(project1.uuid(), project2.uuid()));
 
     assertThat(results)
-      .extracting(ComponentDto::getKey, ComponentDto::isEnabled)
+      .extracting(ComponentDto::getDbKey, ComponentDto::isEnabled)
       .containsExactlyInAnyOrder(
         tuple(project1.getDbKey(), true),
         tuple(project2.getDbKey(), false));
index e3d4838ade8b17dae85d676e9ba0398932223fea..8b0c455d92fa465bc394136747aaeaa358c5a538 100644 (file)
@@ -402,27 +402,6 @@ public class PropertiesDaoTest {
       .isEmpty();
   }
 
-  @Test
-  public void select_component_properties_by_keys() throws Exception {
-    ComponentDto project = dbTester.components().insertPrivateProject();
-    UserDto user = dbTester.users().insertUser();
-
-    String key = "key";
-    String anotherKey = "anotherKey";
-    insertProperties(
-      newGlobalPropertyDto().setKey(key),
-      newComponentPropertyDto(project).setKey(key),
-      newUserPropertyDto(user).setKey(key),
-      newComponentPropertyDto(project).setKey(anotherKey));
-
-    assertThat(underTest.selectPropertiesByKeysAndComponentId(session, newHashSet(key), project.getId())).extracting("key").containsOnly(key);
-    assertThat(underTest.selectPropertiesByKeysAndComponentId(session, newHashSet(key, anotherKey), project.getId())).extracting("key").containsOnly(key, anotherKey);
-    assertThat(underTest.selectPropertiesByKeysAndComponentId(session, newHashSet(key, anotherKey, "unknown"), project.getId())).extracting("key").containsOnly(key, anotherKey);
-
-    assertThat(underTest.selectPropertiesByKeysAndComponentId(session, newHashSet("unknown"), project.getId())).isEmpty();
-    assertThat(underTest.selectPropertiesByKeysAndComponentId(session, newHashSet(key), 123456789L)).isEmpty();
-  }
-
   @Test
   public void select_component_properties_by_ids() throws Exception {
     ComponentDto project = dbTester.components().insertPrivateProject();
index 713f7dab8ffadf2369ecf117c6607d00cb209671..cb499371b999b5859e3f718fb01dffefbd3110e9 100644 (file)
@@ -17,7 +17,6 @@
  * 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.platform.db.migration.version.v66;
 
 import java.sql.SQLException;
index f7884a01dcf35ea47fc326baffd2a61d9a82106d..db40d988aecf4caf8b7747f371178e612325429d 100644 (file)
@@ -76,6 +76,10 @@ public class CreateTableProjectBranches extends DdlChange {
         .setColumnName("created_at")
         .setIsNullable(false)
         .build())
+      .addColumn(BigIntegerColumnDef.newBigIntegerColumnDefBuilder()
+        .setColumnName("updated_at")
+        .setIsNullable(false)
+        .build())
       .build()
     );
   }
index a960ef02b658dc5d501bbc59d54f324c02d33eea..f8d74a0b11523683c0a4760651d6f14688722ce9 100644 (file)
@@ -53,6 +53,7 @@ public class CreateTableProjectBranchesTest {
     db.assertColumnDefinition(TABLE, "merge_branch_uuid", Types.VARCHAR, 50, true);
     db.assertColumnDefinition(TABLE, "pull_request_title", Types.VARCHAR, 4000, true);
     db.assertColumnDefinition(TABLE, "created_at", Types.BIGINT, null, false);
+    db.assertColumnDefinition(TABLE, "updated_at", Types.BIGINT, null, false);
     db.assertPrimaryKey(TABLE, "pk_" + TABLE, "uuid");
   }
 
index aad44371d6fdace6dfc1ac7e76f4fea9a4ef2958..e6f63e2ef8440f65ed58030189b3b00c7066aed4 100644 (file)
@@ -87,8 +87,8 @@ public class ReportSubmitter {
   public CeTask submit(String organizationKey, String projectKey, @Nullable String projectBranch, @Nullable String projectName, Map<String, String> characteristics,
     InputStream reportInput) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      String effectiveProjectKey = ComponentKeys.createKey(projectKey, projectBranch);
       OrganizationDto organizationDto = getOrganizationDtoOrFail(dbSession, organizationKey);
+      String effectiveProjectKey = ComponentKeys.createKey(projectKey, projectBranch);
       Optional<ComponentDto> opt = dbClient.componentDao().selectByKey(dbSession, effectiveProjectKey);
       ensureOrganizationIsConsistent(opt, organizationDto);
       ComponentDto project = opt.or(() -> createProject(dbSession, organizationDto, projectKey, projectBranch, projectName));
@@ -122,12 +122,13 @@ public class ReportSubmitter {
     }
   }
 
-  private ComponentDto createProject(DbSession dbSession, OrganizationDto organization, String projectKey, @Nullable String projectBranch, @Nullable String projectName) {
+  private ComponentDto createProject(DbSession dbSession, OrganizationDto organization, String projectKey, @Nullable String deprecatedBranch, @Nullable String projectName) {
     userSession.checkPermission(OrganizationPermission.PROVISION_PROJECTS, organization);
     Integer userId = userSession.getUserId();
 
+    String effectiveProjectKey = ComponentKeys.createEffectiveKey(projectKey, deprecatedBranch);
     boolean wouldCurrentUserHaveScanPermission = permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(
-      dbSession, organization.getUuid(), userId, projectBranch, projectKey, Qualifiers.PROJECT);
+      dbSession, organization.getUuid(), userId, effectiveProjectKey, Qualifiers.PROJECT);
     if (!wouldCurrentUserHaveScanPermission) {
       throw insufficientPrivilegesException();
     }
@@ -138,7 +139,7 @@ public class ReportSubmitter {
       .setOrganizationUuid(organization.getUuid())
       .setKey(projectKey)
       .setName(StringUtils.defaultIfBlank(projectName, projectKey))
-      .setBranch(projectBranch)
+      .setBranch(deprecatedBranch)
       .setQualifier(Qualifiers.PROJECT)
       .setPrivate(newProjectPrivate)
       .build();
index bb922d5e256bd5e6ddb02f884d44a65d18a6ccc9..e86d1a7501ced28403463c7865e7cb93553ddc77 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.computation.task.projectanalysis.analysis;
 
 import java.util.Map;
+import java.util.Optional;
 import javax.annotation.CheckForNull;
 import org.sonar.server.qualityprofile.QualityProfile;
 
@@ -74,10 +75,28 @@ public interface AnalysisMetadataHolder {
   boolean isCrossProjectDuplicationEnabled();
 
   /**
+   * Branch is present whatever the type of branch (long or short, main or not). However
+   * it is absent when analyzing a pull request.
+   *
    * @throws IllegalStateException if branch has not been set
    */
-  @CheckForNull
-  String getBranch();
+  Optional<Branch> getBranch();
+
+  /**
+   * The project as represented by the main branch. It is used to load settings
+   * like Quality gates, webhooks and configuration.
+   *
+   * In case of analysis of main branch, the returned value is the main branch,
+   * so its uuid and key are the same in
+   * {@link org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder#getRoot().
+   *
+   * In case of analysis of non-main branch or pull request, the returned value
+   * is the main branch. Its uuid and key are different than
+   * {@link org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder#getRoot().
+   *
+   * @throws IllegalStateException if project has not been set
+   */
+  Project getProject();
 
   /**
    * @throws IllegalStateException if root component ref has not been set
index e03ddd2afcf137ee93b49d4dd76c8cfae0bddaaf..7b0a9dcbec0fa2520d39759c2e0135e5e9175b4c 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.computation.task.projectanalysis.analysis;
 
 import com.google.common.collect.ImmutableMap;
 import java.util.Map;
+import java.util.Optional;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.sonar.server.computation.util.InitializedProperty;
@@ -37,7 +38,8 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder
   private final InitializedProperty<Boolean> incrementalAnalysis = new InitializedProperty<>();
   private final InitializedProperty<Analysis> baseProjectSnapshot = new InitializedProperty<>();
   private final InitializedProperty<Boolean> crossProjectDuplicationEnabled = new InitializedProperty<>();
-  private final InitializedProperty<String> branch = new InitializedProperty<>();
+  private final InitializedProperty<Branch> branch = new InitializedProperty<>();
+  private final InitializedProperty<Project> project = new InitializedProperty<>();
   private final InitializedProperty<Integer> rootComponentRef = new InitializedProperty<>();
   private final InitializedProperty<Map<String, QualityProfile>> qProfilesPerLanguage = new InitializedProperty<>();
   private final InitializedProperty<Map<String, ScannerPlugin>> pluginsByKey = new InitializedProperty<>();
@@ -134,16 +136,29 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder
   }
 
   @Override
-  public MutableAnalysisMetadataHolder setBranch(@Nullable String branch) {
+  public MutableAnalysisMetadataHolder setBranch(@Nullable Branch branch) {
     checkState(!this.branch.isInitialized(), "Branch has already been set");
     this.branch.setProperty(branch);
     return this;
   }
 
   @Override
-  public String getBranch() {
+  public Optional<Branch> getBranch() {
     checkState(branch.isInitialized(), "Branch has not been set");
-    return branch.getProperty();
+    return Optional.ofNullable(branch.getProperty());
+  }
+
+  @Override
+  public MutableAnalysisMetadataHolder setProject(Project project) {
+    checkState(!this.project.isInitialized(), "Project has already been set");
+    this.project.setProperty(project);
+    return this;
+  }
+
+  @Override
+  public Project getProject() {
+    checkState(project.isInitialized(), "Project has not been set");
+    return project.getProperty();
   }
 
   @Override
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Branch.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Branch.java
new file mode 100644 (file)
index 0000000..7695ae1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.analysis;
+
+import java.util.Optional;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.db.component.BranchType;
+import org.sonar.server.computation.task.projectanalysis.component.ComponentKeyGenerator;
+
+@Immutable
+public interface Branch extends ComponentKeyGenerator {
+
+  BranchType getType();
+
+  boolean isMain();
+
+  /**
+   * Name can be empty when it's not known on the main branch
+   * (regular analysis without the branch parameters)
+   */
+  Optional<String> getName();
+
+  /**
+   * Whether the cross-project duplication tracker must be enabled
+   * or not.
+   */
+  boolean supportsCrossProjectCpd();
+}
index 88214c7c5966d2cb0538c2710d68cd72e1d95bf9..624d16b58b99b000b234bbafa36b9a2f1aff53c8 100644 (file)
@@ -58,7 +58,12 @@ public interface MutableAnalysisMetadataHolder extends AnalysisMetadataHolder {
   /**
    * @throws IllegalStateException if branch has already been set
    */
-  MutableAnalysisMetadataHolder setBranch(@Nullable String branch);
+  MutableAnalysisMetadataHolder setBranch(@Nullable  Branch branch);
+
+  /**
+   * @throws IllegalStateException if project has already been set
+   */
+  MutableAnalysisMetadataHolder setProject(Project project);
 
   /**
    * @throws IllegalStateException if root component ref has already been set
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Project.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Project.java
new file mode 100644 (file)
index 0000000..96d8404
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.analysis;
+
+import javax.annotation.concurrent.Immutable;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+
+@Immutable
+public class Project {
+
+  private final String uuid;
+  private final String key;
+  private final String name;
+
+  public Project(String uuid, String key, String name) {
+    this.uuid = uuid;
+    this.key = key;
+    this.name = name;
+  }
+
+  /**
+   * Always links to a row that exists in database.
+   */
+  public String getUuid() {
+    return uuid;
+  }
+
+  /**
+   * Always links to a row that exists in database.
+   */
+  public String getKey() {
+    return key;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    Project project = (Project) o;
+    return uuid.equals(project.uuid);
+  }
+
+  @Override
+  public int hashCode() {
+    return uuid.hashCode();
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder("Project{");
+    sb.append("uuid='").append(uuid).append('\'');
+    sb.append(", key='").append(key).append('\'');
+    sb.append(", name='").append(name).append('\'');
+    sb.append('}');
+    return sb.toString();
+  }
+
+  public static Project copyOf(Component component) {
+    return new Project(component.getUuid(), component.getKey(), component.getName());
+  }
+
+  public static Project copyOf(ComponentDto component) {
+    return new Project(component.uuid(), component.getDbKey(), component.name());
+  }
+}
index e2a3371186059d57aa755478e5f2322dddc5ac76..1e207837af209b5b5a5abc24202598859c7c7631 100644 (file)
@@ -35,7 +35,6 @@ import org.sonar.api.ce.measure.Measure;
 import org.sonar.api.ce.measure.MeasureComputer.MeasureComputerContext;
 import org.sonar.api.ce.measure.MeasureComputer.MeasureComputerDefinition;
 import org.sonar.api.ce.measure.Settings;
-import org.sonar.api.config.Configuration;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
 import org.sonar.server.computation.task.projectanalysis.issue.ComponentIssuesRepository;
@@ -97,20 +96,16 @@ public class MeasureComputerContextImpl implements MeasureComputerContext {
       @Override
       @CheckForNull
       public String getString(String key) {
-        return getComponentSettings().get(key).orElse(null);
+        return config.getConfiguration().get(key).orElse(null);
       }
 
       @Override
       public String[] getStringArray(String key) {
-        return getComponentSettings().getStringArray(key);
+        return config.getConfiguration().getStringArray(key);
       }
     };
   }
 
-  private Configuration getComponentSettings() {
-    return config.getConfiguration(internalComponent);
-  }
-
   @Override
   @CheckForNull
   public Measure getMeasure(String metric) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoader.java
new file mode 100644 (file)
index 0000000..1e5777b
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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 javax.annotation.Nullable;
+import org.sonar.api.utils.MessageException;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolder;
+
+import static org.apache.commons.lang.StringUtils.trimToNull;
+
+public class BranchLoader {
+
+  private final MutableAnalysisMetadataHolder metadataHolder;
+  private final BranchLoaderDelegate delegate;
+
+  public BranchLoader(MutableAnalysisMetadataHolder metadataHolder) {
+    this(metadataHolder, null);
+  }
+
+  public BranchLoader(MutableAnalysisMetadataHolder metadataHolder, @Nullable BranchLoaderDelegate delegate) {
+    this.metadataHolder = metadataHolder;
+    this.delegate = delegate;
+  }
+
+  public void load(ScannerReport.Metadata metadata) {
+    String deprecatedBranch = trimToNull(metadata.getDeprecatedBranch());
+    String branchName = trimToNull(metadata.getBranchName());
+
+    if (deprecatedBranch != null && branchName != null) {
+      throw MessageException.of("Properties sonar.branch and sonar.branch.name can't be set together");
+    }
+
+    if (delegate != null && deprecatedBranch == null) {
+      delegate.load(metadata);
+    } else {
+      metadataHolder.setBranch(new MainBranchImpl(deprecatedBranch));
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoaderDelegate.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoaderDelegate.java
new file mode 100644 (file)
index 0000000..19aff4f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+@ComputeEngineSide
+public interface BranchLoaderDelegate {
+
+  void load(ScannerReport.Metadata metadata);
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterDelegate.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterDelegate.java
new file mode 100644 (file)
index 0000000..21ab94b
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.db.DbSession;
+
+@ComputeEngineSide
+public interface BranchPersisterDelegate {
+
+  void persist(DbSession dbSession);
+
+}
index 0d2b65169d0e309e13a3ced95129a7b99360cc20..a2be3b3a09764a121b09cf3a80a4eba6984b249b 100644 (file)
@@ -72,7 +72,9 @@ public interface Component {
   String getUuid();
 
   /**
-   * Returns the component key
+   * Returns the component key <b>as defined in database</b>
+   * It may differ from keys listed in scanner report
+   * when analyzing a branch.
    */
   String getKey();
 
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentKeyGenerator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentKeyGenerator.java
new file mode 100644 (file)
index 0000000..5f277aa
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 javax.annotation.Nullable;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+@FunctionalInterface
+public interface ComponentKeyGenerator {
+
+  String generateKey(ScannerReport.Component module, @Nullable ScannerReport.Component fileOrDir);
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilder.java
new file mode 100644 (file)
index 0000000..7627811
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * 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 java.util.function.Function;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.Component.FileStatus;
+import org.sonar.server.computation.task.projectanalysis.analysis.Project;
+
+import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
+import static org.apache.commons.lang.StringUtils.trimToNull;
+
+public class ComponentTreeBuilder {
+
+  private static final String DEFAULT_PROJECT_VERSION = "not provided";
+
+  private final ComponentKeyGenerator keyGenerator;
+  /**
+   * 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;
+
+  private final Project project;
+
+  @Nullable
+  private final SnapshotDto baseAnalysis;
+
+  public ComponentTreeBuilder(
+    ComponentKeyGenerator keyGenerator,
+    Function<String, String> uuidSupplier,
+    Function<Integer, ScannerReport.Component> scannerComponentSupplier,
+    Project project,
+    @Nullable SnapshotDto baseAnalysis) {
+
+    this.keyGenerator = keyGenerator;
+    this.uuidSupplier = uuidSupplier;
+    this.scannerComponentSupplier = scannerComponentSupplier;
+    this.project = project;
+    this.baseAnalysis = baseAnalysis;
+  }
+
+  public Component buildProject(ScannerReport.Component project) {
+    return buildComponent(project, project);
+  }
+
+  private Component[] buildChildren(ScannerReport.Component component, ScannerReport.Component parentModule) {
+    return component.getChildRefList()
+      .stream()
+      .map(componentRef -> buildComponent(scannerComponentSupplier.apply(componentRef), parentModule))
+      .toArray(Component[]::new);
+  }
+
+  private ComponentImpl buildComponent(ScannerReport.Component component, ScannerReport.Component closestModule) {
+    switch (component.getType()) {
+      case PROJECT:
+        String projectKey = keyGenerator.generateKey(component, null);
+        String uuid = uuidSupplier.apply(projectKey);
+        return ComponentImpl.builder(Component.Type.PROJECT)
+          .setUuid(uuid)
+          .setKey(projectKey)
+          .setName(nameOfProject(component))
+          .setStatus(convertStatus(component.getStatus()))
+          .setDescription(trimToNull(component.getDescription()))
+          .setReportAttributes(createAttributesBuilder(component)
+            .setVersion(createProjectVersion(component))
+            .build())
+          .addChildren(buildChildren(component, component))
+          .build();
+
+      case MODULE:
+        String moduleKey = keyGenerator.generateKey(component, null);
+        return ComponentImpl.builder(Component.Type.MODULE)
+          .setUuid(uuidSupplier.apply(moduleKey))
+          .setKey(moduleKey)
+          .setName(nameOfOthers(component, moduleKey))
+          .setStatus(convertStatus(component.getStatus()))
+          .setDescription(trimToNull(component.getDescription()))
+          .setReportAttributes(createAttributesBuilder(component).build())
+          .addChildren(buildChildren(component, component))
+          .build();
+
+      case DIRECTORY:
+      case FILE:
+        String key = keyGenerator.generateKey(closestModule, component);
+        return ComponentImpl.builder(convertDirOrFileType(component.getType()))
+          .setUuid(uuidSupplier.apply(key))
+          .setKey(key)
+          .setName(nameOfOthers(component, key))
+          .setStatus(convertStatus(component.getStatus()))
+          .setDescription(trimToNull(component.getDescription()))
+          .setReportAttributes(createAttributesBuilder(component).build())
+          .setFileAttributes(createFileAttributes(component))
+          .addChildren(buildChildren(component, closestModule))
+          .build();
+
+      default:
+        throw new IllegalArgumentException(format("Unsupported component type '%s'", component.getType()));
+    }
+  }
+
+  private static Component.Status convertStatus(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);
+    }
+  }
+
+  private String nameOfProject(ScannerReport.Component component) {
+    String name = trimToNull(component.getName());
+    if (name != null) {
+      return name;
+    }
+    return project.getName();
+  }
+
+  private static String nameOfOthers(ScannerReport.Component reportComponent, String defaultName) {
+    String name = trimToNull(reportComponent.getName());
+    return name == null ? defaultName : name;
+  }
+
+  private String createProjectVersion(ScannerReport.Component component) {
+    String version = trimToNull(component.getVersion());
+    if (version != null) {
+      return version;
+    }
+    if (baseAnalysis != null) {
+      return firstNonNull(baseAnalysis.getVersion(), DEFAULT_PROJECT_VERSION);
+    }
+    return DEFAULT_PROJECT_VERSION;
+  }
+
+  private static ReportAttributes.Builder createAttributesBuilder(ScannerReport.Component component) {
+    return ReportAttributes.newBuilder(component.getRef())
+      .setVersion(trimToNull(component.getVersion()))
+      .setPath(trimToNull(component.getPath()));
+  }
+
+  @CheckForNull
+  private 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());
+  }
+
+  private static Component.Type convertDirOrFileType(ScannerReport.Component.ComponentType type) {
+    switch (type) {
+      case DIRECTORY:
+        return Component.Type.DIRECTORY;
+      case FILE:
+        return Component.Type.FILE;
+      default:
+        throw new IllegalArgumentException("Unsupported ComponentType value " + type);
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentUuidFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentUuidFactory.java
new file mode 100644 (file)
index 0000000..6cf3c21
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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 java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+
+public class ComponentUuidFactory {
+
+  private final Map<String, String> uuidsByKey = new HashMap<>();
+
+  public ComponentUuidFactory(DbClient dbClient, DbSession dbSession, String rootKey) {
+    List<ComponentDto> components = dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, rootKey);
+    for (ComponentDto dto : components) {
+      uuidsByKey.put(dto.getDbKey(), dto.uuid());
+    }
+  }
+
+  /**
+   * Get UUID from database if it exists, otherwise generate a new one.
+   */
+  public String getOrCreateForKey(String key) {
+    return uuidsByKey.computeIfAbsent(key, k -> Uuids.create());
+  }
+}
index 1dd33a4f4e32f2a1143ddaaa40a0a4b01cb6447e..c6bab6f4bf6e6008fa6dc154d4be4b59a099822b 100644 (file)
@@ -21,12 +21,8 @@ package org.sonar.server.computation.task.projectanalysis.component;
 
 import org.sonar.api.config.Configuration;
 
-/**
- * Repository of component settings.
- */
 public interface ConfigurationRepository {
-  /**
-   * Returns the configuration for the specified Component.
-   */
-  Configuration getConfiguration(Component component);
+
+  Configuration getConfiguration();
+
 }
index 80489e4730a9d0fa0682db81945ae37fd0feabf2..cbc8df1bb9e746a42252cef15256adca577568d7 100644 (file)
  */
 package org.sonar.server.computation.task.projectanalysis.component;
 
-import java.util.Collection;
-import java.util.Map;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
 import org.sonar.api.config.Configuration;
+import org.sonar.ce.queue.CeTask;
 import org.sonar.ce.settings.ProjectConfigurationFactory;
-import org.sonar.server.util.cache.CacheLoader;
-import org.sonar.server.util.cache.MemoryCache;
 
 /**
  * Repository of component settings implementation based on a memory cache.
  */
 public class ConfigurationRepositoryImpl implements ConfigurationRepository {
 
-  private final ProjectConfigurationFactory projectConfigurationFactory;
-  private final MemoryCache<String, Configuration> cache = new MemoryCache<>(new CacheLoader<String, Configuration>() {
-    @Override
-    public Configuration load(String key) {
-      return projectConfigurationFactory.newProjectConfiguration(key);
-    }
+  private final Supplier<Configuration> configuration;
 
-    @Override
-    public Map<String, Configuration> loadAll(Collection<? extends String> keys) {
-      throw new UnsupportedOperationException("loadAll is not supported");
-    }
-  });
-
-  public ConfigurationRepositoryImpl(ProjectConfigurationFactory projectSettingsFactory) {
-    this.projectConfigurationFactory = projectSettingsFactory;
+  public ConfigurationRepositoryImpl(CeTask ceTask, ProjectConfigurationFactory f) {
+    // project key is loaded from task because
+    // analysisMetadataHolder.getProject() may be not set yet
+    // when the first ComputationSteps are executed.
+    this.configuration = Suppliers.memoize(() -> f.newProjectConfiguration(ceTask.getComponentKey()));
   }
 
   @Override
-  public Configuration getConfiguration(Component component) {
-    return cache.get(component.getKey());
+  public Configuration getConfiguration() {
+    return configuration.get();
   }
-
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MainBranchImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MainBranchImpl.java
new file mode 100644 (file)
index 0000000..6893a43
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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 java.util.Optional;
+import javax.annotation.Nullable;
+import org.sonar.api.utils.MessageException;
+import org.sonar.core.component.ComponentKeys;
+import org.sonar.db.component.BranchType;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
+
+import static java.lang.String.format;
+import static org.apache.commons.lang.StringUtils.isEmpty;
+import static org.apache.commons.lang.StringUtils.trimToNull;
+
+/**
+ * The default (and legacy) implementation of {@link Branch}. It is used
+ * when scanner is configured with parameter "sonar.branch".
+ * A legacy branch is implemented as a fork of the project, so any branch
+ * is considered as "main".
+ */
+public class MainBranchImpl implements Branch {
+
+  @Nullable
+  private final String branchName;
+
+  public MainBranchImpl(@Nullable String name) {
+    this.branchName = name;
+    if (name != null && !ComponentKeys.isValidBranch(name)) {
+      throw MessageException.of(format("\"%s\" is not a valid branch name. "
+        + "Allowed characters are alphanumeric, '-', '_', '.' and '/'.", name));
+    }
+  }
+
+  @Override
+  public BranchType getType() {
+    return BranchType.LONG;
+  }
+
+  @Override
+  public boolean isMain() {
+    return true;
+  }
+
+  @Override
+  public Optional<String> getName() {
+    return Optional.ofNullable(branchName);
+  }
+
+  @Override
+  public boolean supportsCrossProjectCpd() {
+    // only on regular project, not on branches
+    return branchName == null;
+  }
+
+  @Override
+  public String generateKey(ScannerReport.Component module, @Nullable ScannerReport.Component fileOrDir) {
+    String moduleWithBranch =  ComponentKeys.createKey(module.getKey(), branchName);
+    if (fileOrDir == null || isEmpty(fileOrDir.getPath())) {
+      return moduleWithBranch;
+    }
+    return ComponentKeys.createEffectiveKey(moduleWithBranch, trimToNull(fileOrDir.getPath()));
+  }
+}
index 3ef387a802a498f4e8d307b99af5dde2cd740cda..24c57476a2d55eef22f3da134ca98c9e297cea14 100644 (file)
@@ -24,7 +24,7 @@ package org.sonar.server.computation.task.projectanalysis.component;
  */
 public interface TreeRootHolder {
   /**
-   * The root of the tree of Component representing the component in the current ScannerReport.
+   * The root of the tree, for example the project or the portfolio.
    *
    * @throws IllegalStateException if the holder is empty (ie. there is no root yet)
    */
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/UuidFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/UuidFactory.java
deleted file mode 100644 (file)
index 393e79f..0000000
+++ /dev/null
@@ -1,49 +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 java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.sonar.core.util.Uuids;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
-
-public class UuidFactory {
-  private final Map<String, String> uuidsByKey = new HashMap<>();
-
-  public UuidFactory(DbClient dbClient, String rootKey) {
-    try (DbSession dbSession = dbClient.openSession(false)) {
-      List<ComponentDto> components = dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, rootKey);
-      for (ComponentDto componentDto : components) {
-        uuidsByKey.put(componentDto.getDbKey(), componentDto.uuid());
-      }
-    }
-  }
-
-  /**
-   * Get UUID from database if it exists, else generate a new one
-   */
-  public String getOrCreateForKey(String key) {
-    String uuid = uuidsByKey.get(key);
-    return (uuid == null) ? Uuids.create() : uuid;
-  }
-}
index 7b9392619c17fed22a1aad1ea623b11db11333dd..d4d2b580bda5c9ea8ae81dbe9cb6a8bbe14fa92c 100644 (file)
@@ -33,9 +33,10 @@ import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetada
 import org.sonar.server.computation.task.projectanalysis.api.posttask.PostProjectAnalysisTasksExecutor;
 import org.sonar.server.computation.task.projectanalysis.batch.BatchReportDirectoryHolderImpl;
 import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReaderImpl;
+import org.sonar.server.computation.task.projectanalysis.component.BranchLoader;
+import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepositoryImpl;
 import org.sonar.server.computation.task.projectanalysis.component.DbIdsRepositoryImpl;
 import org.sonar.server.computation.task.projectanalysis.component.DisabledComponentsHolderImpl;
-import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepositoryImpl;
 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderImpl;
 import org.sonar.server.computation.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolderImpl;
 import org.sonar.server.computation.task.projectanalysis.duplication.DuplicationMeasures;
@@ -161,7 +162,6 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop
       ActiveRulesHolderImpl.class,
       MeasureComputersHolderImpl.class,
       MutableTaskResultHolderImpl.class,
-
       BatchReportReaderImpl.class,
 
       // repositories
@@ -243,6 +243,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop
       // views
       ViewIndex.class,
 
+      BranchLoader.class,
       MeasureToMeasureDto.class,
 
       // webhooks
index da9c3a1b6e8048e290e9ed3b7c42cf5f6e36d18b..4a463e16c695881316c4d830000f85c6bce2c665 100644 (file)
  */
 package org.sonar.server.computation.task.projectanalysis.duplication;
 
+import java.util.Optional;
 import javax.annotation.CheckForNull;
 import org.picocontainer.Startable;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
 
 import static com.google.common.base.Preconditions.checkState;
 
@@ -47,13 +49,14 @@ public class CrossProjectDuplicationStatusHolderImpl implements CrossProjectDupl
 
   @Override
   public void start() {
-    boolean crossProjectDuplicationIsEnabledInReport = analysisMetadataHolder.isCrossProjectDuplicationEnabled();
-    boolean branchIsUsed = analysisMetadataHolder.getBranch() != null;
-    if (crossProjectDuplicationIsEnabledInReport && !branchIsUsed) {
+    boolean enabledInReport = analysisMetadataHolder.isCrossProjectDuplicationEnabled();
+    Optional<Branch> branch = analysisMetadataHolder.getBranch();
+    boolean supportedByBranch = !branch.isPresent() || branch.get().supportsCrossProjectCpd();
+    if (enabledInReport && supportedByBranch) {
       LOGGER.debug("Cross project duplication is enabled");
       this.enabled = true;
     } else {
-      if (!crossProjectDuplicationIsEnabledInReport) {
+      if (!enabledInReport) {
         LOGGER.debug("Cross project duplication is disabled because it's disabled in the analysis report");
       } else {
         LOGGER.debug("Cross project duplication is disabled because of a branch is used");
index 9fccec304427fe13b01563c88d57014fc4cde942..c9d41d231d43b68f4af7198b6ef03e88f290cbbf 100644 (file)
@@ -28,7 +28,6 @@ import org.sonar.db.DbSession;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
 import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
-import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
 
 import static org.sonar.api.CoreProperties.DEFAULT_ISSUE_ASSIGNEE;
 
@@ -41,16 +40,14 @@ public class DefaultAssignee {
   private static final Logger LOG = Loggers.get(DefaultAssignee.class);
 
   private final DbClient dbClient;
-  private final TreeRootHolder treeRootHolder;
   private final ConfigurationRepository configRepository;
   private final AnalysisMetadataHolder analysisMetadataHolder;
 
   private boolean loaded = false;
   private String login = null;
 
-  public DefaultAssignee(DbClient dbClient, TreeRootHolder treeRootHolder, ConfigurationRepository configRepository, AnalysisMetadataHolder analysisMetadataHolder) {
+  public DefaultAssignee(DbClient dbClient, ConfigurationRepository configRepository, AnalysisMetadataHolder analysisMetadataHolder) {
     this.dbClient = dbClient;
-    this.treeRootHolder = treeRootHolder;
     this.configRepository = configRepository;
     this.analysisMetadataHolder = analysisMetadataHolder;
   }
@@ -60,7 +57,7 @@ public class DefaultAssignee {
     if (loaded) {
       return login;
     }
-    String configuredLogin = configRepository.getConfiguration(treeRootHolder.getRoot()).get(DEFAULT_ISSUE_ASSIGNEE).orElse(null);
+    String configuredLogin = configRepository.getConfiguration().get(DEFAULT_ISSUE_ASSIGNEE).orElse(null);
     if (!Strings.isNullOrEmpty(configuredLogin) && isValidLogin(configuredLogin)) {
       this.login = configuredLogin;
     }
index d45d0338b3befc763c13cabfd0f83c2d8f9d9bb5..63746de05c9b9358dd33b1813c08e96e35df61dd 100644 (file)
@@ -30,7 +30,6 @@ import org.sonar.api.utils.log.Loggers;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
-import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Strings.isNullOrEmpty;
@@ -49,8 +48,8 @@ public class IssueFilter {
   private final List<IssuePattern> exclusionPatterns;
   private final List<IssuePattern> inclusionPatterns;
 
-  public IssueFilter(TreeRootHolder treeRootHolder, ConfigurationRepository configRepository) {
-    Configuration config = configRepository.getConfiguration(treeRootHolder.getRoot());
+  public IssueFilter(ConfigurationRepository configRepository) {
+    Configuration config = configRepository.getConfiguration();
     this.exclusionPatterns = loadPatterns(PATTERNS_MULTICRITERIA_EXCLUSION_KEY, config);
     this.inclusionPatterns = loadPatterns(PATTERNS_MULTICRITERIA_INCLUSION_KEY, config);
   }
index a524141097cde5e799a78ecbc43a35001b6e79bf..2ad4bb9fb399b6d4786dd5437df5ec40aaaf7595 100644 (file)
@@ -34,7 +34,7 @@ public class QualityGateServiceImpl implements QualityGateService {
   private final DbClient dbClient;
   private final MetricRepository metricRepository;
 
-  public QualityGateServiceImpl(DbClient dbClient, final MetricRepository metricRepository) {
+  public QualityGateServiceImpl(DbClient dbClient, MetricRepository metricRepository) {
     this.dbClient = dbClient;
     this.metricRepository = metricRepository;
   }
index 5592ff9d7cddbe3f9234602c4cfcd0b170f337d9..edc9921eb6fb1938928fef35149763162d57bb5f 100644 (file)
  */
 package org.sonar.server.computation.task.projectanalysis.step;
 
-import com.google.common.base.Optional;
-import java.util.Objects;
-import java.util.function.Function;
+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;
@@ -32,13 +33,14 @@ import org.sonar.server.computation.task.projectanalysis.analysis.Analysis;
 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.ComponentRootBuilder;
+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.MutableTreeRootHolder;
-import org.sonar.server.computation.task.projectanalysis.component.UuidFactory;
 import org.sonar.server.computation.task.step.ComputationStep;
 
-import static com.google.common.base.Preconditions.checkState;
-import static org.sonar.core.component.ComponentKeys.createKey;
+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}
@@ -50,7 +52,8 @@ public class BuildComponentTreeStep implements ComputationStep {
   private final MutableTreeRootHolder treeRootHolder;
   private final MutableAnalysisMetadataHolder analysisMetadataHolder;
 
-  public BuildComponentTreeStep(DbClient dbClient, BatchReportReader reportReader, MutableTreeRootHolder treeRootHolder, MutableAnalysisMetadataHolder analysisMetadataHolder) {
+  public BuildComponentTreeStep(DbClient dbClient, BatchReportReader reportReader,
+    MutableTreeRootHolder treeRootHolder, MutableAnalysisMetadataHolder analysisMetadataHolder) {
     this.dbClient = dbClient;
     this.reportReader = reportReader;
     this.treeRootHolder = treeRootHolder;
@@ -64,64 +67,52 @@ public class BuildComponentTreeStep implements ComputationStep {
 
   @Override
   public void execute() {
-    String branch = analysisMetadataHolder.getBranch();
-    ScannerReport.Component reportProject = reportReader.readComponent(analysisMetadataHolder.getRootComponentRef());
-    String projectKey = createKey(reportProject.getKey(), branch);
-    UuidFactory uuidFactory = new UuidFactory(dbClient, projectKey);
-
     try (DbSession dbSession = dbClient.openSession(false)) {
-      BaseAnalysisSupplier baseAnalysisSupplier = new BaseAnalysisSupplier(dbClient, dbSession);
-      ComponentRootBuilder rootBuilder = new ComponentRootBuilder(branch,
-        uuidFactory::getOrCreateForKey,
+      ScannerReport.Component reportProject = reportReader.readComponent(analysisMetadataHolder.getRootComponentRef());
+      ComponentKeyGenerator keyGenerator = loadKeyGenerator();
+
+      // root key of branch, not necessarily of project
+      String rootKey = keyGenerator.generateKey(reportProject, null);
+
+      // loads the UUIDs from database. If they don't exist, then generate new ones
+      ComponentUuidFactory componentUuidFactory = new ComponentUuidFactory(dbClient, dbSession, rootKey);
+
+      String rootUuid = componentUuidFactory.getOrCreateForKey(rootKey);
+      SnapshotDto baseAnalysis = loadBaseAnalysis(dbSession, rootUuid);
+
+      ComponentTreeBuilder builder = new ComponentTreeBuilder(keyGenerator,
+        componentUuidFactory::getOrCreateForKey,
         reportReader::readComponent,
-        () -> dbClient.componentDao().selectByKey(dbSession, projectKey),
-        baseAnalysisSupplier);
-      Component project = rootBuilder.build(reportProject, projectKey);
+        analysisMetadataHolder.getProject(),
+        baseAnalysis);
+      Component project = builder.buildProject(reportProject);
+
       treeRootHolder.setRoot(project);
-      analysisMetadataHolder.setBaseAnalysis(toAnalysis(baseAnalysisSupplier.apply(project.getUuid())));
+      analysisMetadataHolder.setBaseAnalysis(toAnalysis(baseAnalysis));
     }
   }
 
-  /**
-   * A supplier of the base analysis of the project (if it exists) that will cache the retrieved SnapshotDto and
-   * implement a sanity check to ensure it is always call with the same UUID value (since it's the project's UUID, it
-   * is unique for a whole task).
-   */
-  private static final class BaseAnalysisSupplier implements Function<String, Optional<SnapshotDto>> {
-    private final DbClient dbClient;
-    private final DbSession dbSession;
-    private String projectUuid = null;
-    private Optional<SnapshotDto> cache = null;
-
-    private BaseAnalysisSupplier(DbClient dbClient, DbSession dbSession) {
-      this.dbClient = dbClient;
-      this.dbSession = dbSession;
-    }
+  private ComponentKeyGenerator loadKeyGenerator() {
+    return Stream.of(analysisMetadataHolder.getBranch(), Optional.of(new DefaultKeyGenerator()))
+      // TODO pull request generator will be added here
+      .filter(Optional::isPresent)
+      .flatMap(x -> x.map(Stream::of).orElseGet(Stream::empty))
+      .findFirst()
+      .get();
+  }
 
-    @Override
-    public Optional<SnapshotDto> apply(String projectUuid) {
-      if (this.cache == null) {
-        this.cache = Optional.fromNullable(
-          dbClient.snapshotDao().selectAnalysisByQuery(
-            dbSession,
-            new SnapshotQuery()
-              .setComponentUuid(projectUuid)
-              .setIsLast(true)));
-        this.projectUuid = projectUuid;
-      } else {
-        checkState(
-          Objects.equals(this.projectUuid, projectUuid),
-          "BaseAnalysisSupplier called with different project uuid values. First one was %s but current one is %s",
-          this.projectUuid, projectUuid);
-      }
-      return this.cache;
-    }
+  @CheckForNull
+  private SnapshotDto loadBaseAnalysis(DbSession dbSession, String rootUuid) {
+    return dbClient.snapshotDao().selectAnalysisByQuery(
+      dbSession,
+      new SnapshotQuery()
+        .setComponentUuid(rootUuid)
+        .setIsLast(true));
   }
 
   @CheckForNull
-  private static Analysis toAnalysis(Optional<SnapshotDto> snapshotDto) {
-    if (snapshotDto.isPresent()) {
-      SnapshotDto dto = snapshotDto.get();
+  private static Analysis toAnalysis(@Nullable SnapshotDto dto) {
+    if (dto != null) {
       return new Analysis.Builder()
         .setId(dto.getId())
         .setUuid(dto.getUuid())
@@ -131,4 +122,14 @@ 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 ee17fcfcd1390a615b43169703cdc95643914184..27c193a6276fa2cc14c4311f46c5bff97b1cf59d 100644 (file)
@@ -93,7 +93,7 @@ public class LoadPeriodsStep implements ComputationStep {
     PeriodResolver periodResolver = new PeriodResolver(dbClient, session, projectDto.get().uuid(), analysisMetadataHolder.getAnalysisDate(),
       isReportType ? projectOrView.getReportAttributes().getVersion() : null);
 
-    Configuration config = configRepository.getConfiguration(projectOrView);
+    Configuration config = configRepository.getConfiguration();
     Period period = periodResolver.resolve(config);
     // SONAR-4700 Add a past snapshot only if it exists
     if (period != null) {
index ca1d5588d54fca78720860270328d4d307eef539..3248e2c780478c616a041e32e41a9552f8167a95 100644 (file)
 package org.sonar.server.computation.task.projectanalysis.step;
 
 import com.google.common.base.Optional;
-import org.apache.commons.lang.StringUtils;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
-import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
-import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit;
-import org.sonar.server.computation.task.projectanalysis.component.DepthTraversalTypeAwareCrawler;
-import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
-import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter;
 import org.sonar.server.computation.task.projectanalysis.qualitygate.MutableQualityGateHolder;
 import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGate;
 import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateService;
 import org.sonar.server.computation.task.step.ComputationStep;
 
-import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
+import static org.apache.commons.lang.StringUtils.isBlank;
 
 /**
  * This step retrieves the QualityGate and stores it in
@@ -46,14 +40,12 @@ public class LoadQualityGateStep implements ComputationStep {
 
   private static final String PROPERTY_QUALITY_GATE = "sonar.qualitygate";
 
-  private final TreeRootHolder treeRootHolder;
   private final ConfigurationRepository configRepository;
   private final QualityGateService qualityGateService;
   private final MutableQualityGateHolder qualityGateHolder;
 
-  public LoadQualityGateStep(TreeRootHolder treeRootHolder, ConfigurationRepository settingsRepository,
+  public LoadQualityGateStep(ConfigurationRepository settingsRepository,
     QualityGateService qualityGateService, MutableQualityGateHolder qualityGateHolder) {
-    this.treeRootHolder = treeRootHolder;
     this.configRepository = settingsRepository;
     this.qualityGateService = qualityGateService;
     this.qualityGateHolder = qualityGateHolder;
@@ -61,22 +53,11 @@ public class LoadQualityGateStep implements ComputationStep {
 
   @Override
   public void execute() {
-    new DepthTraversalTypeAwareCrawler(
-      new TypeAwareVisitorAdapter(CrawlerDepthLimit.PROJECT, PRE_ORDER) {
-        @Override
-        public void visitProject(Component project) {
-          executeForProject(project);
-        }
-      }).visit(treeRootHolder.getRoot());
-  }
-
-  private void executeForProject(Component project) {
-    String projectKey = project.getKey();
-    Configuration config = configRepository.getConfiguration(project);
+    Configuration config = configRepository.getConfiguration();
     String qualityGateSetting = config.get(PROPERTY_QUALITY_GATE).orElse(null);
 
-    if (StringUtils.isBlank(qualityGateSetting)) {
-      LOGGER.debug("No quality gate is configured for project " + projectKey);
+    if (isBlank(qualityGateSetting)) {
+      LOGGER.debug("No quality gate is configured");
       qualityGateHolder.setNoQualityGate();
       return;
     }
index 664fb463b9587b8fc76828bbe9f6fc5a45be19a6..e5f5f6bf33bead5c99ed3fb74cc90339112b3b1d 100644 (file)
@@ -25,13 +25,16 @@ import java.util.List;
 import java.util.Optional;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
 import org.sonar.api.utils.MessageException;
 import org.sonar.ce.queue.CeTask;
+import org.sonar.core.component.ComponentKeys;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginRepository;
 import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.scanner.protocol.output.ScannerReport;
@@ -39,18 +42,18 @@ import org.sonar.scanner.protocol.output.ScannerReport.Metadata.Plugin;
 import org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile;
 import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolder;
 import org.sonar.server.computation.task.projectanalysis.analysis.Organization;
+import org.sonar.server.computation.task.projectanalysis.analysis.Project;
 import org.sonar.server.computation.task.projectanalysis.analysis.ScannerPlugin;
 import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReader;
+import org.sonar.server.computation.task.projectanalysis.component.BranchLoader;
 import org.sonar.server.computation.task.step.ComputationStep;
-import org.sonar.server.organization.BillingValidations;
-import org.sonar.server.organization.BillingValidations.BillingValidationsException;
-import org.sonar.server.organization.BillingValidationsProxy;
 import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.qualityprofile.QualityProfile;
 
+import static java.util.stream.Collectors.toMap;
 import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Maps.transformValues;
 import static java.lang.String.format;
-import static java.util.stream.Collectors.toMap;
 import static org.apache.commons.lang.StringUtils.isNotEmpty;
 import static org.sonar.core.util.stream.MoreCollectors.toList;
 
@@ -58,50 +61,73 @@ import static org.sonar.core.util.stream.MoreCollectors.toList;
  * Feed analysis metadata holder with metadata from the analysis report.
  */
 public class LoadReportAnalysisMetadataHolderStep implements ComputationStep {
-
   private final CeTask ceTask;
   private final BatchReportReader reportReader;
-  private final MutableAnalysisMetadataHolder mutableAnalysisMetadataHolder;
+  private final MutableAnalysisMetadataHolder analysisMetadata;
   private final DefaultOrganizationProvider defaultOrganizationProvider;
   private final DbClient dbClient;
-  private final BillingValidations billingValidations;
+  private final BranchLoader branchLoader;
   private final PluginRepository pluginRepository;
 
-  public LoadReportAnalysisMetadataHolderStep(CeTask ceTask, BatchReportReader reportReader, MutableAnalysisMetadataHolder mutableAnalysisMetadataHolder,
-    DefaultOrganizationProvider defaultOrganizationProvider, DbClient dbClient, BillingValidationsProxy billingValidations, PluginRepository pluginRepository) {
+  public LoadReportAnalysisMetadataHolderStep(CeTask ceTask, BatchReportReader reportReader, MutableAnalysisMetadataHolder analysisMetadata,
+    DefaultOrganizationProvider defaultOrganizationProvider, DbClient dbClient, BranchLoader branchLoader, PluginRepository pluginRepository) {
     this.ceTask = ceTask;
     this.reportReader = reportReader;
-    this.mutableAnalysisMetadataHolder = mutableAnalysisMetadataHolder;
+    this.analysisMetadata = analysisMetadata;
     this.defaultOrganizationProvider = defaultOrganizationProvider;
     this.dbClient = dbClient;
-    this.billingValidations = billingValidations;
+    this.branchLoader = branchLoader;
     this.pluginRepository = pluginRepository;
   }
 
   @Override
   public void execute() {
     ScannerReport.Metadata reportMetadata = reportReader.readMetadata();
-    mutableAnalysisMetadataHolder.setAnalysisDate(reportMetadata.getAnalysisDate());
 
-    checkProjectKeyConsistency(reportMetadata);
+    loadMetadata(reportMetadata);
+    Organization organization = loadOrganization(reportMetadata);
+    loadProject(reportMetadata, organization);
+    loadIncrementalMode(reportMetadata);
+    loadQualityProfiles(reportMetadata, organization);
+    branchLoader.load(reportMetadata);
+  }
+
+  private void loadMetadata(ScannerReport.Metadata reportMetadata) {
+    analysisMetadata.setAnalysisDate(reportMetadata.getAnalysisDate());
+    analysisMetadata.setRootComponentRef(reportMetadata.getRootComponentRef());
+    analysisMetadata.setCrossProjectDuplicationEnabled(reportMetadata.getCrossProjectDuplicationActivated());
+  }
+
+  private void loadProject(ScannerReport.Metadata reportMetadata, Organization organization) {
+    String reportProjectKey = projectKeyFromReport(reportMetadata);
+    checkProjectKeyConsistency(reportProjectKey);
+    ComponentDto dto = toProject(reportProjectKey);
+    if (!dto.getOrganizationUuid().equals(organization.getUuid())) {
+      throw MessageException.of(format("Project is not in the expected organization: %s", organization.getKey()));
+    }
+    if (dto.getMainBranchProjectUuid() != null) {
+      throw MessageException.of("Project should not reference a branch");
+    }
+    analysisMetadata.setProject(new Project(dto.uuid(), dto.getDbKey(), dto.name()));
+  }
+
+  private Organization loadOrganization(ScannerReport.Metadata reportMetadata) {
     Organization organization = toOrganization(ceTask.getOrganizationUuid());
     checkOrganizationKeyConsistency(reportMetadata, organization);
-    checkOrganizationCanExecuteAnalysis(organization);
-    checkQualityProfilesConsistency(reportMetadata, organization);
+    analysisMetadata.setOrganization(organization);
+    return organization;
+  }
 
-    mutableAnalysisMetadataHolder.setRootComponentRef(reportMetadata.getRootComponentRef());
-    mutableAnalysisMetadataHolder.setBranch(isNotEmpty(reportMetadata.getBranch()) ? reportMetadata.getBranch() : null);
-    mutableAnalysisMetadataHolder.setCrossProjectDuplicationEnabled(reportMetadata.getCrossProjectDuplicationActivated());
-    mutableAnalysisMetadataHolder.setIncrementalAnalysis(reportMetadata.getIncremental());
-    mutableAnalysisMetadataHolder.setQProfilesByLanguage(reportMetadata.getQprofilesPerLanguage().values().stream()
+  private void loadQualityProfiles(ScannerReport.Metadata reportMetadata, Organization organization) {
+    checkQualityProfilesConsistency(reportMetadata, organization);
+    analysisMetadata.setQProfilesByLanguage(reportMetadata.getQprofilesPerLanguage().values().stream()
       .collect(toMap(
         QProfile::getLanguage,
         qp -> new QualityProfile(qp.getKey(), qp.getName(), qp.getLanguage(), new Date(qp.getRulesUpdatedAt())))));
-    mutableAnalysisMetadataHolder.setScannerPluginsByKey(reportMetadata.getPluginsByKey().values().stream()
+    analysisMetadata.setScannerPluginsByKey(reportMetadata.getPluginsByKey().values().stream()
       .collect(toMap(
         Plugin::getKey,
         p -> new ScannerPlugin(p.getKey(), getBasePluginKey(p), p.getUpdatedAt()))));
-    mutableAnalysisMetadataHolder.setOrganization(organization);
   }
 
   @CheckForNull
@@ -115,6 +141,10 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep {
     return pluginInfo.getBasePlugin();
   }
 
+  private void loadIncrementalMode(ScannerReport.Metadata reportMetadata) {
+    analysisMetadata.setIncrementalAnalysis(reportMetadata.getIncremental());
+  }
+
   /**
    * Check that the Quality profiles sent by scanner correctly relate to the project organization.
    */
@@ -134,8 +164,7 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep {
     }
   }
 
-  private void checkProjectKeyConsistency(ScannerReport.Metadata reportMetadata) {
-    String reportProjectKey = projectKeyFromReport(reportMetadata);
+  private void checkProjectKeyConsistency(String reportProjectKey) {
     String componentKey = ceTask.getComponentKey();
     if (componentKey == null) {
       throw MessageException.of(format(
@@ -168,14 +197,6 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep {
     }
   }
 
-  private void checkOrganizationCanExecuteAnalysis(Organization organization) {
-    try {
-      billingValidations.checkOnProjectAnalysis(new BillingValidations.Organization(organization.getKey(), organization.getUuid()));
-    } catch (BillingValidationsException e) {
-      throw MessageException.of(e.getMessage());
-    }
-  }
-
   private String resolveReportOrganizationKey(@Nullable String organizationKey) {
     if (reportBelongsToDefaultOrganization(organizationKey)) {
       return defaultOrganizationProvider.get().getKey();
@@ -190,14 +211,23 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep {
   private Organization toOrganization(String organizationUuid) {
     try (DbSession dbSession = dbClient.openSession(false)) {
       Optional<OrganizationDto> organizationDto = dbClient.organizationDao().selectByUuid(dbSession, organizationUuid);
-      checkState(organizationDto.isPresent(), "Organization with uuid '{}' can't be found", organizationUuid);
+      checkState(organizationDto.isPresent(), "Organization with uuid '%s' can't be found", organizationUuid);
       return Organization.from(organizationDto.get());
     }
   }
 
+  private ComponentDto toProject(String projectKey) {
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      com.google.common.base.Optional<ComponentDto> opt = dbClient.componentDao().selectByKey(dbSession, projectKey);
+      checkState(opt.isPresent(), "Project with key '%s' can't be found", projectKey);
+      return opt.get();
+    }
+  }
+
   private static String projectKeyFromReport(ScannerReport.Metadata reportMetadata) {
-    if (isNotEmpty(reportMetadata.getBranch())) {
-      return reportMetadata.getProjectKey() + ":" + reportMetadata.getBranch();
+    String deprecatedBranch = reportMetadata.getDeprecatedBranch();
+    if (StringUtils.isNotEmpty(deprecatedBranch)) {
+      return ComponentKeys.createKey(reportMetadata.getProjectKey(), deprecatedBranch);
     }
     return reportMetadata.getProjectKey();
   }
index 985b2f60fb7d549b14ae077830f7587e741bd563..7961ca9f41077be838115eaa234ac3fd04caba01 100644 (file)
@@ -26,7 +26,9 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
+import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.resources.Qualifiers;
@@ -38,6 +40,8 @@ import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentUpdateDto;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
+import org.sonar.server.computation.task.projectanalysis.component.BranchPersisterDelegate;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit;
 import org.sonar.server.computation.task.projectanalysis.component.DbIdsRepositoryImpl;
@@ -50,6 +54,7 @@ import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolde
 import org.sonar.server.computation.task.step.ComputationStep;
 
 import static com.google.common.collect.FluentIterable.from;
+import static java.util.Optional.ofNullable;
 import static org.sonar.db.component.ComponentDto.UUID_PATH_OF_ROOT;
 import static org.sonar.db.component.ComponentDto.UUID_PATH_SEPARATOR;
 import static org.sonar.db.component.ComponentDto.formatUuidPathFromParent;
@@ -66,16 +71,26 @@ public class PersistComponentsStep implements ComputationStep {
   private final System2 system2;
   private final MutableDisabledComponentsHolder disabledComponentsHolder;
   private final AnalysisMetadataHolder analysisMetadataHolder;
+  @Nullable
+  private final BranchPersisterDelegate branchPersister;
 
   public PersistComponentsStep(DbClient dbClient, TreeRootHolder treeRootHolder,
     MutableDbIdsRepository dbIdsRepository, System2 system2,
     MutableDisabledComponentsHolder disabledComponentsHolder, AnalysisMetadataHolder analysisMetadataHolder) {
+    this(dbClient, treeRootHolder, dbIdsRepository, system2, disabledComponentsHolder, analysisMetadataHolder, null);
+  }
+
+  public PersistComponentsStep(DbClient dbClient, TreeRootHolder treeRootHolder,
+    MutableDbIdsRepository dbIdsRepository, System2 system2,
+    MutableDisabledComponentsHolder disabledComponentsHolder, AnalysisMetadataHolder analysisMetadataHolder,
+    @Nullable BranchPersisterDelegate branchPersister) {
     this.dbClient = dbClient;
     this.treeRootHolder = treeRootHolder;
     this.dbIdsRepository = dbIdsRepository;
     this.system2 = system2;
     this.disabledComponentsHolder = disabledComponentsHolder;
     this.analysisMetadataHolder = analysisMetadataHolder;
+    this.branchPersister = branchPersister;
   }
 
   @Override
@@ -86,6 +101,8 @@ public class PersistComponentsStep implements ComputationStep {
   @Override
   public void execute() {
     try (DbSession dbSession = dbClient.openSession(false)) {
+      ofNullable(branchPersister).ifPresent(p -> p.persist(dbSession));
+
       String projectUuid = treeRootHolder.getRoot().getUuid();
 
       // safeguard, reset all rows to b-changed=false
@@ -93,9 +110,11 @@ public class PersistComponentsStep implements ComputationStep {
 
       Map<String, ComponentDto> existingDtosByKeys = indexExistingDtosByKey(dbSession);
       boolean isRootPrivate = isRootPrivate(treeRootHolder.getRoot(), existingDtosByKeys);
+      String mainBranchProjectUuid = loadProjectUuidOfMainBranch();
+
       // Insert or update the components in database. They are removed from existingDtosByKeys
       // at the same time.
-      new PathAwareCrawler<>(new PersistComponentStepsVisitor(existingDtosByKeys, dbSession))
+      new PathAwareCrawler<>(new PersistComponentStepsVisitor(existingDtosByKeys, dbSession, mainBranchProjectUuid))
         .visit(treeRootHolder.getRoot());
 
       disableRemainingComponents(dbSession, existingDtosByKeys.values());
@@ -105,6 +124,19 @@ public class PersistComponentsStep implements ComputationStep {
     }
   }
 
+  /**
+   * See {@link ComponentDto#mainBranchProjectUuid} : value is null on main branches, otherwise it is
+   * the uuid of the main branch.
+   */
+  @CheckForNull
+  private String loadProjectUuidOfMainBranch() {
+    Optional<Branch> branch = analysisMetadataHolder.getBranch();
+    if (branch.isPresent() && !branch.get().isMain()) {
+      return analysisMetadataHolder.getProject().getUuid();
+    }
+    return null;
+  }
+
   private void disableRemainingComponents(DbSession dbSession, Collection<ComponentDto> dtos) {
     Set<String> uuids = dtos.stream()
       .filter(ComponentDto::isEnabled)
@@ -144,8 +176,10 @@ public class PersistComponentsStep implements ComputationStep {
 
     private final Map<String, ComponentDto> existingComponentDtosByKey;
     private final DbSession dbSession;
+    @Nullable
+    private final String mainBranchProjectUuid;
 
-    public PersistComponentStepsVisitor(Map<String, ComponentDto> existingComponentDtosByKey, DbSession dbSession) {
+    PersistComponentStepsVisitor(Map<String, ComponentDto> existingComponentDtosByKey, DbSession dbSession, @Nullable String mainBranchProjectUuid) {
       super(
         CrawlerDepthLimit.LEAVES,
         PRE_ORDER,
@@ -169,6 +203,7 @@ public class PersistComponentsStep implements ComputationStep {
         });
       this.existingComponentDtosByKey = existingComponentDtosByKey;
       this.dbSession = dbSession;
+      this.mainBranchProjectUuid = mainBranchProjectUuid;
     }
 
     @Override
@@ -250,141 +285,143 @@ public class PersistComponentsStep implements ComputationStep {
     private void addToCache(Component component, ComponentDto componentDto) {
       dbIdsRepository.setComponentId(component, componentDto.getId());
     }
-  }
 
-  public ComponentDto createForProject(Component project) {
-    ComponentDto res = createBase(project);
+    public ComponentDto createForProject(Component project) {
+      ComponentDto res = createBase(project);
 
-    res.setScope(Scopes.PROJECT);
-    res.setQualifier(Qualifiers.PROJECT);
-    res.setName(project.getName());
-    res.setLongName(res.name());
-    res.setDescription(project.getDescription());
+      res.setScope(Scopes.PROJECT);
+      res.setQualifier(Qualifiers.PROJECT);
+      res.setName(project.getName());
+      res.setLongName(res.name());
+      res.setDescription(project.getDescription());
 
-    res.setProjectUuid(res.uuid());
-    res.setRootUuid(res.uuid());
-    res.setUuidPath(UUID_PATH_OF_ROOT);
-    res.setModuleUuidPath(UUID_PATH_SEPARATOR + res.uuid() + UUID_PATH_SEPARATOR);
+      res.setProjectUuid(res.uuid());
+      res.setRootUuid(res.uuid());
+      res.setUuidPath(UUID_PATH_OF_ROOT);
+      res.setModuleUuidPath(UUID_PATH_SEPARATOR + res.uuid() + UUID_PATH_SEPARATOR);
 
-    return res;
-  }
+      return res;
+    }
 
-  public ComponentDto createForModule(Component module, PathAwareVisitor.Path<ComponentDtoHolder> path) {
-    ComponentDto res = createBase(module);
+    public ComponentDto createForModule(Component module, PathAwareVisitor.Path<ComponentDtoHolder> path) {
+      ComponentDto res = createBase(module);
 
-    res.setScope(Scopes.PROJECT);
-    res.setQualifier(Qualifiers.MODULE);
-    res.setName(module.getName());
-    res.setLongName(res.name());
-    res.setPath(module.getReportAttributes().getPath());
-    res.setDescription(module.getDescription());
+      res.setScope(Scopes.PROJECT);
+      res.setQualifier(Qualifiers.MODULE);
+      res.setName(module.getName());
+      res.setLongName(res.name());
+      res.setPath(module.getReportAttributes().getPath());
+      res.setDescription(module.getDescription());
 
-    setRootAndParentModule(res, path);
+      setRootAndParentModule(res, path);
 
-    return res;
-  }
+      return res;
+    }
 
-  public ComponentDto createForDirectory(Component directory, PathAwareVisitor.Path<ComponentDtoHolder> path) {
-    ComponentDto res = createBase(directory);
+    public ComponentDto createForDirectory(Component directory, PathAwareVisitor.Path<ComponentDtoHolder> path) {
+      ComponentDto res = createBase(directory);
 
-    res.setScope(Scopes.DIRECTORY);
-    res.setQualifier(Qualifiers.DIRECTORY);
-    res.setName(directory.getReportAttributes().getPath());
-    res.setLongName(directory.getReportAttributes().getPath());
-    res.setPath(directory.getReportAttributes().getPath());
+      res.setScope(Scopes.DIRECTORY);
+      res.setQualifier(Qualifiers.DIRECTORY);
+      res.setName(directory.getReportAttributes().getPath());
+      res.setLongName(directory.getReportAttributes().getPath());
+      res.setPath(directory.getReportAttributes().getPath());
 
-    setParentModuleProperties(res, path);
+      setParentModuleProperties(res, path);
 
-    return res;
-  }
+      return res;
+    }
 
-  public ComponentDto createForFile(Component file, PathAwareVisitor.Path<ComponentDtoHolder> path) {
-    ComponentDto res = createBase(file);
+    public ComponentDto createForFile(Component file, PathAwareVisitor.Path<ComponentDtoHolder> path) {
+      ComponentDto res = createBase(file);
 
-    res.setScope(Scopes.FILE);
-    res.setQualifier(getFileQualifier(file));
-    res.setName(FilenameUtils.getName(file.getReportAttributes().getPath()));
-    res.setLongName(file.getReportAttributes().getPath());
-    res.setPath(file.getReportAttributes().getPath());
-    res.setLanguage(file.getFileAttributes().getLanguageKey());
+      res.setScope(Scopes.FILE);
+      res.setQualifier(getFileQualifier(file));
+      res.setName(FilenameUtils.getName(file.getReportAttributes().getPath()));
+      res.setLongName(file.getReportAttributes().getPath());
+      res.setPath(file.getReportAttributes().getPath());
+      res.setLanguage(file.getFileAttributes().getLanguageKey());
 
-    setParentModuleProperties(res, path);
+      setParentModuleProperties(res, path);
 
-    return res;
-  }
+      return res;
+    }
 
-  private ComponentDto createForView(Component view) {
-    ComponentDto res = createBase(view);
+    private ComponentDto createForView(Component view) {
+      ComponentDto res = createBase(view);
 
-    res.setScope(Scopes.PROJECT);
-    res.setQualifier(view.getViewAttributes().getType().getQualifier());
-    res.setName(view.getName());
-    res.setDescription(view.getDescription());
-    res.setLongName(res.name());
+      res.setScope(Scopes.PROJECT);
+      res.setQualifier(view.getViewAttributes().getType().getQualifier());
+      res.setName(view.getName());
+      res.setDescription(view.getDescription());
+      res.setLongName(res.name());
 
-    res.setProjectUuid(res.uuid());
-    res.setRootUuid(res.uuid());
-    res.setUuidPath(UUID_PATH_OF_ROOT);
-    res.setModuleUuidPath(UUID_PATH_SEPARATOR + res.uuid() + UUID_PATH_SEPARATOR);
+      res.setProjectUuid(res.uuid());
+      res.setRootUuid(res.uuid());
+      res.setUuidPath(UUID_PATH_OF_ROOT);
+      res.setModuleUuidPath(UUID_PATH_SEPARATOR + res.uuid() + UUID_PATH_SEPARATOR);
 
-    return res;
-  }
+      return res;
+    }
 
-  private ComponentDto createForSubView(Component subView, PathAwareVisitor.Path<ComponentDtoHolder> path) {
-    ComponentDto res = createBase(subView);
+    private ComponentDto createForSubView(Component subView, PathAwareVisitor.Path<ComponentDtoHolder> path) {
+      ComponentDto res = createBase(subView);
 
-    res.setScope(Scopes.PROJECT);
-    res.setQualifier(Qualifiers.SUBVIEW);
-    res.setName(subView.getName());
-    res.setDescription(subView.getDescription());
-    res.setLongName(res.name());
-    res.setCopyComponentUuid(subView.getSubViewAttributes().getOriginalViewUuid());
+      res.setScope(Scopes.PROJECT);
+      res.setQualifier(Qualifiers.SUBVIEW);
+      res.setName(subView.getName());
+      res.setDescription(subView.getDescription());
+      res.setLongName(res.name());
+      res.setCopyComponentUuid(subView.getSubViewAttributes().getOriginalViewUuid());
 
-    setRootAndParentModule(res, path);
+      setRootAndParentModule(res, path);
 
-    return res;
-  }
+      return res;
+    }
 
-  private ComponentDto createForProjectView(Component projectView, PathAwareVisitor.Path<ComponentDtoHolder> path) {
-    ComponentDto res = createBase(projectView);
+    private ComponentDto createForProjectView(Component projectView, PathAwareVisitor.Path<ComponentDtoHolder> path) {
+      ComponentDto res = createBase(projectView);
 
-    res.setScope(Scopes.FILE);
-    res.setQualifier(Qualifiers.PROJECT);
-    res.setName(projectView.getName());
-    res.setLongName(res.name());
-    res.setCopyComponentUuid(projectView.getProjectViewAttributes().getProjectUuid());
+      res.setScope(Scopes.FILE);
+      res.setQualifier(Qualifiers.PROJECT);
+      res.setName(projectView.getName());
+      res.setLongName(res.name());
+      res.setCopyComponentUuid(projectView.getProjectViewAttributes().getProjectUuid());
 
-    setRootAndParentModule(res, path);
+      setRootAndParentModule(res, path);
 
-    return res;
-  }
+      return res;
+    }
 
-  private ComponentDto createBase(Component component) {
-    String componentKey = component.getKey();
-    String componentUuid = component.getUuid();
-
-    ComponentDto componentDto = new ComponentDto();
-    componentDto.setOrganizationUuid(analysisMetadataHolder.getOrganization().getUuid());
-    componentDto.setUuid(componentUuid);
-    componentDto.setDbKey(componentKey);
-    componentDto.setDeprecatedKey(componentKey);
-    componentDto.setEnabled(true);
-    componentDto.setCreatedAt(new Date(system2.now()));
-    return componentDto;
-  }
+    private ComponentDto createBase(Component component) {
+      String componentKey = component.getKey();
+      String componentUuid = component.getUuid();
 
-  /**
-   * Applies to a node of type either MODULE, SUBVIEW, PROJECT_VIEW
-   */
-  private static void setRootAndParentModule(ComponentDto res, PathAwareVisitor.Path<ComponentDtoHolder> path) {
-    ComponentDto rootDto = path.root().getDto();
-    res.setRootUuid(rootDto.uuid());
-    res.setProjectUuid(rootDto.uuid());
-
-    ComponentDto parentModule = path.parent().getDto();
-    res.setUuidPath(formatUuidPathFromParent(parentModule));
-    res.setModuleUuid(parentModule.uuid());
-    res.setModuleUuidPath(parentModule.moduleUuidPath() + res.uuid() + UUID_PATH_SEPARATOR);
+      ComponentDto componentDto = new ComponentDto();
+      componentDto.setOrganizationUuid(analysisMetadataHolder.getOrganization().getUuid());
+      componentDto.setUuid(componentUuid);
+      componentDto.setDbKey(componentKey);
+      componentDto.setDeprecatedKey(componentKey);
+      componentDto.setMainBranchProjectUuid(mainBranchProjectUuid);
+      componentDto.setEnabled(true);
+      componentDto.setCreatedAt(new Date(system2.now()));
+
+      return componentDto;
+    }
+
+    /**
+     * Applies to a node of type either MODULE, SUBVIEW, PROJECT_VIEW
+     */
+    private void setRootAndParentModule(ComponentDto res, PathAwareVisitor.Path<ComponentDtoHolder> path) {
+      ComponentDto rootDto = path.root().getDto();
+      res.setRootUuid(rootDto.uuid());
+      res.setProjectUuid(rootDto.uuid());
+
+      ComponentDto parentModule = path.parent().getDto();
+      res.setUuidPath(formatUuidPathFromParent(parentModule));
+      res.setModuleUuid(parentModule.uuid());
+      res.setModuleUuidPath(parentModule.moduleUuidPath() + res.uuid() + UUID_PATH_SEPARATOR);
+    }
   }
 
   /**
@@ -424,7 +461,7 @@ public class PersistComponentsStep implements ComputationStep {
         .copyFrom(target)
         .setBChanged(true);
     }
-    return Optional.ofNullable(update);
+    return ofNullable(update);
   }
 
   private static String getFileQualifier(Component component) {
index 677689f58a462406d1496edd82a9edfe697ec4a7..5f9026e824a3f8ef0899abfcd6b60c04b183aad6 100644 (file)
@@ -75,7 +75,7 @@ public class PurgeDatastoresStep implements ComputationStep {
   private void execute(Component root) {
     try (DbSession dbSession = dbClient.openSession(true)) {
       IdUuidPair idUuidPair = new IdUuidPair(dbIdsRepository.getComponentId(root), root.getUuid());
-      projectCleaner.purge(dbSession, idUuidPair, configRepository.getConfiguration(root), disabledComponentsHolder.getUuids());
+      projectCleaner.purge(dbSession, idUuidPair, configRepository.getConfiguration(), disabledComponentsHolder.getUuids());
       dbSession.commit();
     }
   }
index da8c73acd1dc3d90ed143487e4439b07fa3abc33..d39e3c4021a7b4ad7d57800a4ea0d586c39f10de 100644 (file)
@@ -92,7 +92,7 @@ public class QualityGateEventsStep implements ComputationStep {
     }
 
     if (!baseMeasure.get().hasQualityGateStatus()) {
-      LOGGER.warn(String.format("Previous alterStatus for project %s is not a supported value. Can not compute Quality Gate event", project.getKey()));
+      LOGGER.warn(String.format("Previous Quality gate status for project %s is not a supported value. Can not compute Quality Gate event", project.getKey()));
       checkNewQualityGate(project, rawStatus);
       return;
     }
index 02a4df5f3dadbc3b664b856c8b41e0a531be691a..975422d8b268b27b777198b6c3ee451820308c37 100644 (file)
@@ -37,6 +37,7 @@ public class ReportComputationSteps extends AbstractComputationSteps {
 
     // Builds Component tree
     LoadReportAnalysisMetadataHolderStep.class,
+    VerifyBillingStep.class,
     BuildComponentTreeStep.class,
     ValidateProjectStep.class,
 
index 56dc9be618d5fef452be28044267309089a12ef1..77417850dad6ae69be5b64ad7e16c4908bc1d014 100644 (file)
@@ -126,7 +126,6 @@ public class ValidateProjectStep implements ComputationStep {
     public void visitProject(Component rawProject) {
       this.rawProject = rawProject;
       String rawProjectKey = rawProject.getKey();
-      validateBranch();
       validateIncremental(rawProjectKey);
       validateNotIncrementalAndFirstAnalysis(rawProjectKey);
       validateBatchKey(rawProject);
@@ -140,6 +139,7 @@ public class ValidateProjectStep implements ComputationStep {
     private void validateRootIsProject(Optional<ComponentDto> baseProject) {
       if (baseProject.isPresent()) {
         ComponentDto componentDto = baseProject.get();
+        // the scope field is verified for excluding the project copies generated by portfolios
         if (!Qualifiers.PROJECT.equals(componentDto.qualifier()) || !Scopes.PROJECT.equals(componentDto.scope())) {
           validationMessages.add(format("Component (uuid=%s, key=%s) is not a project", rawProject.getUuid(), rawProject.getKey()));
         }
@@ -226,17 +226,6 @@ public class ValidateProjectStep implements ComputationStep {
       }
     }
 
-    private void validateBranch() {
-      String branch = analysisMetadataHolder.getBranch();
-      if (branch == null) {
-        return;
-      }
-      if (!ComponentKeys.isValidBranch(branch)) {
-        validationMessages.add(format("\"%s\" is not a valid branch name. "
-          + "Allowed characters are alphanumeric, '-', '_', '.' and '/'.", branch));
-      }
-    }
-
     private Optional<ComponentDto> loadBaseComponent(String rawComponentKey) {
       ComponentDto baseComponent = baseModulesByKey.get(rawComponentKey);
       if (baseComponent == null) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/VerifyBillingStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/VerifyBillingStep.java
new file mode 100644 (file)
index 0000000..74c3cd3
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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.step;
+
+import org.sonar.api.utils.MessageException;
+import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.server.computation.task.projectanalysis.analysis.Organization;
+import org.sonar.server.computation.task.step.ComputationStep;
+import org.sonar.server.organization.BillingValidations;
+import org.sonar.server.organization.BillingValidationsProxy;
+
+/**
+ * Verify that organization can execute analysis
+ */
+public class VerifyBillingStep implements ComputationStep {
+
+  private final AnalysisMetadataHolder analysisMetadata;
+  private final BillingValidations billingValidations;
+
+  public VerifyBillingStep(AnalysisMetadataHolder analysisMetadata, BillingValidationsProxy billingValidations) {
+    this.analysisMetadata = analysisMetadata;
+    this.billingValidations = billingValidations;
+  }
+
+  @Override
+  public void execute() {
+    try {
+      Organization organization = analysisMetadata.getOrganization();
+      BillingValidations.Organization billingOrganization = new BillingValidations.Organization(organization.getKey(), organization.getUuid());
+      billingValidations.checkOnProjectAnalysis(billingOrganization);
+    } catch (BillingValidations.BillingValidationsException e) {
+      throw MessageException.of(e.getMessage());
+    }
+  }
+
+  @Override
+  public String getDescription() {
+    return "Verify billing";
+  }
+}
index c66de9fd3ae68bf8c6f44e1c617cc02e8db930a1..7b124b756991ede934ba8cbe2e14ab4a3d4f17cf 100644 (file)
@@ -30,7 +30,6 @@ import org.sonar.api.utils.log.Loggers;
 import org.sonar.core.config.WebhookProperties;
 import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
-import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
 
 import static java.lang.String.format;
 import static org.sonar.core.config.WebhookProperties.MAX_WEBHOOKS_PER_TYPE;
@@ -39,16 +38,14 @@ public class WebhookPostTask implements PostProjectAnalysisTask {
 
   private static final Logger LOGGER = Loggers.get(WebhookPostTask.class);
 
-  private final TreeRootHolder rootHolder;
   private final ConfigurationRepository configRepository;
   private final WebhookPayloadFactory payloadFactory;
   private final WebhookCaller caller;
   private final WebhookDeliveryStorage deliveryStorage;
 
-  public WebhookPostTask(TreeRootHolder rootHolder, ConfigurationRepository settingsRepository, WebhookPayloadFactory payloadFactory,
+  public WebhookPostTask(ConfigurationRepository configRepository, WebhookPayloadFactory payloadFactory,
     WebhookCaller caller, WebhookDeliveryStorage deliveryStorage) {
-    this.rootHolder = rootHolder;
-    this.configRepository = settingsRepository;
+    this.configRepository = configRepository;
     this.payloadFactory = payloadFactory;
     this.caller = caller;
     this.deliveryStorage = deliveryStorage;
@@ -56,7 +53,7 @@ public class WebhookPostTask implements PostProjectAnalysisTask {
 
   @Override
   public void finished(ProjectAnalysis analysis) {
-    Configuration config = configRepository.getConfiguration(rootHolder.getRoot());
+    Configuration config = configRepository.getConfiguration();
 
     Iterable<String> webhookProps = Iterables.concat(
       getWebhookProperties(config, WebhookProperties.GLOBAL_KEY),
index 64c2277ead64b9e124fc505f976e3b1deaabe98d..eb68407e8bdf2608dddf09cb956886be58dc1f5c 100644 (file)
 
 package org.sonar.server.organization;
 
+import javax.annotation.Nullable;
+
 public class BillingValidationsProxyImpl implements BillingValidationsProxy {
 
+  @Nullable
   private final BillingValidationsExtension billingValidationsExtension;
 
-  public BillingValidationsProxyImpl(BillingValidationsExtension billingValidationsExtension) {
-    this.billingValidationsExtension = billingValidationsExtension;
+  public BillingValidationsProxyImpl(BillingValidationsExtension e) {
+    this.billingValidationsExtension = e;
   }
 
   // Used when no plugin is providing the extension
index c6766c405b47cfc777d055907f806429fd15f463..4456a521e7978ad956fc8fe8f740771777ca7846 100644 (file)
@@ -30,7 +30,6 @@ import javax.annotation.Nullable;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.server.ServerSide;
-import org.sonar.core.component.ComponentKeys;
 import org.sonar.core.permission.ProjectPermissions;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
@@ -73,14 +72,12 @@ public class PermissionTemplateService {
 
   public boolean wouldUserHaveScanPermissionWithDefaultTemplate(DbSession dbSession,
     String organizationUuid, @Nullable Integer userId,
-    @Nullable String branch, String projectKey,
-    String qualifier) {
+    String projectKey, String qualifier) {
     if (userSession.hasPermission(OrganizationPermission.SCAN, organizationUuid)) {
       return true;
     }
 
-    String effectiveKey = ComponentKeys.createKey(projectKey, branch);
-    ComponentDto dto = new ComponentDto().setOrganizationUuid(organizationUuid).setDbKey(effectiveKey).setQualifier(qualifier);
+    ComponentDto dto = new ComponentDto().setOrganizationUuid(organizationUuid).setDbKey(projectKey).setQualifier(qualifier);
     PermissionTemplateDto template = findTemplate(dbSession, organizationUuid, dto);
     if (template == null) {
       return false;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/BranchesAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/BranchesAction.java
new file mode 100644 (file)
index 0000000..83c6eee
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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.project.ws;
+
+import java.util.Collection;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchKeyType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.ws.WsUtils;
+import org.sonarqube.ws.WsProjects;
+
+import static org.sonar.core.util.Protobuf.setNullable;
+
+public class BranchesAction implements ProjectsWsAction {
+
+  private static final String PROJECT_PARAM = "project";
+
+  private final DbClient dbClient;
+  private final UserSession userSession;
+
+  public BranchesAction(DbClient dbClient, UserSession userSession) {
+    this.dbClient = dbClient;
+    this.userSession = userSession;
+  }
+
+  @Override
+  public void define(WebService.NewController context) {
+    WebService.NewAction action = context.createAction("branches")
+      .setSince("6.6")
+      .setHandler(this);
+
+    action
+      .createParam(PROJECT_PARAM)
+      .setRequired(true);
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    String projectKey = request.mandatoryParam(PROJECT_PARAM);
+
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      ComponentDto project = dbClient.componentDao().selectOrFailByKey(dbSession, projectKey);
+      userSession.checkComponentPermission(UserRole.USER, project);
+      Collection<BranchDto> branches = dbClient.branchDao().selectByComponent(dbSession, project);
+
+      WsProjects.BranchesWsResponse.Builder protobufResponse = WsProjects.BranchesWsResponse.newBuilder();
+      branches.stream()
+        .filter(b -> b.getKeeType().equals(BranchKeyType.BRANCH))
+        .forEach(b -> addToProtobuf(protobufResponse, b));
+      WsUtils.writeProtobuf(protobufResponse.build(), request, response);
+    }
+  }
+
+  private static void addToProtobuf(WsProjects.BranchesWsResponse.Builder response, BranchDto branch) {
+    WsProjects.BranchesWsResponse.Branch.Builder builder = response.addBranchesBuilder();
+    setNullable(branch.getKey(), builder::setName);
+    builder.setIsMain(branch.isMain());
+    builder.setType(WsProjects.BranchesWsResponse.BranchType.valueOf(branch.getBranchType().name()));
+    builder.build();
+  }
+}
index 48ce49927e7544e57f6de2a69ee3eab4c797adaa..bb8379878d3f6e2408ff867360373aa35055defc 100644 (file)
@@ -29,6 +29,7 @@ public class ProjectsWsModule extends Module {
       ProjectsWs.class,
       CreateAction.class,
       IndexAction.class,
+      BranchesAction.class,
       BulkDeleteAction.class,
       DeleteAction.class,
       UpdateKeyAction.class,
index 931a979096811b3bfdd2c6b4533b67f6a6efc3a2..6c947ae6501339fbb64ae9baac2a7b4913f660ea 100644 (file)
@@ -58,7 +58,6 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.tuple;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -127,9 +126,9 @@ public class ReportSubmitterTest {
     mockSuccessfulPrepareSubmitCall();
     ComponentDto project = newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setDbKey(PROJECT_KEY);
     when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), eq(null))).thenReturn(project);
-    when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), anyInt(), anyString(),
-      eq(PROJECT_KEY), eq(Qualifiers.PROJECT)))
-        .thenReturn(true);
+    when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), anyInt(), eq(PROJECT_KEY),
+      eq(Qualifiers.PROJECT)))
+      .thenReturn(true);
 
     Map<String, String> taskCharacteristics = new HashMap<>();
     taskCharacteristics.put("incremental", "true");
@@ -183,9 +182,9 @@ public class ReportSubmitterTest {
     mockSuccessfulPrepareSubmitCall();
     ComponentDto createdProject = newPrivateProjectDto(organization, PROJECT_UUID).setDbKey(PROJECT_KEY);
     when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), eq(null))).thenReturn(createdProject);
-    when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), anyInt(), anyString(),
-      eq(PROJECT_KEY), eq(Qualifiers.PROJECT)))
-        .thenReturn(true);
+    when(
+      permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), anyInt(), eq(PROJECT_KEY), eq(Qualifiers.PROJECT)))
+      .thenReturn(true);
     when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), eq(organization.getUuid()), any(ComponentDto.class))).thenReturn(true);
 
     underTest.submit(organization.getKey(), PROJECT_KEY, null, PROJECT_NAME, IOUtils.toInputStream("{binary}"));
@@ -214,9 +213,9 @@ public class ReportSubmitterTest {
     mockSuccessfulPrepareSubmitCall();
     ComponentDto createdProject = newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setDbKey(PROJECT_KEY);
     when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), eq(null))).thenReturn(createdProject);
-    when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), anyInt(), anyString(),
+    when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), anyInt(),
       eq(PROJECT_KEY), eq(Qualifiers.PROJECT)))
-        .thenReturn(true);
+      .thenReturn(true);
     when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), eq(defaultOrganizationUuid), any(ComponentDto.class))).thenReturn(false);
 
     underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, IOUtils.toInputStream("{binary}"));
@@ -233,9 +232,9 @@ public class ReportSubmitterTest {
     mockSuccessfulPrepareSubmitCall();
     ComponentDto project = newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setDbKey(PROJECT_KEY);
     when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), eq(null))).thenReturn(project);
-    when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), anyInt(), anyString(),
+    when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), anyInt(),
       eq(PROJECT_KEY), eq(Qualifiers.PROJECT)))
-        .thenReturn(true);
+      .thenReturn(true);
 
     underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, IOUtils.toInputStream("{binary}"));
 
index 894ebcdc85fd9f75811044ef850836f9c0f15eda..bd7f6543c7764f455c6d4cdca15062295f10cd45 100644 (file)
@@ -30,9 +30,9 @@ public class AnalysisImplTest {
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
-  static final long ID = 10;
-  static final String UUID = "uuid ";
-  static final long CREATED_AT = 123456789L;
+  private static final long ID = 10;
+  private static final String UUID = "uuid ";
+  private static final long CREATED_AT = 123456789L;
 
   @Test
   public void build_snapshot() throws Exception {
index 707acbeaffbf0481d997b2ea4f3439b2dcfe3374..9b956794b41b0531338c0bee43fd30c14648d2b8 100644 (file)
@@ -23,6 +23,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.computation.task.projectanalysis.component.MainBranchImpl;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -260,36 +261,55 @@ public class AnalysisMetadataHolderImplTest {
   public void set_branch() {
     AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl();
 
-    underTest.setBranch("origin/master");
+    underTest.setBranch(new MainBranchImpl("master"));
 
-    assertThat(underTest.getBranch()).isEqualTo("origin/master");
+    assertThat(underTest.getBranch().get().getName()).hasValue("master");
   }
 
   @Test
-  public void set_no_branch() {
+  public void getBranch_throws_ISE_when_holder_is_not_initialized() {
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage("Branch has not been set");
+
+    new AnalysisMetadataHolderImpl().getBranch();
+  }
+
+  @Test
+  public void setBranch_throws_ISE_when_called_twice() {
     AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl();
+    underTest.setBranch(new MainBranchImpl("master"));
 
-    underTest.setBranch(null);
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage("Branch has already been set");
+    underTest.setBranch(new MainBranchImpl("master"));
+  }
+
+  @Test
+  public void set_and_get_project() {
+    AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl();
+
+    Project project = new Project("U", "K", "N");
+    underTest.setProject(project);
 
-    assertThat(underTest.getBranch()).isNull();
+    assertThat(underTest.getProject()).isSameAs(project);
   }
 
   @Test
-  public void getBranch_throws_ISE_when_holder_is_not_initialized() {
+  public void getProject_throws_ISE_when_holder_is_not_initialized() {
     expectedException.expect(IllegalStateException.class);
-    expectedException.expectMessage("Branch has not been set");
+    expectedException.expectMessage("Project has not been set");
 
-    new AnalysisMetadataHolderImpl().getBranch();
+    new AnalysisMetadataHolderImpl().getProject();
   }
 
   @Test
-  public void setBranch_throws_ISE_when_called_twice() {
+  public void setProject_throws_ISE_when_called_twice() {
     AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl();
-    underTest.setBranch("origin/master");
+    underTest.setProject(new Project("U", "K", "N"));
 
     expectedException.expect(IllegalStateException.class);
-    expectedException.expectMessage("Branch has already been set");
-    underTest.setBranch("origin/master");
+    expectedException.expectMessage("Project has already been set");
+    underTest.setProject(new Project("U", "K", "N"));
   }
 
   @Test
index 96edd5650ac54a75ab20435b5247d2578da357c9..05977dbbd112f8a0aba04ea2fa6ae5c257685f46 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.computation.task.projectanalysis.analysis;
 
 import java.util.Date;
 import java.util.Map;
+import java.util.Optional;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.junit.rules.ExternalResource;
@@ -46,7 +47,9 @@ public class AnalysisMetadataHolderRule extends ExternalResource implements Muta
 
   private final InitializedProperty<Boolean> crossProjectDuplicationEnabled = new InitializedProperty<>();
 
-  private final InitializedProperty<String> branch = new InitializedProperty<>();
+  private final InitializedProperty<Branch> branch = new InitializedProperty<>();
+
+  private final InitializedProperty<Project> project = new InitializedProperty<>();
 
   private final InitializedProperty<Integer> rootComponentRef = new InitializedProperty<>();
 
@@ -141,15 +144,27 @@ public class AnalysisMetadataHolderRule extends ExternalResource implements Muta
   }
 
   @Override
-  public AnalysisMetadataHolderRule setBranch(@Nullable String branch) {
+  public AnalysisMetadataHolderRule setBranch(@Nullable Branch branch) {
     this.branch.setProperty(branch);
     return this;
   }
 
   @Override
-  public String getBranch() {
+  public Optional<Branch> getBranch() {
     checkState(branch.isInitialized(), "Branch has not been set");
-    return branch.getProperty();
+    return Optional.ofNullable(branch.getProperty());
+  }
+
+  @Override
+  public AnalysisMetadataHolderRule setProject(Project p) {
+    this.project.setProperty(p);
+    return this;
+  }
+
+  @Override
+  public Project getProject() {
+    checkState(project.isInitialized(), "Project has not been set");
+    return project.getProperty();
   }
 
   @Override
index a6c6ca2154801ebd5a92f43f9ac6133b70232007..b425b7ce3840da65532f22f4e0eaf1721bc0f811 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.computation.task.projectanalysis.analysis;
 
 import java.util.Map;
+import java.util.Optional;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.junit.rules.ExternalResource;
@@ -35,8 +36,9 @@ public class MutableAnalysisMetadataHolderRule extends ExternalResource implemen
   }
 
   @Override
-  public MutableAnalysisMetadataHolder setOrganization(Organization organization) {
-    return delegate.setOrganization(organization);
+  public MutableAnalysisMetadataHolderRule setOrganization(Organization organization) {
+    delegate.setOrganization(organization);
+    return this;
   }
 
   @Override
@@ -98,16 +100,27 @@ public class MutableAnalysisMetadataHolderRule extends ExternalResource implemen
   }
 
   @Override
-  public String getBranch() {
+  public Optional<Branch> getBranch() {
     return delegate.getBranch();
   }
 
   @Override
-  public MutableAnalysisMetadataHolderRule setBranch(@Nullable String branch) {
+  public MutableAnalysisMetadataHolderRule setBranch(Branch branch) {
     delegate.setBranch(branch);
     return this;
   }
 
+  @Override
+  public MutableAnalysisMetadataHolderRule setProject(@Nullable Project project) {
+    delegate.setProject(project);
+    return this;
+  }
+
+  @Override
+  public Project getProject() {
+    return delegate.getProject();
+  }
+
   @Override
   public MutableAnalysisMetadataHolderRule setRootComponentRef(int rootComponentRef) {
     delegate.setRootComponentRef(rootComponentRef);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/ProjectTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/ProjectTest.java
new file mode 100644 (file)
index 0000000..4694183
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.analysis;
+
+import org.junit.Test;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT;
+
+public class ProjectTest {
+  @Test
+  public void test_bean() {
+    Project project = new Project("U1", "K1", "N1");
+
+    assertThat(project.getUuid()).isEqualTo("U1");
+    assertThat(project.getKey()).isEqualTo("K1");
+    assertThat(project.getName()).isEqualTo("N1");
+
+    assertThat(project.toString()).isEqualTo("Project{uuid='U1', key='K1', name='N1'}");
+  }
+
+  @Test
+  public void test_equals_and_hashCode() {
+    Project project1 = new Project("U1", "K1", "N1");
+    Project project1bis = new Project("U1", "K1", "N1");
+    Project project2 = new Project("U2", "K2", project1.getName() /* same name */);
+
+    assertThat(project1.equals(project1)).isTrue();
+    assertThat(project1.equals(project1bis)).isTrue();
+    assertThat(project1.equals(project2)).isFalse();
+    assertThat(project1.equals("U1")).isFalse();
+
+    assertThat(project1.hashCode()).isEqualTo(project1.hashCode());
+    assertThat(project1.hashCode()).isEqualTo(project1bis.hashCode());
+  }
+
+  @Test
+  public void test_copyOf() {
+    Component root = ReportComponent.builder(PROJECT, 1).setKey("ROOT").build();
+
+    Project project = Project.copyOf(root);
+    assertThat(project.getUuid()).isEqualTo(root.getUuid()).isNotNull();
+    assertThat(project.getKey()).isEqualTo(root.getKey()).isNotNull();
+    assertThat(project.getName()).isEqualTo(root.getName()).isNotNull();
+  }
+}
index 533c19917e855c204e43442af1b78803f604b1b6..13d795ef5dfa9f41dbd6d19c3a72e7a83967f319 100644 (file)
@@ -107,7 +107,7 @@ public class MeasureComputerContextImplTest {
   public void get_string_settings() throws Exception {
     MapSettings serverSettings = new MapSettings();
     serverSettings.setProperty("prop", "value");
-    when(settingsRepository.getConfiguration(FILE_1)).thenReturn(serverSettings.asConfig());
+    when(settingsRepository.getConfiguration()).thenReturn(serverSettings.asConfig());
 
     MeasureComputerContextImpl underTest = newContext(FILE_1_REF);
     assertThat(underTest.getSettings().getString("prop")).isEqualTo("value");
@@ -118,7 +118,7 @@ public class MeasureComputerContextImplTest {
   public void get_string_array_settings() throws Exception {
     MapSettings serverSettings = new MapSettings();
     serverSettings.setProperty("prop", "1,3.4,8,50");
-    when(settingsRepository.getConfiguration(FILE_1)).thenReturn(serverSettings.asConfig());
+    when(settingsRepository.getConfiguration()).thenReturn(serverSettings.asConfig());
 
     MeasureComputerContextImpl underTest = newContext(FILE_1_REF);
     assertThat(underTest.getSettings().getStringArray("prop")).containsExactly("1", "3.4", "8", "50");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoaderTest.java
new file mode 100644 (file)
index 0000000..f63cbd7
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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 org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.MessageException;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+
+public class BranchLoaderTest {
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Rule
+  public AnalysisMetadataHolderRule metadataHolder = new AnalysisMetadataHolderRule();
+
+  @Test
+  public void throw_ME_if_both_branch_properties_are_set() {
+    ScannerReport.Metadata metadata = ScannerReport.Metadata.newBuilder()
+      .setDeprecatedBranch("foo")
+      .setBranchName("bar")
+      .build();
+
+    expectedException.expect(MessageException.class);
+    expectedException.expectMessage("Properties sonar.branch and sonar.branch.name can't be set together");
+
+    new BranchLoader(metadataHolder).load(metadata);
+  }
+
+  @Test
+  public void regular_analysis_of_project_is_enabled_if_delegate_is_absent() {
+    ScannerReport.Metadata metadata = ScannerReport.Metadata.newBuilder()
+      .build();
+
+    new BranchLoader(metadataHolder).load(metadata);
+
+    assertThat(metadataHolder.getBranch()).isPresent();
+
+    Branch branch = metadataHolder.getBranch().get();
+    assertThat(branch.isMain()).isTrue();
+    assertThat(branch.getName()).isEmpty();
+  }
+
+  @Test
+  public void default_support_of_branches_is_enabled_if_delegate_is_absent() {
+    ScannerReport.Metadata metadata = ScannerReport.Metadata.newBuilder()
+      .setDeprecatedBranch("foo")
+      .build();
+
+    new BranchLoader(metadataHolder).load(metadata);
+
+    assertThat(metadataHolder.getBranch()).isPresent();
+
+    Branch branch = metadataHolder.getBranch().get();
+    assertThat(branch.isMain()).isTrue();
+    assertThat(branch.getName()).hasValue("foo");
+  }
+
+  @Test
+  public void default_support_of_branches_is_enabled_if_delegate_is_present() {
+    ScannerReport.Metadata metadata = ScannerReport.Metadata.newBuilder()
+      .setDeprecatedBranch("foo")
+      .build();
+
+    FakeDelegate delegate = new FakeDelegate();
+    new BranchLoader(metadataHolder, delegate).load(metadata);
+
+    assertThat(metadataHolder.getBranch()).isPresent();
+
+    Branch branch = metadataHolder.getBranch().get();
+    assertThat(branch.isMain()).isTrue();
+    assertThat(branch.getName()).hasValue("foo");
+  }
+
+  private class FakeDelegate implements BranchLoaderDelegate {
+    Branch branch = mock(Branch.class);
+
+    @Override
+    public void load(ScannerReport.Metadata metadata) {
+      metadataHolder.setBranch(branch);
+    }
+  }
+}
index 992f8deaaf184591102750d4ec694e4ca5c32b31..797677c0cbecf9e755c75a119d05f6fee85413a2 100644 (file)
@@ -29,12 +29,7 @@ import static org.sonar.server.computation.task.projectanalysis.component.Compon
 
 public class ComponentFunctionsTest {
 
-  public static final int SOME_INT = new Random().nextInt();
-
-  @Test(expected = NullPointerException.class)
-  public void toReportRef_throws_NPE_if_Component_is_null() {
-    toReportRef().apply(null);
-  }
+  private static final int SOME_INT = new Random().nextInt();
 
   @Test(expected = IllegalStateException.class)
   public void toReportRef_throws_ISE_if_Component_has_no_ReportAttributes() {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentRootBuilderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentRootBuilderTest.java
deleted file mode 100644 (file)
index f4aefdb..0000000
+++ /dev/null
@@ -1,545 +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.base.Optional;
-import com.google.common.base.Supplier;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Function;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.ExternalResource;
-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.base.Preconditions.checkNotNull;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.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.ComponentType.UNSET;
-import static org.sonar.server.computation.task.projectanalysis.component.ComponentRootBuilder.createFileAttributes;
-import static org.sonar.server.computation.task.projectanalysis.component.ComponentRootBuilder.createOtherReportAttributes;
-import static org.sonar.server.computation.task.projectanalysis.component.ComponentRootBuilder.createProjectReportAttributes;
-import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
-
-public class ComponentRootBuilderTest {
-
-  private static final Function<String, String> SIMPLE_UUID_GENERATOR = (componentKey) -> componentKey + "_uuid";
-  private static final String NO_BRANCH = null;
-  private static final String PROJECT_KEY = "this is the key";
-  private static final String MODULE_KEY = "module key";
-  private static final String DIRECTORY_PATH = "directory path";
-  private static final String DIRECTORY_KEY = MODULE_KEY + ":" + DIRECTORY_PATH;
-  private static final String FILE_PATH = "file path";
-  private static final String FILE_KEY = MODULE_KEY + ":" + FILE_PATH;
-  private static final ComponentDto PROJECT_DTO = new ComponentDto().setName("name in db");
-  private static final Supplier<Optional<ComponentDto>> NO_COMPONENT_DTO_FOR_PROJECT = Optional::absent;
-  private static final Function<String, Optional<SnapshotDto>> NO_BASEANALYSIS = (projectUuid) -> Optional.absent();
-  private static final Supplier<Optional<ComponentDto>> COMPONENT_DTO_FOR_PROJECT = () -> Optional.of(PROJECT_DTO);
-  private static final EnumSet<ScannerReport.Component.ComponentType> REPORT_TYPES = EnumSet.of(
-    PROJECT, MODULE, DIRECTORY, FILE);
-  private static final String PROJECT_UUID = "project uuid";
-  private static final String DEFAULT_VERSION = "not provided";
-
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  @Rule
-  public ScannerComponentProvider scannerComponentProvider = new ScannerComponentProvider();
-
-  private ComponentRootBuilder underTest = new ComponentRootBuilder(NO_BRANCH, SIMPLE_UUID_GENERATOR, scannerComponentProvider, NO_COMPONENT_DTO_FOR_PROJECT, NO_BASEANALYSIS);
-
-  @Test
-  public void build_throws_IAE_for_all_types_but_PROJECT_MODULE_DIRECTORY_FILE() {
-    Arrays.stream(ScannerReport.Component.ComponentType.values())
-      .filter((type) -> type != UNRECOGNIZED)
-      .filter((type) -> !REPORT_TYPES.contains(type))
-      .forEach(
-        (type) -> {
-          ScannerReport.Component component = newBuilder().setType(type).build();
-          try {
-            underTest.build(component, "don't care");
-            fail("Should have thrown a IllegalArgumentException");
-          } catch (IllegalArgumentException e) {
-            assertThat(e).hasMessage("Unsupported component type '" + type + "'");
-          }
-        });
-  }
-
-  @Test
-  public void name_of_project_is_name_in_Scanner_Component_when_set() {
-    String expected = "the name";
-    Component root = underTest.build(newBuilder().setType(PROJECT).setName(expected).build(), PROJECT_KEY);
-    assertThat(root.getName()).isEqualTo(expected);
-  }
-
-  @Test
-  public void name_of_project_is_name_in_Scanner_Component_when_set_even_if_there_is_a_ComponentDto() {
-    String expected = "the name";
-    Component root = new ComponentRootBuilder(NO_BRANCH, SIMPLE_UUID_GENERATOR, scannerComponentProvider, COMPONENT_DTO_FOR_PROJECT, NO_BASEANALYSIS)
-      .build(newBuilder().setType(PROJECT).setName(expected).build(), PROJECT_KEY);
-    assertThat(root.getName()).isEqualTo(expected);
-  }
-
-  @Test
-  public void name_of_project_is_specified_key_when_name_is_unset_in_Scanner_Component_and_there_is_no_ComponentDto() {
-    Component root = underTest.build(newBuilder().setType(PROJECT).build(), PROJECT_KEY);
-    assertThat(root.getName()).isEqualTo(PROJECT_KEY);
-  }
-
-  @Test
-  public void name_of_project_is_specified_key_when_name_is_empty_in_Scanner_Component_and_there_is_no_ComponentDto() {
-    Component root = underTest.build(newBuilder().setType(PROJECT).setName("").build(), PROJECT_KEY);
-
-    assertThat(root.getName()).isEqualTo(PROJECT_KEY);
-  }
-
-  @Test
-  public void name_of_project_is_name_of_ComponentDto_when_name_is_unset_in_Scanner_Component_and_there_is_a_ComponentDto() {
-    Component root = new ComponentRootBuilder(NO_BRANCH, SIMPLE_UUID_GENERATOR, scannerComponentProvider, COMPONENT_DTO_FOR_PROJECT, NO_BASEANALYSIS)
-      .build(newBuilder().setType(PROJECT).build(), PROJECT_KEY);
-
-    assertThat(root.getName()).isEqualTo(PROJECT_DTO.name());
-  }
-
-  @Test
-  public void name_of_project_is_name_of_ComponentDto_when_name_is_empty_in_Scanner_Component_and_there_is_a_ComponentDto() {
-    Component root = new ComponentRootBuilder(NO_BRANCH, SIMPLE_UUID_GENERATOR, scannerComponentProvider, COMPONENT_DTO_FOR_PROJECT, NO_BASEANALYSIS)
-      .build(newBuilder().setType(PROJECT).setName("").build(), PROJECT_KEY);
-
-    assertThat(root.getName()).isEqualTo(PROJECT_DTO.name());
-  }
-
-  @Test
-  public void name_of_module_directory_and_file_contains_branch_when_non_empty() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setRef(1).addChildRef(2).build();
-    scannerComponentProvider.add(newBuilder().setRef(2).setType(MODULE).setKey(MODULE_KEY).addChildRef(3));
-    scannerComponentProvider.add(newBuilder().setRef(3).setType(DIRECTORY).setPath(DIRECTORY_PATH).addChildRef(4));
-    scannerComponentProvider.add(newBuilder().setRef(4).setType(FILE).setPath(FILE_PATH).setLines(1));
-
-    String branch = "BRANCH";
-    ComponentRootBuilder builder = new ComponentRootBuilder(branch, SIMPLE_UUID_GENERATOR, scannerComponentProvider, NO_COMPONENT_DTO_FOR_PROJECT, NO_BASEANALYSIS);
-
-    Component root = builder.build(project, PROJECT_KEY);
-    assertThat(root.getKey()).isEqualTo(PROJECT_KEY);
-    assertThat(root.getChildren()).hasSize(1);
-    Component module = root.getChildren().iterator().next();
-    assertThat(module.getKey()).isEqualTo(MODULE_KEY + ":" + branch);
-    assertThat(module.getChildren()).hasSize(1);
-    Component directory = module.getChildren().iterator().next();
-    assertThat(directory.getKey()).isEqualTo(module.getKey() + ":" + DIRECTORY_PATH);
-    assertThat(directory.getChildren()).hasSize(1);
-    Component file = directory.getChildren().iterator().next();
-    assertThat(file.getKey()).isEqualTo(module.getKey() + ":" + FILE_PATH);
-    assertThat(file.getChildren()).isEmpty();
-  }
-
-  @Test
-  public void name_of_module_directory_and_file_is_key_of_Scanner_Component_when_name_is_unset() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setRef(1).addChildRef(2).build();
-    scannerComponentProvider.add(newBuilder().setRef(2).setType(MODULE).setKey(MODULE_KEY).addChildRef(3));
-    scannerComponentProvider.add(newBuilder().setRef(3).setType(DIRECTORY).setPath(DIRECTORY_PATH).addChildRef(4));
-    scannerComponentProvider.add(newBuilder().setRef(4).setType(FILE).setPath(FILE_PATH).setLines(1));
-
-    Component root = underTest.build(project, PROJECT_KEY);
-    assertThat(root.getKey()).isEqualTo(PROJECT_KEY);
-    Component module = root.getChildren().iterator().next();
-    assertThat(module.getName()).isEqualTo(MODULE_KEY);
-    Component directory = module.getChildren().iterator().next();
-    assertThat(directory.getName()).isEqualTo(module.getKey() + ":" + DIRECTORY_PATH);
-    Component file = directory.getChildren().iterator().next();
-    assertThat(file.getName()).isEqualTo(module.getKey() + ":" + FILE_PATH);
-  }
-
-  @Test
-  public void name_of_module_directory_and_file_is_key_of_Scanner_Component_when_name_is_empty() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setRef(1).setName("").addChildRef(2).build();
-    scannerComponentProvider.add(newBuilder().setRef(2).setType(MODULE).setKey(MODULE_KEY).setName("").addChildRef(3));
-    scannerComponentProvider.add(newBuilder().setRef(3).setType(DIRECTORY).setPath(DIRECTORY_PATH).setName("").addChildRef(4));
-    scannerComponentProvider.add(newBuilder().setRef(4).setType(FILE).setPath(FILE_PATH).setName("").setLines(1));
-
-    Component root = underTest.build(project, PROJECT_KEY);
-    assertThat(root.getKey()).isEqualTo(PROJECT_KEY);
-    Component module = root.getChildren().iterator().next();
-    assertThat(module.getName()).isEqualTo(MODULE_KEY);
-    Component directory = module.getChildren().iterator().next();
-    assertThat(directory.getName()).isEqualTo(module.getKey() + ":" + DIRECTORY_PATH);
-    Component file = directory.getChildren().iterator().next();
-    assertThat(file.getName()).isEqualTo(module.getKey() + ":" + FILE_PATH);
-  }
-
-  @Test
-  public void name_of_module_directory_and_files_includes_name_of_closest_module() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setRef(1).addChildRef(11).addChildRef(21).addChildRef(31).build();
-    scannerComponentProvider.add(newBuilder().setRef(11).setType(MODULE).setKey("module 1").addChildRef(12).addChildRef(22).addChildRef(32));
-    scannerComponentProvider.add(newBuilder().setRef(12).setType(MODULE).setKey("module 2").addChildRef(13).addChildRef(23).addChildRef(33));
-    scannerComponentProvider.add(newBuilder().setRef(13).setType(MODULE).setKey("module 3").addChildRef(24).addChildRef(34));
-    scannerComponentProvider.add(newBuilder().setRef(21).setType(DIRECTORY).setPath("directory in project").addChildRef(35));
-    scannerComponentProvider.add(newBuilder().setRef(22).setType(DIRECTORY).setPath("directory in module 1").addChildRef(36));
-    scannerComponentProvider.add(newBuilder().setRef(23).setType(DIRECTORY).setPath("directory in module 2").addChildRef(37));
-    scannerComponentProvider.add(newBuilder().setRef(24).setType(DIRECTORY).setPath("directory in module 3").addChildRef(38));
-    scannerComponentProvider.add(newBuilder().setRef(31).setType(FILE).setPath("file in project").setLines(1));
-    scannerComponentProvider.add(newBuilder().setRef(32).setType(FILE).setPath("file in module 1").setLines(1));
-    scannerComponentProvider.add(newBuilder().setRef(33).setType(FILE).setPath("file in module 2").setLines(1));
-    scannerComponentProvider.add(newBuilder().setRef(34).setType(FILE).setPath("file in module 3").setLines(1));
-    scannerComponentProvider.add(newBuilder().setRef(35).setType(FILE).setPath("file in directory in project").setLines(1));
-    scannerComponentProvider.add(newBuilder().setRef(36).setType(FILE).setPath("file in directory in module 1").setLines(1));
-    scannerComponentProvider.add(newBuilder().setRef(37).setType(FILE).setPath("file in directory in module 2").setLines(1));
-    scannerComponentProvider.add(newBuilder().setRef(38).setType(FILE).setPath("file in directory in module 3").setLines(1));
-
-    Component root = underTest.build(project, PROJECT_KEY);
-    Map<Integer, Component> componentsByRef = indexComponentByRef(root);
-    assertThat(componentsByRef.get(11).getKey()).isEqualTo("module 1");
-    assertThat(componentsByRef.get(12).getKey()).isEqualTo("module 2");
-    assertThat(componentsByRef.get(13).getKey()).isEqualTo("module 3");
-    assertThat(componentsByRef.get(21).getKey()).startsWith(PROJECT_KEY + ":");
-    assertThat(componentsByRef.get(22).getKey()).startsWith("module 1" + ":");
-    assertThat(componentsByRef.get(23).getKey()).startsWith("module 2" + ":");
-    assertThat(componentsByRef.get(24).getKey()).startsWith("module 3" + ":");
-    assertThat(componentsByRef.get(31).getKey()).startsWith(PROJECT_KEY + ":");
-    assertThat(componentsByRef.get(32).getKey()).startsWith("module 1" + ":");
-    assertThat(componentsByRef.get(33).getKey()).startsWith("module 2" + ":");
-    assertThat(componentsByRef.get(34).getKey()).startsWith("module 3" + ":");
-    assertThat(componentsByRef.get(35).getKey()).startsWith(PROJECT_KEY + ":");
-    assertThat(componentsByRef.get(36).getKey()).startsWith("module 1" + ":");
-    assertThat(componentsByRef.get(37).getKey()).startsWith("module 2" + ":");
-    assertThat(componentsByRef.get(38).getKey()).startsWith("module 3" + ":");
-  }
-
-  @Test
-  public void version_of_project_is_set_to_default_value_when_unset_in_Scanner_Component_and_no_base_analysis() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).build();
-
-    ComponentRootBuilder builder = new ComponentRootBuilder(NO_BRANCH, SIMPLE_UUID_GENERATOR, scannerComponentProvider,
-      NO_COMPONENT_DTO_FOR_PROJECT, this::noBaseAnalysisButValidateProjectUuidArgument);
-
-    Component root = builder.build(project, PROJECT_KEY);
-    assertThat(root.getReportAttributes().getVersion()).isEqualTo(DEFAULT_VERSION);
-  }
-
-  @Test
-  public void version_of_project_is_set_to_default_value_when_empty_in_Scanner_Component_and_no_base_analysis() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setVersion("").build();
-
-    ComponentRootBuilder builder = new ComponentRootBuilder(NO_BRANCH, SIMPLE_UUID_GENERATOR, scannerComponentProvider,
-      NO_COMPONENT_DTO_FOR_PROJECT, this::noBaseAnalysisButValidateProjectUuidArgument);
-
-    Component root = builder.build(project, PROJECT_KEY);
-    assertThat(root.getReportAttributes().getVersion()).isEqualTo(DEFAULT_VERSION);
-  }
-
-  private Optional<SnapshotDto> noBaseAnalysisButValidateProjectUuidArgument(String projectUuid) {
-    assertThat(projectUuid).isEqualTo(SIMPLE_UUID_GENERATOR.apply(PROJECT_KEY));
-    return Optional.absent();
-  }
-
-  @Test
-  public void version_of_project_is_set_to_base_analysis_version_when_unset_in_Scanner_Component_and_base_analysis_has_a_version() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).build();
-
-    String expected = "some version";
-    ComponentRootBuilder builder = new ComponentRootBuilder(NO_BRANCH, SIMPLE_UUID_GENERATOR, scannerComponentProvider,
-      NO_COMPONENT_DTO_FOR_PROJECT,
-      (projectUuid) -> {
-        assertThat(projectUuid).isEqualTo(SIMPLE_UUID_GENERATOR.apply(PROJECT_KEY));
-        return Optional.of(new SnapshotDto().setVersion(expected));
-      });
-
-    Component root = builder.build(project, PROJECT_KEY);
-    assertThat(root.getReportAttributes().getVersion()).isEqualTo(expected);
-  }
-
-  @Test
-  public void version_of_project_is_set_to_base_analysis_version_when_empty_in_Scanner_Component_and_base_analysis_has_a_version() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setVersion("").build();
-
-    String expected = "some version";
-    ComponentRootBuilder builder = new ComponentRootBuilder(NO_BRANCH, SIMPLE_UUID_GENERATOR, scannerComponentProvider, NO_COMPONENT_DTO_FOR_PROJECT,
-      (projectUuid) -> {
-        assertThat(projectUuid).isEqualTo(SIMPLE_UUID_GENERATOR.apply(PROJECT_KEY));
-        return Optional.of(new SnapshotDto().setVersion(expected));
-      });
-
-    Component root = builder.build(project, PROJECT_KEY);
-    assertThat(root.getReportAttributes().getVersion()).isEqualTo(expected);
-  }
-
-  @Test
-  public void version_of_project_is_set_to_default_value_when_unset_in_Scanner_Component_and_base_analysis_has_no_version() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).build();
-
-    ComponentRootBuilder builder = new ComponentRootBuilder(NO_BRANCH, SIMPLE_UUID_GENERATOR, scannerComponentProvider,
-      NO_COMPONENT_DTO_FOR_PROJECT,
-      (projectUuid) -> {
-        assertThat(projectUuid).isEqualTo(SIMPLE_UUID_GENERATOR.apply(PROJECT_KEY));
-        return Optional.of(new SnapshotDto());
-      });
-
-    Component root = builder.build(project, PROJECT_KEY);
-    assertThat(root.getReportAttributes().getVersion()).isEqualTo(DEFAULT_VERSION);
-  }
-
-  @Test
-  public void version_of_project_is_set_to_default_value_when_empty_in_Scanner_Component_and_base_analysis_has_no_version() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setVersion("").build();
-
-    ComponentRootBuilder builder = new ComponentRootBuilder(NO_BRANCH, SIMPLE_UUID_GENERATOR, scannerComponentProvider, NO_COMPONENT_DTO_FOR_PROJECT,
-      (projectUuid) -> {
-        assertThat(projectUuid).isEqualTo(SIMPLE_UUID_GENERATOR.apply(PROJECT_KEY));
-        return Optional.of(new SnapshotDto());
-      });
-
-    Component root = builder.build(project, PROJECT_KEY);
-    assertThat(root.getReportAttributes().getVersion()).isEqualTo(DEFAULT_VERSION);
-  }
-
-  @Test
-  public void version_of_project_is_set_to_value_in_Scanner_Component_when_set() {
-    String expected = "some version";
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setVersion(expected).build();
-    ComponentRootBuilder builder = new ComponentRootBuilder(NO_BRANCH, SIMPLE_UUID_GENERATOR, scannerComponentProvider, NO_COMPONENT_DTO_FOR_PROJECT,
-      this::noBaseAnalysisButEnsureIsNotCalled);
-
-    assertThat(builder.build(project, PROJECT_KEY).getReportAttributes().getVersion()).isEqualTo(expected);
-  }
-
-  private Optional<SnapshotDto> noBaseAnalysisButEnsureIsNotCalled(String projectUuid) {
-    fail("baseAnalysis provider should not have been called");
-    return Optional.absent();
-  }
-
-  @Test
-  public void uuid_is_value_from_uuid_supplier_for_project_module_directory_and_file() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setRef(1).addChildRef(2).build();
-    scannerComponentProvider.add(newBuilder().setRef(2).setType(MODULE).setKey(MODULE_KEY).addChildRef(3));
-    scannerComponentProvider.add(newBuilder().setRef(3).setType(DIRECTORY).setPath(DIRECTORY_PATH).addChildRef(4));
-    scannerComponentProvider.add(newBuilder().setRef(4).setType(FILE).setPath(FILE_PATH).setLines(1));
-
-    Component root = underTest.build(project, PROJECT_KEY);
-    Map<Integer, Component> componentByRef = indexComponentByRef(root);
-    assertThat(componentByRef.get(1).getUuid()).isEqualTo(SIMPLE_UUID_GENERATOR.apply(PROJECT_KEY));
-    assertThat(componentByRef.get(2).getUuid()).isEqualTo(SIMPLE_UUID_GENERATOR.apply(MODULE_KEY));
-    assertThat(componentByRef.get(3).getUuid()).isEqualTo(SIMPLE_UUID_GENERATOR.apply(DIRECTORY_KEY));
-    assertThat(componentByRef.get(4).getUuid()).isEqualTo(SIMPLE_UUID_GENERATOR.apply(FILE_KEY));
-
-  }
-
-  @Test
-  public void description_of_project_module_directory_and_file_is_null_when_unset_in_Scanner_Component() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setRef(1).addChildRef(2).build();
-    scannerComponentProvider.add(newBuilder().setRef(2).setType(MODULE).addChildRef(3));
-    scannerComponentProvider.add(newBuilder().setRef(3).setType(DIRECTORY).addChildRef(4));
-    scannerComponentProvider.add(newBuilder().setRef(4).setType(FILE).setLines(1));
-
-    Component root = underTest.build(project, PROJECT_KEY);
-    Map<Integer, Component> componentByRef = indexComponentByRef(root);
-    assertThat(componentByRef.get(1).getDescription()).isNull();
-    assertThat(componentByRef.get(2).getDescription()).isNull();
-    assertThat(componentByRef.get(3).getDescription()).isNull();
-    assertThat(componentByRef.get(4).getDescription()).isNull();
-  }
-
-  @Test
-  public void description_of_project_module_directory_and_file_is_null_when_empty_in_Scanner_Component() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setRef(1).setDescription("").addChildRef(2).build();
-    scannerComponentProvider.add(newBuilder().setRef(2).setType(MODULE).setDescription("").addChildRef(3));
-    scannerComponentProvider.add(newBuilder().setRef(3).setType(DIRECTORY).setDescription("").addChildRef(4));
-    scannerComponentProvider.add(newBuilder().setRef(4).setType(FILE).setLines(1).setDescription(""));
-
-    Component root = underTest.build(project, PROJECT_KEY);
-    Map<Integer, Component> componentByRef = indexComponentByRef(root);
-    assertThat(componentByRef.get(1).getDescription()).isNull();
-    assertThat(componentByRef.get(2).getDescription()).isNull();
-    assertThat(componentByRef.get(3).getDescription()).isNull();
-    assertThat(componentByRef.get(4).getDescription()).isNull();
-  }
-
-  @Test
-  public void description_of_project_module_directory_and_file_is_description_of_Scanner_Component_when_set() {
-    ScannerReport.Component project = newBuilder().setType(PROJECT).setRef(1).setDescription("desc of project").addChildRef(2).build();
-    scannerComponentProvider.add(newBuilder().setRef(2).setType(MODULE).setDescription("desc of module").addChildRef(3));
-    scannerComponentProvider.add(newBuilder().setRef(3).setType(DIRECTORY).setDescription("desc of directory").addChildRef(4));
-    scannerComponentProvider.add(newBuilder().setRef(4).setType(FILE).setLines(1).setDescription("desc of file"));
-
-    Component root = underTest.build(project, PROJECT_KEY);
-    Map<Integer, Component> componentByRef = indexComponentByRef(root);
-    assertThat(componentByRef.get(1).getDescription()).isEqualTo("desc of project");
-    assertThat(componentByRef.get(2).getDescription()).isEqualTo("desc of module");
-    assertThat(componentByRef.get(3).getDescription()).isEqualTo("desc of directory");
-    assertThat(componentByRef.get(4).getDescription()).isEqualTo("desc of file");
-  }
-
-  @Test
-  public void all_types_but_UNSET_and_UNRECOGNIZED_are_converted() {
-    Arrays.stream(ScannerReport.Component.ComponentType.values())
-      .filter((type) -> type != UNRECOGNIZED)
-      .filter((type) -> type != UNSET)
-      .forEach((type) -> assertThat(ComponentRootBuilder.convertType(type)).isEqualTo(Component.Type.valueOf(type.name())));
-  }
-
-  @Test
-  public void createOtherReportAttributes_takes_ref_version_and_path_from_Scanner_Component() {
-    int ref = 123;
-    String version = "1.0";
-    String path = "some path";
-
-    ReportAttributes reportAttributes = createOtherReportAttributes(newBuilder()
-      .setRef(ref)
-      .setVersion(version)
-      .setPath(path)
-      .build());
-    assertThat(reportAttributes.getRef()).isEqualTo(ref);
-    assertThat(reportAttributes.getPath()).isEqualTo(path);
-    assertThat(reportAttributes.getVersion()).isEqualTo(version);
-  }
-
-  @Test
-  public void createOtherReportAttributes_sets_null_version_when_unset_in_Scanner_Component() {
-    ReportAttributes reportAttributes = createOtherReportAttributes(newBuilder().build());
-    assertThat(reportAttributes.getVersion()).isNull();
-  }
-
-  @Test
-  public void createOtherReportAttributes_sets_null_version_when_empty_in_Scanner_Component() {
-    ReportAttributes reportAttributes = createOtherReportAttributes(newBuilder().setVersion("").build());
-    assertThat(reportAttributes.getVersion()).isNull();
-  }
-
-  @Test
-  public void createOtherReportAttributes_sets_null_path_when_unset_in_Scanner_Component() {
-    ReportAttributes reportAttributes = createOtherReportAttributes(newBuilder().build());
-    assertThat(reportAttributes.getPath()).isNull();
-  }
-
-  @Test
-  public void createOtherReportAttributes_sets_null_path_when_empty_in_Scanner_Component() {
-    ReportAttributes reportAttributes = createOtherReportAttributes(newBuilder().setPath("").build());
-    assertThat(reportAttributes.getPath()).isNull();
-  }
-
-  @Test
-  public void createProjectReportAttributes_sets_null_path_when_unset_in_Scanner_Component() {
-    ReportAttributes reportAttributes = createProjectReportAttributes(newBuilder().build(), PROJECT_UUID, NO_BASEANALYSIS);
-    assertThat(reportAttributes.getPath()).isNull();
-  }
-
-  @Test
-  public void createProjectReportAttributes_sets_null_path_when_empty_in_Scanner_Component() {
-    ReportAttributes reportAttributes = createProjectReportAttributes(newBuilder().setPath("").build(), PROJECT_UUID, NO_BASEANALYSIS);
-    assertThat(reportAttributes.getPath()).isNull();
-  }
-
-  @Test
-  public void createFileAttributes_returns_null_when_type_is_not_FILE() {
-    Arrays.stream(ScannerReport.Component.ComponentType.values())
-      .filter((type) -> type != UNRECOGNIZED)
-      .filter((type) -> type != FILE)
-      .map(
-        (type) -> newBuilder().setType(type).build())
-      .forEach(
-        (component) -> assertThat(createFileAttributes(component)).isNull());
-  }
-
-  @Test
-  public void createFileAttributes_sets_language_to_null_when_unset_in_Scanner_Component() {
-    assertThat(createFileAttributes(newBuilder().setType(FILE).setLines(1).build()).getLanguageKey()).isNull();
-  }
-
-  @Test
-  public void createFileAttributes_sets_language_to_null_when_empty_in_Scanner_Component() {
-    assertThat(createFileAttributes(newBuilder().setType(FILE).setLanguage("").setLines(1).build()).getLanguageKey()).isNull();
-  }
-
-  @Test
-  public void createFileAttributes_sets_unitTest_from_Scanner_Component() {
-    assertThat(createFileAttributes(newBuilder().setType(FILE).setLines(1).build()).isUnitTest()).isFalse();
-    assertThat(createFileAttributes(newBuilder().setType(FILE).setIsTest(true).setLines(1).build()).isUnitTest()).isTrue();
-  }
-
-  @Test
-  public void createFileAttributes_sets_lines_in_Scanner_Component() {
-    assertThat(createFileAttributes(newBuilder().setType(FILE).setLines(10).build()).getLines()).isEqualTo(10);
-  }
-
-  @Test
-  public void fail_with_IAE_when_createFileAttributes_lines_is_not_set() throws Exception {
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("File 'src/main/java/Main.java' has no line");
-    createFileAttributes(newBuilder().setType(FILE).setPath("src/main/java/Main.java").build());
-  }
-
-  @Test
-  public void fail_with_IAE_when_createFileAttributes_sets_lines_to_0() throws Exception {
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("File 'src/main/java/Main.java' has no line");
-    createFileAttributes(newBuilder().setType(FILE).setPath("src/main/java/Main.java").setLines(0).build());
-  }
-
-  @Test
-  public void fail_with_IAE_when_createFileAttributes_sets_lines_to_less_than_0() throws Exception {
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("File 'src/main/java/Main.java' has no line");
-    createFileAttributes(newBuilder().setType(FILE).setPath("src/main/java/Main.java").setLines(-10).build());
-  }
-
-  private static class ScannerComponentProvider extends ExternalResource implements Function<Integer, ScannerReport.Component> {
-    private final Map<Integer, ScannerReport.Component> components = new HashMap<>();
-
-    @Override
-    protected void before() throws Throwable {
-      components.clear();
-    }
-
-    @Override
-    public ScannerReport.Component apply(Integer componentRef) {
-      return checkNotNull(components.get(componentRef), "No Component for componentRef %s", componentRef);
-    }
-
-    public ScannerReport.Component add(ScannerReport.Component.Builder builder) {
-      ScannerReport.Component component = builder.build();
-      ScannerReport.Component existing = components.put(component.getRef(), component);
-      checkArgument(existing == null, "Component %s already set for ref %s", existing, component.getRef());
-      return component;
-    }
-  }
-
-  private static Map<Integer, Component> indexComponentByRef(Component root) {
-    Map<Integer, Component> componentsByRef = new HashMap<>();
-    new DepthTraversalTypeAwareCrawler(
-      new TypeAwareVisitorAdapter(CrawlerDepthLimit.FILE, PRE_ORDER) {
-        @Override
-        public void visitAny(Component any) {
-          componentsByRef.put(any.getReportAttributes().getRef(), any);
-        }
-      }).visit(root);
-    return componentsByRef;
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilderTest.java
new file mode 100644 (file)
index 0000000..6697a44
--- /dev/null
@@ -0,0 +1,735 @@
+/*
+ * 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 java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.ExternalResource;
+import org.sonar.core.component.ComponentKeys;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.scanner.protocol.output.ScannerReport;
+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.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 Function<String, String> UUID_SUPPLIER = (componentKey) -> componentKey + "_uuid";
+  private static final EnumSet<ScannerReport.Component.ComponentType> REPORT_TYPES = EnumSet.of(PROJECT, MODULE, DIRECTORY, FILE);
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Rule
+  public ScannerComponentProvider scannerComponentProvider = new ScannerComponentProvider();
+
+  private Project projectInDb = new Project(UUID_SUPPLIER.apply("K1"), "K1", "theProjectName");
+
+  @Test
+  public void build_throws_IAE_for_all_types_but_PROJECT_MODULE_DIRECTORY_FILE() {
+    Arrays.stream(ScannerReport.Component.ComponentType.values())
+      .filter((type) -> type != UNRECOGNIZED)
+      .filter((type) -> !REPORT_TYPES.contains(type))
+      .forEach(
+        (type) -> {
+          ScannerReport.Component component = newBuilder().setType(type).build();
+          try {
+            call(component);
+            fail("Should have thrown a IllegalArgumentException");
+          } catch (IllegalArgumentException e) {
+            assertThat(e).hasMessage("Unsupported component type '" + type + "'");
+          }
+        });
+  }
+
+  @Test
+  public void by_default_project_is_loaded_from_report() {
+    String nameInReport = "the name";
+    String descriptionInReport = "the desc";
+    Component root = call(newBuilder()
+      .setType(PROJECT)
+      .setKey(projectInDb.getKey())
+      .setRef(42)
+      .setName(nameInReport)
+      .setDescription(descriptionInReport)
+      .setVersion("6.5")
+      .build());
+
+    assertThat(root.getUuid()).isEqualTo("generated_K1_uuid");
+    assertThat(root.getKey()).isEqualTo("generated_K1");
+    assertThat(root.getType()).isEqualTo(Component.Type.PROJECT);
+    assertThat(root.getName()).isEqualTo(nameInReport);
+    assertThat(root.getDescription()).isEqualTo(descriptionInReport);
+    assertThat(root.getReportAttributes().getRef()).isEqualTo(42);
+    assertThat(root.getReportAttributes().getPath()).isNull();
+    assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
+    assertThatFileAttributesAreNotSet(root);
+  }
+
+  @Test
+  public void project_name_is_loaded_from_db_if_absent_from_report() {
+    Component root = call(newBuilder()
+      .setType(PROJECT)
+      .build());
+
+    assertThat(root.getName()).isEqualTo(projectInDb.getName());
+  }
+
+  @Test
+  public void project_version_is_loaded_from_db_if_absent_from_report() {
+    SnapshotDto baseAnalysis = new SnapshotDto().setVersion("6.5");
+    Component root = call(newBuilder()
+      .setType(PROJECT)
+      .build(), baseAnalysis);
+
+    assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
+  }
+
+  @Test
+  public void project_version_is_loaded_from_db_if_empty_report() {
+    SnapshotDto baseAnalysis = new SnapshotDto().setVersion("6.5");
+    Component root = call(newBuilder()
+      .setType(PROJECT)
+      .setVersion("")
+      .build(), baseAnalysis);
+
+    assertThat(root.getReportAttributes().getVersion()).isEqualTo("6.5");
+  }
+
+  @Test
+  public void project_version_is_hardcoded_if_absent_from_report_and_db() {
+    Component root = call(newBuilder()
+      .setType(PROJECT)
+      .build());
+
+    assertThat(root.getReportAttributes().getVersion()).isEqualTo("not provided");
+  }
+
+  @Test
+  public void project_description_is_null_if_absent_from_report() {
+    Component root = call(newBuilder()
+      .setType(PROJECT)
+      .build());
+
+    assertThat(root.getDescription()).isNull();
+  }
+
+  @Test
+  public void keys_of_module_directory_and_file_are_generated() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setKey(projectInDb.getKey())
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(MODULE)
+      .setKey("M")
+      .addChildRef(3));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(3)
+      .setType(DIRECTORY)
+      .setPath("src/js")
+      .addChildRef(4));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(4)
+      .setType(FILE)
+      .setPath("src/js/Foo.js")
+      .setLines(1));
+
+    Component root = call(project);
+    assertThat(root.getKey()).isEqualTo("generated_" + projectInDb.getKey());
+    assertThat(root.getChildren()).hasSize(1);
+
+    Component module = root.getChildren().iterator().next();
+    assertThat(module.getKey()).isEqualTo("generated_M");
+    assertThat(module.getChildren()).hasSize(1);
+
+    Component directory = module.getChildren().iterator().next();
+    assertThat(directory.getKey()).isEqualTo("generated_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.getChildren()).isEmpty();
+  }
+
+  @Test
+  public void names_of_module_directory_and_file_are_keys_if_names_are_absent_from_report() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setKey(projectInDb.getKey())
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(MODULE)
+      .setKey("M")
+      .addChildRef(3));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(3)
+      .setType(DIRECTORY)
+      .setPath("src/js")
+      .addChildRef(4));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(4)
+      .setType(FILE)
+      .setPath("src/js/Foo.js")
+      .setLines(1));
+
+    Component root = call(project);
+
+    Component module = root.getChildren().iterator().next();
+    assertThat(module.getName()).isEqualTo("generated_M");
+
+    Component directory = module.getChildren().iterator().next();
+    assertThat(directory.getName()).isEqualTo("generated_M:src/js");
+
+    Component file = directory.getChildren().iterator().next();
+    assertThat(file.getName()).isEqualTo("generated_M:src/js/Foo.js");
+  }
+
+  @Test
+  public void names_of_module_directory_and_file_are_keys_if_names_are_empty_in_report() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setKey(projectInDb.getKey())
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(MODULE)
+      .setKey("M")
+      .setName("")
+      .addChildRef(3));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(3)
+      .setType(DIRECTORY)
+      .setPath("src/js")
+      .setName("")
+      .addChildRef(4));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(4)
+      .setType(FILE)
+      .setPath("src/js/Foo.js")
+      .setName("")
+      .setLines(1));
+
+    Component root = call(project);
+
+    Component module = root.getChildren().iterator().next();
+    assertThat(module.getName()).isEqualTo("generated_M");
+
+    Component directory = module.getChildren().iterator().next();
+    assertThat(directory.getName()).isEqualTo("generated_M:src/js");
+
+    Component file = directory.getChildren().iterator().next();
+    assertThat(file.getName()).isEqualTo("generated_M:src/js/Foo.js");
+  }
+
+  private void assertThatFileAttributesAreNotSet(Component root) {
+    try {
+      root.getFileAttributes();
+      fail();
+    } catch (IllegalStateException e) {
+      assertThat(e).hasMessage("Only component of type FILE have a FileAttributes object");
+    }
+  }
+
+  @Test
+  public void name_of_module_directory_and_files_includes_name_of_closest_module() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setKey("project 1")
+      .setRef(1)
+      .addChildRef(11).addChildRef(21).addChildRef(31).build();
+    scannerComponentProvider.add(newBuilder().setRef(11).setType(MODULE).setKey("module 1").addChildRef(12).addChildRef(22).addChildRef(32));
+    scannerComponentProvider.add(newBuilder().setRef(12).setType(MODULE).setKey("module 2").addChildRef(13).addChildRef(23).addChildRef(33));
+    scannerComponentProvider.add(newBuilder().setRef(13).setType(MODULE).setKey("module 3").addChildRef(24).addChildRef(34));
+    scannerComponentProvider.add(newBuilder().setRef(21).setType(DIRECTORY).setPath("directory in project").addChildRef(35));
+    scannerComponentProvider.add(newBuilder().setRef(22).setType(DIRECTORY).setPath("directory in module 1").addChildRef(36));
+    scannerComponentProvider.add(newBuilder().setRef(23).setType(DIRECTORY).setPath("directory in module 2").addChildRef(37));
+    scannerComponentProvider.add(newBuilder().setRef(24).setType(DIRECTORY).setPath("directory in module 3").addChildRef(38));
+    scannerComponentProvider.add(newBuilder().setRef(31).setType(FILE).setPath("file in project").setLines(1));
+    scannerComponentProvider.add(newBuilder().setRef(32).setType(FILE).setPath("file in module 1").setLines(1));
+    scannerComponentProvider.add(newBuilder().setRef(33).setType(FILE).setPath("file in module 2").setLines(1));
+    scannerComponentProvider.add(newBuilder().setRef(34).setType(FILE).setPath("file in module 3").setLines(1));
+    scannerComponentProvider.add(newBuilder().setRef(35).setType(FILE).setPath("file in directory in project").setLines(1));
+    scannerComponentProvider.add(newBuilder().setRef(36).setType(FILE).setPath("file in directory in module 1").setLines(1));
+    scannerComponentProvider.add(newBuilder().setRef(37).setType(FILE).setPath("file in directory in module 2").setLines(1));
+    scannerComponentProvider.add(newBuilder().setRef(38).setType(FILE).setPath("file in directory in module 3").setLines(1));
+
+    Component root = call(project);
+    Map<Integer, Component> componentsByRef = indexComponentByRef(root);
+    assertThat(componentsByRef.get(11).getKey()).isEqualTo("generated_module 1");
+    assertThat(componentsByRef.get(12).getKey()).isEqualTo("generated_module 2");
+    assertThat(componentsByRef.get(13).getKey()).isEqualTo("generated_module 3");
+    assertThat(componentsByRef.get(21).getKey()).startsWith("generated_project 1:");
+    assertThat(componentsByRef.get(22).getKey()).startsWith("generated_module 1:");
+    assertThat(componentsByRef.get(23).getKey()).startsWith("generated_module 2:");
+    assertThat(componentsByRef.get(24).getKey()).startsWith("generated_module 3:");
+    assertThat(componentsByRef.get(31).getKey()).startsWith("generated_project 1:");
+    assertThat(componentsByRef.get(32).getKey()).startsWith("generated_module 1:");
+    assertThat(componentsByRef.get(33).getKey()).startsWith("generated_module 2:");
+    assertThat(componentsByRef.get(34).getKey()).startsWith("generated_module 3:");
+    assertThat(componentsByRef.get(35).getKey()).startsWith("generated_project 1:");
+    assertThat(componentsByRef.get(36).getKey()).startsWith("generated_module 1:");
+    assertThat(componentsByRef.get(37).getKey()).startsWith("generated_module 2:");
+    assertThat(componentsByRef.get(38).getKey()).startsWith("generated_module 3:");
+  }
+
+  @Test
+  public void uuids_are_provided_by_supplier() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setKey("c1")
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(MODULE)
+      .setKey("c2")
+      .addChildRef(3));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(3)
+      .setType(DIRECTORY)
+      .setPath("src/js")
+      .addChildRef(4));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(4)
+      .setType(FILE)
+      .setPath("src/js/Foo.js")
+      .setLines(1));
+
+    Component root = call(project);
+    assertThat(root.getUuid()).isEqualTo("generated_c1_uuid");
+
+    Component module = root.getChildren().iterator().next();
+    assertThat(module.getUuid()).isEqualTo("generated_c2_uuid");
+
+    Component directory = module.getChildren().iterator().next();
+    assertThat(directory.getUuid()).isEqualTo("generated_c2:src/js_uuid");
+
+    Component file = directory.getChildren().iterator().next();
+    assertThat(file.getUuid()).isEqualTo("generated_c2:src/js/Foo.js_uuid");
+  }
+
+  @Test
+  public void descriptions_of_module_directory_and_file_are_null_if_absent_from_report() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(MODULE)
+      .addChildRef(3));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(3)
+      .setType(DIRECTORY)
+      .setPath("src/js")
+      .addChildRef(4));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(4)
+      .setType(FILE)
+      .setPath("src/js/Foo.js")
+      .setLines(1));
+
+    Component root = call(project);
+
+    Component module = root.getChildren().iterator().next();
+    assertThat(module.getDescription()).isNull();
+
+    Component directory = module.getChildren().iterator().next();
+    assertThat(directory.getDescription()).isNull();
+
+    Component file = directory.getChildren().iterator().next();
+    assertThat(file.getDescription()).isNull();
+  }
+
+  @Test
+  public void descriptions_of_module_directory_and_file_are_null_if_empty_in_report() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .setDescription("")
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(MODULE)
+      .setDescription("")
+      .addChildRef(3));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(3)
+      .setType(DIRECTORY)
+      .setDescription("")
+      .setPath("src/js")
+      .addChildRef(4));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(4)
+      .setType(FILE)
+      .setDescription("")
+      .setPath("src/js/Foo.js")
+      .setLines(1));
+
+    Component root = call(project);
+
+    Component module = root.getChildren().iterator().next();
+    assertThat(module.getDescription()).isNull();
+
+    Component directory = module.getChildren().iterator().next();
+    assertThat(directory.getDescription()).isNull();
+
+    Component file = directory.getChildren().iterator().next();
+    assertThat(file.getDescription()).isNull();
+  }
+
+  @Test
+  public void descriptions_of_module_directory_and_file_are_set_from_report_if_present() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(MODULE)
+      .setDescription("b")
+      .addChildRef(3));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(3)
+      .setType(DIRECTORY)
+      .setDescription("c")
+      .setPath("src/js")
+      .addChildRef(4));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(4)
+      .setType(FILE)
+      .setDescription("d")
+      .setPath("src/js/Foo.js")
+      .setLines(1));
+
+    Component root = call(project);
+
+    Component module = root.getChildren().iterator().next();
+    assertThat(module.getDescription()).isEqualTo("b");
+
+    Component directory = module.getChildren().iterator().next();
+    assertThat(directory.getDescription()).isEqualTo("c");
+
+    Component file = directory.getChildren().iterator().next();
+    assertThat(file.getDescription()).isEqualTo("d");
+  }
+
+  @Test
+  public void versions_of_module_directory_and_file_are_set_from_report_if_present() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(MODULE)
+      .setVersion("v1")
+      .addChildRef(3));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(3)
+      .setType(DIRECTORY)
+      .setVersion("v2")
+      .setPath("src/js")
+      .addChildRef(4));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(4)
+      .setType(FILE)
+      .setVersion("v3")
+      .setPath("src/js/Foo.js")
+      .setLines(1));
+
+    Component root = call(project);
+
+    Component module = root.getChildren().iterator().next();
+    assertThat(module.getReportAttributes().getVersion()).isEqualTo("v1");
+
+    Component directory = module.getChildren().iterator().next();
+    assertThat(directory.getReportAttributes().getVersion()).isEqualTo("v2");
+
+    Component file = directory.getChildren().iterator().next();
+    assertThat(file.getReportAttributes().getVersion()).isEqualTo("v3");
+  }
+
+  @Test
+  public void versions_of_module_directory_and_file_are_null_if_absent_from_report() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(MODULE)
+      .addChildRef(3));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(3)
+      .setType(DIRECTORY)
+      .setPath("src/js")
+      .addChildRef(4));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(4)
+      .setType(FILE)
+      .setPath("src/js/Foo.js")
+      .setLines(1));
+
+    Component root = call(project);
+
+    Component module = root.getChildren().iterator().next();
+    assertThat(module.getReportAttributes().getVersion()).isNull();
+
+    Component directory = module.getChildren().iterator().next();
+    assertThat(directory.getReportAttributes().getVersion()).isNull();
+
+    Component file = directory.getChildren().iterator().next();
+    assertThat(file.getReportAttributes().getVersion()).isNull();
+  }
+
+  @Test
+  public void versions_of_module_directory_and_file_are_null_if_empty_in_report() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(MODULE)
+      .setVersion("")
+      .addChildRef(3));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(3)
+      .setType(DIRECTORY)
+      .setVersion("")
+      .setPath("src/js")
+      .addChildRef(4));
+    scannerComponentProvider.add(newBuilder()
+      .setRef(4)
+      .setType(FILE)
+      .setVersion("")
+      .setPath("src/js/Foo.js")
+      .setLines(1));
+
+    Component root = call(project);
+
+    Component module = root.getChildren().iterator().next();
+    assertThat(module.getReportAttributes().getVersion()).isNull();
+
+    Component directory = module.getChildren().iterator().next();
+    assertThat(directory.getReportAttributes().getVersion()).isNull();
+
+    Component file = directory.getChildren().iterator().next();
+    assertThat(file.getReportAttributes().getVersion()).isNull();
+  }
+
+  @Test
+  public void only_nb_of_lines_is_mandatory_on_file_attributes() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(FILE)
+      .setPath("src/js/Foo.js")
+      .setLines(1));
+
+    Component root = call(project);
+    Component file = root.getChildren().iterator().next();
+    assertThat(file.getFileAttributes().getLines()).isEqualTo(1);
+    assertThat(file.getFileAttributes().getLanguageKey()).isNull();
+    assertThat(file.getFileAttributes().isUnitTest()).isFalse();
+  }
+
+  @Test
+  public void language_file_attributes_is_null_if_empty_in_report() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(FILE)
+      .setPath("src/js/Foo.js")
+      .setLines(1)
+      .setLanguage(""));
+
+    Component root = call(project);
+    Component file = root.getChildren().iterator().next();
+    assertThat(file.getFileAttributes().getLanguageKey()).isNull();
+  }
+
+  @Test
+  public void file_attributes_are_fully_loaded_from_report() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(FILE)
+      .setPath("src/js/Foo.js")
+      .setLines(1)
+      .setLanguage("js")
+      .setIsTest(true));
+
+    Component root = call(project);
+    Component file = root.getChildren().iterator().next();
+    assertThat(file.getFileAttributes().getLines()).isEqualTo(1);
+    assertThat(file.getFileAttributes().getLanguageKey()).isEqualTo("js");
+    assertThat(file.getFileAttributes().isUnitTest()).isTrue();
+  }
+
+  @Test
+  public void throw_IAE_if_lines_is_absent_from_report() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(FILE)
+      .setPath("src/js/Foo.js"));
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("File 'src/js/Foo.js' has no line");
+
+    call(project);
+  }
+
+  @Test
+  public void throw_IAE_if_lines_is_zero_in_report() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(FILE)
+      .setPath("src/js/Foo.js")
+      .setLines(0));
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("File 'src/js/Foo.js' has no line");
+
+    call(project);
+  }
+
+  @Test
+  public void throw_IAE_if_lines_is_negative_in_report() {
+    ScannerReport.Component project = newBuilder()
+      .setType(PROJECT)
+      .setRef(1)
+      .addChildRef(2)
+      .build();
+    scannerComponentProvider.add(newBuilder()
+      .setRef(2)
+      .setType(FILE)
+      .setPath("src/js/Foo.js")
+      .setLines(-10));
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("File 'src/js/Foo.js' has no line");
+
+    call(project);
+  }
+
+  private static class ScannerComponentProvider extends ExternalResource implements Function<Integer, ScannerReport.Component> {
+    private final Map<Integer, ScannerReport.Component> components = new HashMap<>();
+
+    @Override
+    protected void before() throws Throwable {
+      components.clear();
+    }
+
+    @Override
+    public ScannerReport.Component apply(Integer componentRef) {
+      return Objects.requireNonNull(components.get(componentRef), "No Component for componentRef " + componentRef);
+    }
+
+    public ScannerReport.Component add(ScannerReport.Component.Builder builder) {
+      ScannerReport.Component component = builder.build();
+      ScannerReport.Component existing = components.put(component.getRef(), component);
+      checkArgument(existing == null, "Component %s already set for ref %s", existing, component.getRef());
+      return component;
+    }
+  }
+
+  private Component call(ScannerReport.Component project) {
+    return newUnderTest(null).buildProject(project);
+  }
+
+  private Component call(ScannerReport.Component project, @Nullable SnapshotDto baseAnalysis) {
+    return newUnderTest(baseAnalysis).buildProject(project);
+  }
+
+  private ComponentTreeBuilder newUnderTest(@Nullable SnapshotDto baseAnalysis) {
+    return new ComponentTreeBuilder(KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, projectInDb, baseAnalysis);
+  }
+
+  private static Map<Integer, Component> indexComponentByRef(Component root) {
+    Map<Integer, Component> componentsByRef = new HashMap<>();
+    new DepthTraversalTypeAwareCrawler(
+      new TypeAwareVisitorAdapter(CrawlerDepthLimit.FILE, PRE_ORDER) {
+        @Override
+        public void visitAny(Component any) {
+          componentsByRef.put(any.getReportAttributes().getRef(), any);
+        }
+      }).visit(root);
+    return componentsByRef;
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentUuidFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentUuidFactoryTest.java
new file mode 100644 (file)
index 0000000..22c8798
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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 org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ComponentUuidFactoryTest {
+
+  @Rule
+  public DbTester db = DbTester.create(System2.INSTANCE);
+
+  @Test
+  public void load_uuids_from_existing_components_in_db() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project));
+
+    ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey());
+    assertThat(underTest.getOrCreateForKey(project.getDbKey())).isEqualTo(project.uuid());
+    assertThat(underTest.getOrCreateForKey(module.getDbKey())).isEqualTo(module.uuid());
+  }
+
+  @Test
+  public void generate_uuid_if_it_does_not_exist_in_db() {
+    ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), "theProjectKey");
+
+    String generatedKey = underTest.getOrCreateForKey("foo");
+    assertThat(generatedKey).isNotEmpty();
+
+    // uuid is kept in memory for further calls with same key
+    assertThat(underTest.getOrCreateForKey("foo")).isEqualTo(generatedKey);
+  }
+
+}
index 383de8d5030d3b0baabd980c2278d8193b609de1..26fe4fddfdc8bad7aba12a4b663a6190b09ea242 100644 (file)
  */
 package org.sonar.server.computation.task.projectanalysis.component;
 
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.config.internal.MapSettings;
 import org.sonar.api.utils.System2;
+import org.sonar.ce.queue.CeTask;
 import org.sonar.ce.settings.ProjectConfigurationFactory;
 import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.ce.CeTaskTypes;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.property.PropertyDto;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -40,47 +38,36 @@ import static org.sonar.server.computation.task.projectanalysis.component.Compon
 public class ConfigurationRepositoryTest {
 
   private static final Component ROOT = ReportComponent.builder(PROJECT, 1).setKey("ROOT").build();
+  private static final CeTask TASK = new CeTask.Builder()
+    .setOrganizationUuid("foo")
+    .setUuid("bar")
+    .setType(CeTaskTypes.REPORT)
+    .setComponentUuid(ROOT.getUuid())
+    .setComponentKey(ROOT.getKey())
+    .build();
 
   @Rule
-  public final DbTester dbTester = DbTester.create(System2.INSTANCE);
+  public final DbTester db = DbTester.create(System2.INSTANCE);
 
-  DbClient dbClient = dbTester.getDbClient();
-
-  DbSession session;
-
-  MapSettings globalSettings;
-
-  ConfigurationRepository underTest;
-
-  @Before
-  public void createDao() {
-    globalSettings = new MapSettings();
-    session = dbClient.openSession(false);
-    underTest = new ConfigurationRepositoryImpl(new ProjectConfigurationFactory(globalSettings, dbClient));
-  }
-
-  @After
-  public void tearDown() {
-    session.close();
-  }
+  private DbClient dbClient = db.getDbClient();
+  private MapSettings globalSettings = new MapSettings();
+  private ConfigurationRepository underTest = new ConfigurationRepositoryImpl(TASK, new ProjectConfigurationFactory(globalSettings, dbClient));
 
   @Test
   public void get_project_settings_from_global_settings() {
     globalSettings.setProperty("key", "value");
 
-    Configuration config = underTest.getConfiguration(ROOT);
+    Configuration config = underTest.getConfiguration();
 
     assertThat(config.get("key")).hasValue("value");
   }
 
   @Test
   public void get_project_settings_from_db() {
-    ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert()).setDbKey(ROOT.getKey());
-    dbClient.componentDao().insert(session, project);
-    dbClient.propertiesDao().saveProperty(session, new PropertyDto().setResourceId(project.getId()).setKey("key").setValue("value"));
-    session.commit();
+    ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey(ROOT.getKey()));
+    insertProjectProperty(project, "key", "value");
 
-    Configuration config = underTest.getConfiguration(ROOT);
+    Configuration config = underTest.getConfiguration();
 
     assertThat(config.get("key")).hasValue("value");
   }
@@ -89,10 +76,38 @@ public class ConfigurationRepositoryTest {
   public void call_twice_get_project_settings() {
     globalSettings.setProperty("key", "value");
 
-    Configuration config = underTest.getConfiguration(ROOT);
+    Configuration config = underTest.getConfiguration();
     assertThat(config.get("key")).hasValue("value");
 
-    config = underTest.getConfiguration(ROOT);
+    config = underTest.getConfiguration();
     assertThat(config.get("key")).hasValue("value");
   }
+
+  @Test
+  public void project_settings_override_global_settings() {
+    globalSettings.setProperty("key", "value1");
+    ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey(ROOT.getKey()));
+    insertProjectProperty(project, "key", "value2");
+
+    Configuration config = underTest.getConfiguration();
+    assertThat(config.get("key")).hasValue("value2");
+  }
+
+  @Test
+  public void project_settings_are_cached_to_avoid_db_access() {
+    ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey(ROOT.getKey()));
+    insertProjectProperty(project, "key", "value");
+
+    Configuration config = underTest.getConfiguration();
+    assertThat(config.get("key")).hasValue("value");
+
+    db.executeUpdateSql("delete from properties");
+    db.commit();
+
+    assertThat(config.get("key")).hasValue("value");
+  }
+
+  private void insertProjectProperty(ComponentDto project, String propertyKey, String propertyValue) {
+    db.properties().insertProperties(new PropertyDto().setKey(propertyKey).setValue(propertyValue).setResourceId(project.getId()));
+  }
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/MainBranchImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/MainBranchImplTest.java
new file mode 100644 (file)
index 0000000..a14e07a
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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 javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.MessageException;
+import org.sonar.db.component.BranchType;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MainBranchImplTest {
+
+  private static final ScannerReport.Component PROJECT = ScannerReport.Component.newBuilder().setType(ComponentType.PROJECT).setKey("P").build();
+  private static final ScannerReport.Component MODULE = ScannerReport.Component.newBuilder().setType(ComponentType.MODULE).setKey("M").build();
+  private static final ScannerReport.Component FILE = ScannerReport.Component.newBuilder().setType(ComponentType.FILE).setPath("src/Foo.js").build();
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Test
+  public void throw_ME_if_name_contains_invalid_characters() {
+    assertThatNameIsCorrect("master");
+    assertThatNameIsCorrect("feature/foo");
+    assertThatNameIsCorrect("feature_foo");
+
+    assertThatNameIsNotCorrect("feature foo");
+    assertThatNameIsNotCorrect("feature#foo");
+  }
+
+  @Test
+  public void default_branch_represents_the_project() {
+    MainBranchImpl branch = new MainBranchImpl(null);
+
+    assertThat(branch.isMain()).isTrue();
+    assertThat(branch.getType()).isEqualTo(BranchType.LONG);
+    assertThat(branch.getName()).isEmpty();
+    assertThat(branch.supportsCrossProjectCpd()).isTrue();
+
+    assertThat(branch.generateKey(PROJECT, null)).isEqualTo("P");
+    assertThat(branch.generateKey(MODULE, null)).isEqualTo("M");
+    assertThat(branch.generateKey(MODULE, FILE)).isEqualTo("M:src/Foo.js");
+  }
+
+  @Test
+  public void branch_represents_a_forked_project_with_different_key() {
+    MainBranchImpl branch = new MainBranchImpl("bar");
+
+    // not a real branch. Parameter sonar.branch forks project.
+    assertThat(branch.isMain()).isTrue();
+    assertThat(branch.getType()).isEqualTo(BranchType.LONG);
+    assertThat(branch.getName()).hasValue("bar");
+    assertThat(branch.supportsCrossProjectCpd()).isFalse();
+
+    assertThat(branch.generateKey(PROJECT, null)).isEqualTo("P:bar");
+    assertThat(branch.generateKey(MODULE, null)).isEqualTo("M:bar");
+    assertThat(branch.generateKey(MODULE, FILE)).isEqualTo("M:bar:src/Foo.js");
+  }
+
+  private void assertThatNameIsCorrect(@Nullable String name) {
+    MainBranchImpl branch = new MainBranchImpl(name);
+    assertThat(branch.getName()).hasValue(name);
+  }
+
+  private void assertThatNameIsNotCorrect(String name) {
+    expectedException.expect(MessageException.class);
+    expectedException.expectMessage("\"" + name + "\" is not a valid branch name. Allowed characters are alphanumeric, '-', '_', '.' and '/'.");
+
+    new MainBranchImpl(name);
+  }
+}
index b436f0d38d251468662f946806d4ca3838c58056..8f453755ac5f801c558174a425a19013a946c18d 100644 (file)
@@ -34,7 +34,7 @@ public class TestSettingsRepository implements ConfigurationRepository {
   }
 
   @Override
-  public Configuration getConfiguration(Component component) {
+  public Configuration getConfiguration() {
     return config;
   }
 }
index c03d9d74ca1a5159f88c83c35dea2cb92c3d85c7..2fb55e836469a07721328b00f9275dd701f46fa2 100644 (file)
@@ -25,13 +25,14 @@ import org.junit.rules.ExpectedException;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class CrossProjectDuplicationStatusHolderImplTest {
 
-  private static String BRANCH = "origin/master";
-
   @Rule
   public ExpectedException thrown = ExpectedException.none();
   @Rule
@@ -45,7 +46,7 @@ public class CrossProjectDuplicationStatusHolderImplTest {
   public void cross_project_duplication_is_enabled_when_enabled_in_report_and_no_branch() throws Exception {
     analysisMetadataHolder
       .setCrossProjectDuplicationEnabled(true)
-      .setBranch(null);
+      .setBranch(newBranch(true));
     underTest.start();
 
     assertThat(underTest.isEnabled()).isTrue();
@@ -56,7 +57,7 @@ public class CrossProjectDuplicationStatusHolderImplTest {
   public void cross_project_duplication_is_disabled_when_not_enabled_in_report() throws Exception {
     analysisMetadataHolder
       .setCrossProjectDuplicationEnabled(false)
-      .setBranch(null);
+      .setBranch(newBranch(true));
     underTest.start();
 
     assertThat(underTest.isEnabled()).isFalse();
@@ -67,7 +68,7 @@ public class CrossProjectDuplicationStatusHolderImplTest {
   public void cross_project_duplication_is_disabled_when_branch_is_used() throws Exception {
     analysisMetadataHolder
       .setCrossProjectDuplicationEnabled(true)
-      .setBranch(BRANCH);
+      .setBranch(newBranch(false));
     underTest.start();
 
     assertThat(underTest.isEnabled()).isFalse();
@@ -78,7 +79,7 @@ public class CrossProjectDuplicationStatusHolderImplTest {
   public void cross_project_duplication_is_disabled_when_not_enabled_in_report_and_when_branch_is_used() throws Exception {
     analysisMetadataHolder
       .setCrossProjectDuplicationEnabled(false)
-      .setBranch(BRANCH);
+      .setBranch(newBranch(false));
     underTest.start();
 
     assertThat(underTest.isEnabled()).isFalse();
@@ -89,7 +90,7 @@ public class CrossProjectDuplicationStatusHolderImplTest {
   public void flag_is_build_in_start() throws Exception {
     analysisMetadataHolder
       .setCrossProjectDuplicationEnabled(true)
-      .setBranch(null);
+      .setBranch(newBranch(true));
     underTest.start();
     assertThat(underTest.isEnabled()).isTrue();
 
@@ -105,4 +106,10 @@ public class CrossProjectDuplicationStatusHolderImplTest {
 
     underTest.isEnabled();
   }
+
+  private static Branch newBranch(boolean supportsCrossProjectCpd) {
+    Branch branch = mock(Branch.class);
+    when(branch.supportsCrossProjectCpd()).thenReturn(supportsCrossProjectCpd);
+    return branch;
+  }
 }
index b65ecc8a1cbb214592f3e7f4071f045539d7320d..aa29f66174e51cf10db2bb5f6b410dd05cc3f07e 100644 (file)
@@ -31,10 +31,8 @@ import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetada
 import org.sonar.server.computation.task.projectanalysis.analysis.Organization;
 import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
 import org.sonar.server.computation.task.projectanalysis.component.TestSettingsRepository;
-import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.DUMB_PROJECT;
 
 public class DefaultAssigneeTest {
 
@@ -44,15 +42,12 @@ public class DefaultAssigneeTest {
   @Rule
   public DbTester db = DbTester.create();
 
-  @Rule
-  public TreeRootHolderRule rootHolder = new TreeRootHolderRule().setRoot(DUMB_PROJECT);
-
   private MapSettings settings = new MapSettings();
   private ConfigurationRepository settingsRepository = new TestSettingsRepository(settings.asConfig());
   private AnalysisMetadataHolderImpl analysisMetadataHolder = new AnalysisMetadataHolderImpl();
   private OrganizationDto organizationDto;
 
-  private DefaultAssignee underTest = new DefaultAssignee(db.getDbClient(), rootHolder, settingsRepository, analysisMetadataHolder);
+  private DefaultAssignee underTest = new DefaultAssignee(db.getDbClient(), settingsRepository, analysisMetadataHolder);
 
   @Before
   public void setUp() throws Exception {
index 0c36fc90d9a1839960ebe21652a66dc848d05f32..cca2eb90d928fa7d953cc7cdaafd5133383ae939 100644 (file)
@@ -80,7 +80,7 @@ public class IssueFilterTest {
 
   @Test
   public void ignore_all() throws Exception {
-    IssueFilter underTest = newIssueFilter(newSettings(asList("*", "**"), Collections.<String>emptyList()));
+    IssueFilter underTest = newIssueFilter(newSettings(asList("*", "**"), Collections.emptyList()));
 
     assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isFalse();
     assertThat(underTest.accept(ISSUE_2, COMPONENT_1)).isFalse();
@@ -89,7 +89,7 @@ public class IssueFilterTest {
 
   @Test
   public void ignore_some_rule_and_component() throws Exception {
-    IssueFilter underTest = newIssueFilter(newSettings(asList("xoo:x1", "**/xoo/File1*"), Collections.<String>emptyList()));
+    IssueFilter underTest = newIssueFilter(newSettings(asList("xoo:x1", "**/xoo/File1*"), Collections.emptyList()));
 
     assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isFalse();
     assertThat(underTest.accept(ISSUE_1, COMPONENT_2)).isTrue();
@@ -101,7 +101,7 @@ public class IssueFilterTest {
   public void ignore_many_rules() throws Exception {
     IssueFilter underTest = newIssueFilter(newSettings(
       asList("xoo:x1", "**/xoo/File1*", "xoo:x2", "**/xoo/File1*"),
-      Collections.<String>emptyList()));
+      Collections.emptyList()));
 
     assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isFalse();
     assertThat(underTest.accept(ISSUE_1, COMPONENT_2)).isTrue();
@@ -111,7 +111,7 @@ public class IssueFilterTest {
 
   @Test
   public void include_all() throws Exception {
-    IssueFilter underTest = newIssueFilter(newSettings(Collections.<String>emptyList(), asList("*", "**")));
+    IssueFilter underTest = newIssueFilter(newSettings(Collections.emptyList(), asList("*", "**")));
 
     assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isTrue();
     assertThat(underTest.accept(ISSUE_2, COMPONENT_1)).isTrue();
@@ -120,7 +120,7 @@ public class IssueFilterTest {
 
   @Test
   public void include_some_rule_and_component() throws Exception {
-    IssueFilter underTest = newIssueFilter(newSettings(Collections.<String>emptyList(), asList("xoo:x1", "**/xoo/File1*")));
+    IssueFilter underTest = newIssueFilter(newSettings(Collections.emptyList(), asList("xoo:x1", "**/xoo/File1*")));
 
     assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isTrue();
     assertThat(underTest.accept(ISSUE_1, COMPONENT_2)).isFalse();
@@ -145,7 +145,7 @@ public class IssueFilterTest {
   @Test
   public void include_many_rules() throws Exception {
     IssueFilter underTest = newIssueFilter(newSettings(
-      Collections.<String>emptyList(),
+      Collections.emptyList(),
       asList("xoo:x1", "**/xoo/File1*", "xoo:x2", "**/xoo/File1*")));
 
     assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isTrue();
@@ -169,7 +169,7 @@ public class IssueFilterTest {
     expectedException.expect(IllegalArgumentException.class);
     expectedException.expectMessage("File path pattern cannot be empty. Please check 'sonar.issue.ignore.multicriteria' settings");
 
-    newIssueFilter(newSettings(asList("xoo:x1", ""), Collections.<String>emptyList()));
+    newIssueFilter(newSettings(asList("xoo:x1", ""), Collections.emptyList()));
   }
 
   @Test
@@ -177,12 +177,12 @@ public class IssueFilterTest {
     expectedException.expect(IllegalArgumentException.class);
     expectedException.expectMessage("Rule key pattern cannot be empty. Please check 'sonar.issue.enforce.multicriteria' settings");
 
-    newIssueFilter(newSettings(Collections.<String>emptyList(), asList("", "**")));
+    newIssueFilter(newSettings(Collections.emptyList(), asList("", "**")));
   }
 
   private IssueFilter newIssueFilter(MapSettings settings) {
-    when(settingsRepository.getConfiguration(PROJECT)).thenReturn(settings.asConfig());
-    return new IssueFilter(treeRootHolder, settingsRepository);
+    when(settingsRepository.getConfiguration()).thenReturn(settings.asConfig());
+    return new IssueFilter(settingsRepository);
   }
 
   private static MapSettings newSettings(List<String> exclusionsProperties, List<String> inclusionsProperties) {
index b949295806c703cfe390775f6d4c0de626f5d604..072d0ae3170921ec73fd314feb5a9c20a9c1049c 100644 (file)
@@ -37,9 +37,8 @@ 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.AnalysisMetadataHolderImpl;
-import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolder;
 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.MutableTreeRootHolderRule;
@@ -91,7 +90,8 @@ public class BuildComponentTreeStepTest {
   public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule()
     .setRootComponentRef(ROOT_REF)
     .setAnalysisDate(ANALYSIS_DATE)
-    .setBranch(null);
+    .setBranch(null)
+    .setProject(new Project("U1", REPORT_PROJECT_KEY, REPORT_PROJECT_KEY));
 
   private DbClient dbClient = dbTester.getDbClient();
   private BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder);
@@ -190,27 +190,27 @@ 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 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 compute_keys_and_uuids_on_project_having_module_and_directory() {
index c852a367335a903b50b9e9362884ce5b61c2693e..8d45b82f8a71d0a1da73f31609cc7681fad59fcc 100644 (file)
@@ -86,7 +86,7 @@ public class LoadPeriodsStepTest extends BaseStepTest {
 
   private void setupRoot(Component root) {
     treeRootHolder.setRoot(root);
-    when(settingsRepository.getConfiguration(root)).thenReturn(settings.asConfig());
+    when(settingsRepository.getConfiguration()).thenReturn(settings.asConfig());
   }
 
   @Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStepTest.java
new file mode 100644 (file)
index 0000000..0cce63f
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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.step;
+
+import com.google.common.base.Optional;
+import java.util.Collections;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
+import org.sonar.server.computation.task.projectanalysis.qualitygate.MutableQualityGateHolderRule;
+import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGate;
+import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateService;
+
+import static java.lang.String.format;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class LoadQualityGateStepTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+  @Rule
+  public MutableQualityGateHolderRule mutableQualityGateHolder = new MutableQualityGateHolderRule();
+
+  private ConfigurationRepository settingsRepository = mock(ConfigurationRepository.class);
+  private QualityGateService qualityGateService = mock(QualityGateService.class);
+
+  private LoadQualityGateStep underTest = new LoadQualityGateStep(settingsRepository, qualityGateService, mutableQualityGateHolder);
+
+  @Test
+  public void execute_sets_default_QualityGate_when_project_has_no_settings() {
+    when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().asConfig());
+
+    underTest.execute();
+
+    verifyNoQualityGate();
+
+    // verify only project is processed
+    verify(settingsRepository).getConfiguration();
+    verifyNoMoreInteractions(settingsRepository);
+  }
+
+  @Test
+  public void execute_sets_default_QualityGate_when_property_value_is_not_a_long() {
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage(format("Unsupported value (%s) in property sonar.qualitygate", "10 sds"));
+
+    when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().setProperty("sonar.qualitygate", "10 sds").asConfig());
+
+    underTest.execute();
+  }
+
+  @Test
+  public void execute_sets_default_QualityGate_if_it_can_not_be_found_by_service() {
+    when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().setProperty("sonar.qualitygate", 10).asConfig());
+    when(qualityGateService.findById(10)).thenReturn(Optional.absent());
+
+    underTest.execute();
+
+    verifyNoQualityGate();
+  }
+
+  @Test
+  public void execute_sets_QualityGate_if_it_can_be_found_by_service() {
+    QualityGate qualityGate = new QualityGate(465, "name", Collections.emptyList());
+
+    when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().setProperty("sonar.qualitygate", 10).asConfig());
+    when(qualityGateService.findById(10)).thenReturn(Optional.of(qualityGate));
+
+    underTest.execute();
+
+    assertThat(mutableQualityGateHolder.getQualityGate().get()).isSameAs(qualityGate);
+  }
+
+  private void verifyNoQualityGate() {
+    assertThat(mutableQualityGateHolder.getQualityGate()).isAbsent();
+  }
+
+}
index 2b1798b11a64475baff66960bb81da79b5a2a98e..49b273283a86860004801a1c15356c898b9de80f 100644 (file)
@@ -23,7 +23,6 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
-import org.mockito.ArgumentCaptor;
 import org.sonar.api.utils.MessageException;
 import org.sonar.api.utils.System2;
 import org.sonar.ce.queue.CeTask;
@@ -31,35 +30,30 @@ import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginRepository;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule;
 import org.sonar.server.computation.task.projectanalysis.analysis.Organization;
 import org.sonar.server.computation.task.projectanalysis.analysis.ScannerPlugin;
 import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReaderRule;
+import org.sonar.server.computation.task.projectanalysis.component.BranchLoader;
 import org.sonar.server.computation.task.step.ComputationStep;
-import org.sonar.server.organization.BillingValidations;
-import org.sonar.server.organization.BillingValidations.BillingValidationsException;
-import org.sonar.server.organization.BillingValidationsProxy;
 import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.organization.TestDefaultOrganizationProvider;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.tuple;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 public class LoadReportAnalysisMetadataHolderStepTest {
 
   private static final String PROJECT_KEY = "project_key";
-  private static final String BRANCH = "origin/master";
   private static final long ANALYSIS_DATE = 123456789L;
 
   @Rule
-  public DbTester dbTester = DbTester.create(System2.INSTANCE);
+  public DbTester db = DbTester.create(System2.INSTANCE);
   @Rule
   public BatchReportReaderRule reportReader = new BatchReportReaderRule();
   @Rule
@@ -67,16 +61,16 @@ public class LoadReportAnalysisMetadataHolderStepTest {
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
-  private DbClient dbClient = dbTester.getDbClient();
-  private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester);
-  private BillingValidationsProxy billingValidations = mock(BillingValidationsProxy.class);
+  private DbClient dbClient = db.getDbClient();
+  private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
   private PluginRepository pluginRepository = mock(PluginRepository.class);
   private ComputationStep underTest;
 
   @Before
   public void setUp() {
-    CeTask defaultOrgCeTask = createCeTask(PROJECT_KEY, dbTester.getDefaultOrganization().getUuid());
+    CeTask defaultOrgCeTask = createCeTask(PROJECT_KEY, db.getDefaultOrganization().getUuid());
     underTest = createStep(defaultOrgCeTask);
+    db.components().insertPublicProject(db.getDefaultOrganization(), p -> p.setDbKey(PROJECT_KEY));
   }
 
   @Test
@@ -103,32 +97,6 @@ public class LoadReportAnalysisMetadataHolderStepTest {
     assertThat(analysisMetadataHolder.getAnalysisDate()).isEqualTo(ANALYSIS_DATE);
   }
 
-  @Test
-  public void set_branch() {
-    reportReader.setMetadata(
-      newBatchReportBuilder()
-        .setBranch(BRANCH)
-        .build());
-
-    CeTask ceTask = createCeTask(PROJECT_KEY + ":" + BRANCH, dbTester.getDefaultOrganization().getUuid());
-    ComputationStep underTest = createStep(ceTask);
-
-    underTest.execute();
-
-    assertThat(analysisMetadataHolder.getBranch()).isEqualTo(BRANCH);
-  }
-
-  @Test
-  public void set_null_branch_when_nothing_in_the_report() {
-    reportReader.setMetadata(
-      newBatchReportBuilder()
-        .build());
-
-    underTest.execute();
-
-    assertThat(analysisMetadataHolder.getBranch()).isNull();
-  }
-
   @Test
   public void set_cross_project_duplication_to_true() {
     reportReader.setMetadata(
@@ -192,6 +160,7 @@ public class LoadReportAnalysisMetadataHolderStepTest {
   public void execute_fails_with_MessageException_if_projectKey_is_null_in_CE_task() {
     CeTask res = mock(CeTask.class);
     when(res.getComponentUuid()).thenReturn("prj_uuid");
+    when(res.getOrganizationUuid()).thenReturn(defaultOrganizationProvider.get().getUuid());
     reportReader.setMetadata(ScannerReport.Metadata.newBuilder().build());
 
     ComputationStep underTest = createStep(res);
@@ -235,13 +204,13 @@ public class LoadReportAnalysisMetadataHolderStepTest {
     reportReader.setMetadata(
       newBatchReportBuilder()
         .build());
-    OrganizationDto nonDefaultOrganizationDto = dbTester.organizations().insert();
+    OrganizationDto nonDefaultOrganizationDto = db.organizations().insert();
 
     ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, nonDefaultOrganizationDto.getUuid()));
 
     expectedException.expect(MessageException.class);
     expectedException.expectMessage("Report does not specify an OrganizationKey but it has been submitted to another organization (" +
-      nonDefaultOrganizationDto.getKey() + ") than the default one (" + dbTester.getDefaultOrganization().getKey() + ")");
+      nonDefaultOrganizationDto.getKey() + ") than the default one (" + db.getDefaultOrganization().getKey() + ")");
 
     underTest.execute();
   }
@@ -255,7 +224,7 @@ public class LoadReportAnalysisMetadataHolderStepTest {
     underTest.execute();
 
     Organization organization = analysisMetadataHolder.getOrganization();
-    OrganizationDto defaultOrganization = dbTester.getDefaultOrganization();
+    OrganizationDto defaultOrganization = db.getDefaultOrganization();
     assertThat(organization.getUuid()).isEqualTo(defaultOrganization.getUuid());
     assertThat(organization.getKey()).isEqualTo(defaultOrganization.getKey());
     assertThat(organization.getName()).isEqualTo(defaultOrganization.getName());
@@ -265,13 +234,13 @@ public class LoadReportAnalysisMetadataHolderStepTest {
   public void execute_set_organization_from_ce_task_when_organizationKey_is_set_in_report() {
     reportReader.setMetadata(
       newBatchReportBuilder()
-        .setOrganizationKey(dbTester.getDefaultOrganization().getKey())
+        .setOrganizationKey(db.getDefaultOrganization().getKey())
         .build());
 
     underTest.execute();
 
     Organization organization = analysisMetadataHolder.getOrganization();
-    OrganizationDto defaultOrganization = dbTester.getDefaultOrganization();
+    OrganizationDto defaultOrganization = db.getDefaultOrganization();
     assertThat(organization.getUuid()).isEqualTo(defaultOrganization.getUuid());
     assertThat(organization.getKey()).isEqualTo(defaultOrganization.getKey());
     assertThat(organization.getName()).isEqualTo(defaultOrganization.getName());
@@ -279,13 +248,15 @@ public class LoadReportAnalysisMetadataHolderStepTest {
 
   @Test
   public void execute_set_non_default_organization_from_ce_task() {
-    OrganizationDto nonDefaultOrganizationDto = dbTester.organizations().insert();
+    OrganizationDto nonDefaultOrganizationDto = db.organizations().insert();
+    ComponentDto project = db.components().insertPublicProject(nonDefaultOrganizationDto);
     reportReader.setMetadata(
       newBatchReportBuilder()
         .setOrganizationKey(nonDefaultOrganizationDto.getKey())
+        .setProjectKey(project.getDbKey())
         .build());
 
-    ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, nonDefaultOrganizationDto.getUuid()));
+    ComputationStep underTest = createStep(createCeTask(project.getDbKey(), nonDefaultOrganizationDto.getUuid()));
 
     underTest.execute();
 
@@ -297,15 +268,18 @@ public class LoadReportAnalysisMetadataHolderStepTest {
 
   @Test
   public void execute_ensures_that_report_has_quality_profiles_matching_the_project_organization() {
-    OrganizationDto organization = dbTester.organizations().insert();
+    OrganizationDto organization = db.organizations().insert();
+    ComponentDto project = db.components().insertPublicProject(organization);
     ScannerReport.Metadata.Builder metadataBuilder = newBatchReportBuilder();
-    metadataBuilder.setOrganizationKey(organization.getKey());
+    metadataBuilder
+      .setOrganizationKey(organization.getKey())
+      .setProjectKey(project.getDbKey());
     metadataBuilder.getMutableQprofilesPerLanguage().put("js", ScannerReport.Metadata.QProfile.newBuilder().setKey("p1").setName("Sonar way").setLanguage("js").build());
     reportReader.setMetadata(metadataBuilder.build());
 
-    dbTester.qualityProfiles().insert(organization, p -> p.setLanguage("js").setKee("p1"));
+    db.qualityProfiles().insert(organization, p -> p.setLanguage("js").setKee("p1"));
 
-    ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid()));
+    ComputationStep underTest = createStep(createCeTask(project.getDbKey(), organization.getUuid()));
 
     // no errors
     underTest.execute();
@@ -313,18 +287,21 @@ public class LoadReportAnalysisMetadataHolderStepTest {
 
   @Test
   public void execute_fails_with_MessageException_when_report_has_quality_profiles_on_other_organizations() {
-    OrganizationDto organization1 = dbTester.organizations().insert();
-    OrganizationDto organization2 = dbTester.organizations().insert();
+    OrganizationDto organization1 = db.organizations().insert();
+    OrganizationDto organization2 = db.organizations().insert();
+    ComponentDto projectInOrg1 = db.components().insertPublicProject(organization1);
     ScannerReport.Metadata.Builder metadataBuilder = newBatchReportBuilder();
-    metadataBuilder.setOrganizationKey(organization1.getKey());
+    metadataBuilder
+      .setOrganizationKey(organization1.getKey())
+      .setProjectKey(projectInOrg1.getDbKey());
     metadataBuilder.getMutableQprofilesPerLanguage().put("js", ScannerReport.Metadata.QProfile.newBuilder().setKey("jsInOrg1").setName("Sonar way").setLanguage("js").build());
     metadataBuilder.getMutableQprofilesPerLanguage().put("php", ScannerReport.Metadata.QProfile.newBuilder().setKey("phpInOrg2").setName("PHP way").setLanguage("php").build());
     reportReader.setMetadata(metadataBuilder.build());
 
-    dbTester.qualityProfiles().insert(organization1, p -> p.setLanguage("js").setKee("jsInOrg1"));
-    dbTester.qualityProfiles().insert(organization2, p -> p.setLanguage("php").setKee("phpInOrg2"));
+    db.qualityProfiles().insert(organization1, p -> p.setLanguage("js").setKee("jsInOrg1"));
+    db.qualityProfiles().insert(organization2, p -> p.setLanguage("php").setKee("phpInOrg2"));
 
-    ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization1.getUuid()));
+    ComputationStep underTest = createStep(createCeTask(projectInOrg1.getDbKey(), organization1.getUuid()));
 
     expectedException.expect(MessageException.class);
     expectedException.expectMessage("Quality profiles with following keys don't exist in organization [" + organization1.getKey() + "]: phpInOrg2");
@@ -334,49 +311,20 @@ public class LoadReportAnalysisMetadataHolderStepTest {
 
   @Test
   public void execute_does_not_fail_when_report_has_a_quality_profile_that_does_not_exist_anymore() {
-    OrganizationDto organization = dbTester.organizations().insert();
+    OrganizationDto organization = db.organizations().insert();
+    ComponentDto project = db.components().insertPublicProject(organization);
     ScannerReport.Metadata.Builder metadataBuilder = newBatchReportBuilder();
-    metadataBuilder.setOrganizationKey(organization.getKey());
+    metadataBuilder
+      .setOrganizationKey(organization.getKey())
+      .setProjectKey(project.getDbKey());
     metadataBuilder.getMutableQprofilesPerLanguage().put("js", ScannerReport.Metadata.QProfile.newBuilder().setKey("p1").setName("Sonar way").setLanguage("js").build());
     reportReader.setMetadata(metadataBuilder.build());
 
-    ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid()));
-
-    underTest.execute();
-  }
-
-  @Test
-  public void execute_fails_with_MessageException_when_organization_is_not_allowed_to_execute_analysis() {
-    OrganizationDto organization = dbTester.organizations().insert();
-    reportReader.setMetadata(newBatchReportBuilder()
-      .setOrganizationKey(organization.getKey())
-      .build());
-    ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid()));
-    doThrow(new BillingValidationsException("This organization cannot execute project analysis")).when(billingValidations)
-      .checkOnProjectAnalysis(any(BillingValidations.Organization.class));
-
-    expectedException.expect(MessageException.class);
-    expectedException.expectMessage("This organization cannot execute project analysis");
+    ComputationStep underTest = createStep(createCeTask(project.getDbKey(), organization.getUuid()));
 
     underTest.execute();
   }
 
-  @Test
-  public void execute_does_no_fails_when_organization_is_allowed_to_execute_analysis() {
-    OrganizationDto organization = dbTester.organizations().insert();
-    reportReader.setMetadata(newBatchReportBuilder()
-      .setOrganizationKey(organization.getKey())
-      .build());
-    ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid()));
-
-    underTest.execute();
-
-    ArgumentCaptor<BillingValidations.Organization> argumentCaptor = ArgumentCaptor.forClass(BillingValidations.Organization.class);
-    verify(billingValidations).checkOnProjectAnalysis(argumentCaptor.capture());
-    assertThat(argumentCaptor.getValue().getKey()).isEqualTo(organization.getKey());
-    assertThat(argumentCaptor.getValue().getUuid()).isEqualTo(organization.getUuid());
-  }
-
   @Test
   public void execute_read_plugins_from_report() {
     ScannerReport.Metadata.Builder metadataBuilder = newBatchReportBuilder();
@@ -398,7 +346,8 @@ public class LoadReportAnalysisMetadataHolderStepTest {
   }
 
   private LoadReportAnalysisMetadataHolderStep createStep(CeTask ceTask) {
-    return new LoadReportAnalysisMetadataHolderStep(ceTask, reportReader, analysisMetadataHolder, defaultOrganizationProvider, dbClient, billingValidations, pluginRepository);
+    return new LoadReportAnalysisMetadataHolderStep(ceTask, reportReader, analysisMetadataHolder,
+      defaultOrganizationProvider, dbClient, new BranchLoader(analysisMetadataHolder), pluginRepository);
   }
 
   private static ScannerReport.Metadata.Builder newBatchReportBuilder() {
index b6998f59d1618697f43c9eed00020ec5239ebbb1..fa3e45b5d105bc88684432f8bace12f3f5e5473e 100644 (file)
@@ -28,6 +28,7 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDao;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.server.computation.task.projectanalysis.component.BranchPersisterDelegate;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.component.MutableDbIdsRepository;
 import org.sonar.server.computation.task.projectanalysis.component.MutableDisabledComponentsHolder;
@@ -67,6 +68,7 @@ public class PersistComponentsStepTest {
       mock(MutableDbIdsRepository.class),
       System2.INSTANCE,
       mock(MutableDisabledComponentsHolder.class),
-      mock(AnalysisMetadataHolder.class)).execute();
+      mock(AnalysisMetadataHolder.class),
+      mock(BranchPersisterDelegate.class)).execute();
   }
 }
index 6264fc0c3d12cec78aa754515b03f83d0cb99ee6..a29dc29fe416d75ab1ab7475d25f3180ba9e7d8a 100644 (file)
@@ -134,7 +134,7 @@ public class PurgeDatastoresStepTest extends BaseStepTest {
 
   private void verify_call_purge_method_of_the_purge_task(Component project) {
     treeRootHolder.setRoot(project);
-    when(settingsRepository.getConfiguration(project)).thenReturn(new MapSettings().asConfig());
+    when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().asConfig());
     dbIdsRepository.setComponentId(project, PROJECT_ID);
 
     underTest.execute();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateLoadingStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateLoadingStepTest.java
deleted file mode 100644 (file)
index 7e671ff..0000000
+++ /dev/null
@@ -1,119 +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.step;
-
-import com.google.common.base.Optional;
-import java.util.Collections;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.server.computation.task.projectanalysis.component.Component;
-import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
-import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
-import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
-import org.sonar.server.computation.task.projectanalysis.component.VisitException;
-import org.sonar.server.computation.task.projectanalysis.qualitygate.Condition;
-import org.sonar.server.computation.task.projectanalysis.qualitygate.MutableQualityGateHolderRule;
-import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGate;
-import org.sonar.server.computation.task.projectanalysis.qualitygate.QualityGateService;
-
-import static java.lang.String.format;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.guava.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.sonar.test.ExceptionCauseMatcher.hasType;
-
-public class QualityGateLoadingStepTest {
-  private static final String PROJECT_KEY = "project key";
-  private static final ReportComponent PROJECT_ALONE = ReportComponent.builder(Component.Type.PROJECT, 1).setKey(PROJECT_KEY).build();
-
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-  @Rule
-  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-  @Rule
-  public MutableQualityGateHolderRule mutableQualityGateHolder = new MutableQualityGateHolderRule();
-
-  private ConfigurationRepository settingsRepository = mock(ConfigurationRepository.class);
-  private QualityGateService qualityGateService = mock(QualityGateService.class);
-
-  private LoadQualityGateStep underTest = new LoadQualityGateStep(treeRootHolder, settingsRepository, qualityGateService, mutableQualityGateHolder);
-
-  @Test
-  public void execute_sets_default_QualityGate_when_project_has_no_settings() {
-    ReportComponent root = ReportComponent.builder(Component.Type.PROJECT, 1).setKey(PROJECT_KEY).addChildren(ReportComponent.builder(Component.Type.FILE, 2).build()).build();
-    treeRootHolder.setRoot(root);
-    when(settingsRepository.getConfiguration(root)).thenReturn(new MapSettings().asConfig());
-
-    underTest.execute();
-
-    verifyNoQualityGate();
-
-    // verify only project is processed
-    verify(settingsRepository).getConfiguration(root);
-    verifyNoMoreInteractions(settingsRepository);
-  }
-
-  @Test
-  public void execute_sets_default_QualityGate_when_property_value_is_not_a_long() {
-    expectedException.expect(VisitException.class);
-    expectedException.expectCause(
-      hasType(IllegalStateException.class)
-        .andMessage(format("Unsupported value (%s) in property sonar.qualitygate", "10 sds")));
-
-    treeRootHolder.setRoot(PROJECT_ALONE);
-    when(settingsRepository.getConfiguration(PROJECT_ALONE)).thenReturn(new MapSettings().setProperty("sonar.qualitygate", "10 sds").asConfig());
-
-    underTest.execute();
-  }
-
-  @Test
-  public void execute_sets_default_QualityGate_if_it_can_not_be_found_by_service() {
-    treeRootHolder.setRoot(PROJECT_ALONE);
-    when(settingsRepository.getConfiguration(PROJECT_ALONE)).thenReturn(new MapSettings().setProperty("sonar.qualitygate", 10).asConfig());
-    when(qualityGateService.findById(10)).thenReturn(Optional.<QualityGate>absent());
-
-    underTest.execute();
-
-    verifyNoQualityGate();
-  }
-
-  @Test
-  public void execute_sets_QualityGate_if_it_can_be_found_by_service() {
-    QualityGate qualityGate = new QualityGate(465, "name", Collections.<Condition>emptyList());
-
-    treeRootHolder.setRoot(PROJECT_ALONE);
-    when(settingsRepository.getConfiguration(PROJECT_ALONE)).thenReturn(new MapSettings().setProperty("sonar.qualitygate", 10).asConfig());
-    when(qualityGateService.findById(10)).thenReturn(Optional.of(qualityGate));
-
-    underTest.execute();
-
-    assertThat(mutableQualityGateHolder.getQualityGate().get()).isSameAs(qualityGate);
-  }
-
-  private void verifyNoQualityGate() {
-    assertThat(mutableQualityGateHolder.getQualityGate()).isAbsent();
-  }
-
-}
index 5c823ab234d2baf326a024e214172047841ff9f7..d013003c1b83e99f18563f878a2b20151011c183 100644 (file)
@@ -22,27 +22,36 @@ package org.sonar.server.computation.task.projectanalysis.step;
 import com.google.common.base.Optional;
 import java.text.SimpleDateFormat;
 import java.util.Date;
-import java.util.Random;
+import java.util.function.Consumer;
 import java.util.stream.Stream;
+import javax.annotation.Nullable;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.System2;
+import org.sonar.core.component.ComponentKeys;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
-import org.sonar.db.organization.OrganizationDto;
+import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
+import org.sonar.server.computation.task.projectanalysis.analysis.Project;
+import org.sonar.server.computation.task.projectanalysis.component.BranchPersisterDelegate;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.component.FileAttributes;
+import org.sonar.server.computation.task.projectanalysis.component.MainBranchImpl;
 import org.sonar.server.computation.task.projectanalysis.component.MutableDbIdsRepositoryRule;
 import org.sonar.server.computation.task.projectanalysis.component.MutableDisabledComponentsHolder;
 import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
 import org.sonar.server.computation.task.step.ComputationStep;
 
+import static org.apache.commons.lang.StringUtils.isEmpty;
+import static org.apache.commons.lang.StringUtils.trimToNull;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.guava.api.Assertions.assertThat;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
@@ -52,7 +61,6 @@ import static org.sonar.db.component.ComponentDto.UUID_PATH_OF_ROOT;
 import static org.sonar.db.component.ComponentDto.UUID_PATH_SEPARATOR;
 import static org.sonar.db.component.ComponentTesting.newDirectory;
 import static org.sonar.db.component.ComponentTesting.newModuleDto;
-import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
 import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.DIRECTORY;
 import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.FILE;
 import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT;
@@ -66,7 +74,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
   private static final String ORGANIZATION_UUID = "org1";
 
   @Rule
-  public DbTester dbTester = DbTester.create(System2.INSTANCE);
+  public DbTester db = DbTester.create(System2.INSTANCE);
   @Rule
   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
   @Rule
@@ -76,18 +84,19 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     .setOrganizationUuid(ORGANIZATION_UUID);
 
   private System2 system2 = mock(System2.class);
-  private DbClient dbClient = dbTester.getDbClient();
+  private DbClient dbClient = db.getDbClient();
   private Date now;
   private MutableDisabledComponentsHolder disabledComponentsHolder = mock(MutableDisabledComponentsHolder.class, RETURNS_DEEP_STUBS);
   private PersistComponentsStep underTest;
+  private BranchPersisterDelegate branchPersister;
 
   @Before
   public void setup() throws Exception {
     now = DATE_FORMAT.parse("2015-06-02");
     when(system2.now()).thenReturn(now.getTime());
 
-    dbTester.organizations().insertForUuid(ORGANIZATION_UUID);
-    underTest = new PersistComponentsStep(dbClient, treeRootHolder, dbIdsRepository, system2, disabledComponentsHolder, analysisMetadataHolder);
+    db.organizations().insertForUuid(ORGANIZATION_UUID);
+    underTest = new PersistComponentsStep(dbClient, treeRootHolder, dbIdsRepository, system2, disabledComponentsHolder, analysisMetadataHolder, branchPersister);
   }
 
   @Override
@@ -97,7 +106,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
   @Test
   public void persist_components() {
-    ComponentDto projectDto = insertProject();
+    ComponentDto projectDto = prepareProject();
     Component file = builder(FILE, 4).setUuid("DEFG").setKey("MODULE_KEY:src/main/java/dir/Foo.java")
       .setPath("src/main/java/dir/Foo.java")
       .setFileAttributes(new FileAttributes(false, "java", 1))
@@ -119,9 +128,9 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
     underTest.execute();
 
-    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4);
+    assertThat(db.countRowsOfTable("projects")).isEqualTo(4);
 
-    ComponentDto moduleDto = dbClient.componentDao().selectByKey(dbTester.getSession(), MODULE_KEY).get();
+    ComponentDto moduleDto = dbClient.componentDao().selectByKey(db.getSession(), MODULE_KEY).get();
     assertThat(moduleDto.getOrganizationUuid()).isEqualTo(ORGANIZATION_UUID);
     assertThat(moduleDto.name()).isEqualTo("Module");
     assertThat(moduleDto.description()).isEqualTo("Module description");
@@ -137,7 +146,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(moduleDto.getRootUuid()).isEqualTo(projectDto.uuid());
     assertThat(moduleDto.getCreatedAt()).isEqualTo(now);
 
-    ComponentDto directoryDto = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir").get();
+    ComponentDto directoryDto = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir").get();
     assertThat(directoryDto.getOrganizationUuid()).isEqualTo(ORGANIZATION_UUID);
     assertThat(directoryDto.name()).isEqualTo("src/main/java/dir");
     assertThat(directoryDto.description()).isNull();
@@ -153,7 +162,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(directoryDto.getRootUuid()).isEqualTo(moduleDto.uuid());
     assertThat(directoryDto.getCreatedAt()).isEqualTo(now);
 
-    ComponentDto fileDto = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get();
+    ComponentDto fileDto = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get();
     assertThat(fileDto.getOrganizationUuid()).isEqualTo(ORGANIZATION_UUID);
     assertThat(fileDto.name()).isEqualTo("Foo.java");
     assertThat(fileDto.description()).isNull();
@@ -175,33 +184,113 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(dbIdsRepository.getComponentId(file)).isEqualTo(fileDto.getId());
   }
 
+  @Test
+  public void persist_components_of_existing_branch() {
+    ComponentDto project = prepareBranch("feature/foo");
+    Component file = builder(FILE, 4).setUuid("DEFG").setKey("MODULE_KEY:src/main/java/dir/Foo.java")
+      .setPath("src/main/java/dir/Foo.java")
+      .setFileAttributes(new FileAttributes(false, "java", 1))
+      .build();
+    Component directory = builder(DIRECTORY, 3).setUuid("CDEF").setKey("MODULE_KEY:src/main/java/dir")
+      .setPath("src/main/java/dir")
+      .addChildren(file)
+      .build();
+    Component module = builder(Component.Type.MODULE, 2).setUuid("BCDE").setKey(MODULE_KEY)
+      .setPath("module")
+      .setName("Module")
+      .setDescription("Module description")
+      .addChildren(directory)
+      .build();
+    Component treeRoot = asTreeRoot(project)
+      .addChildren(module)
+      .build();
+    treeRootHolder.setRoot(treeRoot);
+
+    underTest.execute();
+
+    assertThat(db.countRowsOfTable("projects")).isEqualTo(4);
+
+    ComponentDto moduleDto = dbClient.componentDao().selectByKey(db.getSession(), MODULE_KEY).get();
+    assertThat(moduleDto.getOrganizationUuid()).isEqualTo(ORGANIZATION_UUID);
+    assertThat(moduleDto.name()).isEqualTo("Module");
+    assertThat(moduleDto.description()).isEqualTo("Module description");
+    assertThat(moduleDto.path()).isEqualTo("module");
+    assertThat(moduleDto.uuid()).isEqualTo("BCDE");
+    assertThat(moduleDto.getUuidPath()).isEqualTo(project.getUuidPath() + project.uuid() + UUID_PATH_SEPARATOR);
+    assertThat(moduleDto.moduleUuid()).isEqualTo(project.uuid());
+    assertThat(moduleDto.moduleUuidPath()).isEqualTo(project.moduleUuidPath() + moduleDto.uuid() + ".");
+    assertThat(moduleDto.getMainBranchProjectUuid()).isEqualTo(project.uuid());
+    assertThat(moduleDto.projectUuid()).isEqualTo(project.uuid());
+    assertThat(moduleDto.qualifier()).isEqualTo("BRC");
+    assertThat(moduleDto.scope()).isEqualTo("PRJ");
+    assertThat(moduleDto.getRootUuid()).isEqualTo(project.uuid());
+    assertThat(moduleDto.getCreatedAt()).isEqualTo(now);
+
+    ComponentDto directoryDto = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir").get();
+    assertThat(directoryDto.getOrganizationUuid()).isEqualTo(ORGANIZATION_UUID);
+    assertThat(directoryDto.name()).isEqualTo("src/main/java/dir");
+    assertThat(directoryDto.description()).isNull();
+    assertThat(directoryDto.path()).isEqualTo("src/main/java/dir");
+    assertThat(directoryDto.uuid()).isEqualTo("CDEF");
+    assertThat(directoryDto.getUuidPath()).isEqualTo(moduleDto.getUuidPath() + moduleDto.uuid() + UUID_PATH_SEPARATOR);
+    assertThat(directoryDto.moduleUuid()).isEqualTo(moduleDto.uuid());
+    assertThat(directoryDto.moduleUuidPath()).isEqualTo(moduleDto.moduleUuidPath());
+    assertThat(directoryDto.getMainBranchProjectUuid()).isEqualTo(project.uuid());
+    assertThat(directoryDto.projectUuid()).isEqualTo(project.uuid());
+    assertThat(directoryDto.qualifier()).isEqualTo("DIR");
+    assertThat(directoryDto.scope()).isEqualTo("DIR");
+    assertThat(directoryDto.getRootUuid()).isEqualTo(moduleDto.uuid());
+    assertThat(directoryDto.getCreatedAt()).isEqualTo(now);
+
+    ComponentDto fileDto = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get();
+    assertThat(fileDto.getOrganizationUuid()).isEqualTo(ORGANIZATION_UUID);
+    assertThat(fileDto.name()).isEqualTo("Foo.java");
+    assertThat(fileDto.description()).isNull();
+    assertThat(fileDto.path()).isEqualTo("src/main/java/dir/Foo.java");
+    assertThat(fileDto.language()).isEqualTo("java");
+    assertThat(fileDto.uuid()).isEqualTo("DEFG");
+    assertThat(fileDto.getUuidPath()).isEqualTo(directoryDto.getUuidPath() + directoryDto.uuid() + UUID_PATH_SEPARATOR);
+    assertThat(fileDto.moduleUuid()).isEqualTo(moduleDto.uuid());
+    assertThat(fileDto.moduleUuidPath()).isEqualTo(moduleDto.moduleUuidPath());
+    assertThat(fileDto.getMainBranchProjectUuid()).isEqualTo(project.uuid());
+    assertThat(fileDto.projectUuid()).isEqualTo(project.uuid());
+    assertThat(fileDto.qualifier()).isEqualTo("FIL");
+    assertThat(fileDto.scope()).isEqualTo("FIL");
+    assertThat(fileDto.getRootUuid()).isEqualTo(moduleDto.uuid());
+    assertThat(fileDto.getCreatedAt()).isEqualTo(now);
+
+    assertThat(dbIdsRepository.getComponentId(module)).isEqualTo(moduleDto.getId());
+    assertThat(dbIdsRepository.getComponentId(directory)).isEqualTo(directoryDto.getId());
+    assertThat(dbIdsRepository.getComponentId(file)).isEqualTo(fileDto.getId());
+  }
+
   @Test
   public void persist_file_directly_attached_on_root_directory() {
-    ComponentDto projectDto = insertProject();
+    ComponentDto projectDto = prepareProject();
     treeRootHolder.setRoot(
       asTreeRoot(projectDto)
         .addChildren(
-          builder(DIRECTORY, 2).setUuid("CDEF").setKey(PROJECT_KEY + ":/").setPath("/")
+          builder(DIRECTORY, 2).setUuid("CDEF").setKey(projectDto.getDbKey() + ":/").setPath("/")
             .addChildren(
-              builder(FILE, 3).setUuid("DEFG").setKey(PROJECT_KEY + ":pom.xml").setPath("pom.xml")
+              builder(FILE, 3).setUuid("DEFG").setKey(projectDto.getDbKey() + ":pom.xml").setPath("pom.xml")
                 .build())
             .build())
         .build());
 
     underTest.execute();
 
-    ComponentDto directory = dbClient.componentDao().selectByKey(dbTester.getSession(), "PROJECT_KEY:/").get();
+    ComponentDto directory = dbClient.componentDao().selectByKey(db.getSession(), projectDto.getDbKey() + ":/").get();
     assertThat(directory.name()).isEqualTo("/");
     assertThat(directory.path()).isEqualTo("/");
 
-    ComponentDto file = dbClient.componentDao().selectByKey(dbTester.getSession(), "PROJECT_KEY:pom.xml").get();
+    ComponentDto file = dbClient.componentDao().selectByKey(db.getSession(), projectDto.getDbKey() + ":pom.xml").get();
     assertThat(file.name()).isEqualTo("pom.xml");
     assertThat(file.path()).isEqualTo("pom.xml");
   }
 
   @Test
   public void persist_unit_test() {
-    ComponentDto projectDto = insertProject();
+    ComponentDto projectDto = prepareProject();
     treeRootHolder.setRoot(
       asTreeRoot(projectDto)
         .addChildren(
@@ -217,7 +306,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
     underTest.execute();
 
-    ComponentDto file = dbClient.componentDao().selectByKey(dbTester.getSession(), PROJECT_KEY + ":src/test/java/dir/FooTest.java").get();
+    ComponentDto file = dbClient.componentDao().selectByKey(db.getSession(), PROJECT_KEY + ":src/test/java/dir/FooTest.java").get();
     assertThat(file.name()).isEqualTo("FooTest.java");
     assertThat(file.path()).isEqualTo("src/test/java/dir/FooTest.java");
     assertThat(file.qualifier()).isEqualTo("UTS");
@@ -227,14 +316,13 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
   @Test
   public void persist_only_new_components() {
     // Project and module already exists
-    ComponentDto project = newPrivateProjectDto(dbTester.getDefaultOrganization(), "ABCD").setDbKey(PROJECT_KEY).setName("Project");
-    dbClient.componentDao().insert(dbTester.getSession(), project);
+    ComponentDto project = prepareProject();
     ComponentDto module = ComponentTesting.newModuleDto("BCDE", project).setDbKey(MODULE_KEY).setName("Module");
-    dbClient.componentDao().insert(dbTester.getSession(), module);
-    dbTester.getSession().commit();
+    dbClient.componentDao().insert(db.getSession(), module);
+    db.getSession().commit();
 
     treeRootHolder.setRoot(
-      builder(PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY)
+      builder(PROJECT, 1).setUuid(project.uuid()).setKey(project.getDbKey())
         .setName("Project")
         .addChildren(
           builder(Component.Type.MODULE, 2).setUuid("BCDE").setKey(MODULE_KEY)
@@ -252,15 +340,15 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
     underTest.execute();
 
-    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4);
+    assertThat(db.countRowsOfTable("projects")).isEqualTo(4);
 
-    ComponentDto projectReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), PROJECT_KEY).get();
+    ComponentDto projectReloaded = dbClient.componentDao().selectByKey(db.getSession(), project.getDbKey()).get();
     assertThat(projectReloaded.getId()).isEqualTo(project.getId());
     assertThat(projectReloaded.uuid()).isEqualTo(project.uuid());
     assertThat(projectReloaded.getUuidPath()).isEqualTo(UUID_PATH_OF_ROOT);
     assertThat(projectReloaded.getMainBranchProjectUuid()).isNull();
 
-    ComponentDto moduleReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), MODULE_KEY).get();
+    ComponentDto moduleReloaded = dbClient.componentDao().selectByKey(db.getSession(), MODULE_KEY).get();
     assertThat(moduleReloaded.getId()).isEqualTo(module.getId());
     assertThat(moduleReloaded.uuid()).isEqualTo(module.uuid());
     assertThat(moduleReloaded.getUuidPath()).isEqualTo(module.getUuidPath());
@@ -270,7 +358,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(moduleReloaded.getRootUuid()).isEqualTo(module.getRootUuid());
     assertThat(moduleReloaded.getMainBranchProjectUuid()).isNull();
 
-    ComponentDto directory = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir").get();
+    ComponentDto directory = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir").get();
     assertThat(directory.getUuidPath()).isEqualTo(directory.getUuidPath());
     assertThat(directory.moduleUuid()).isEqualTo(module.uuid());
     assertThat(directory.moduleUuidPath()).isEqualTo(module.moduleUuidPath());
@@ -278,7 +366,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(directory.getRootUuid()).isEqualTo(module.uuid());
     assertThat(directory.getMainBranchProjectUuid()).isNull();
 
-    ComponentDto file = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get();
+    ComponentDto file = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get();
     assertThat(file.getUuidPath()).isEqualTo(file.getUuidPath());
     assertThat(file.moduleUuid()).isEqualTo(module.uuid());
     assertThat(file.moduleUuidPath()).isEqualTo(module.moduleUuidPath());
@@ -289,7 +377,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
   @Test
   public void compute_root_uuid() {
-    ComponentDto project = insertProject();
+    ComponentDto project = prepareProject();
     treeRootHolder.setRoot(
       asTreeRoot(project)
         .addChildren(
@@ -312,36 +400,28 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
     underTest.execute();
 
-    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(5);
+    assertThat(db.countRowsOfTable("projects")).isEqualTo(5);
 
-    Optional<ComponentDto> module = dbClient.componentDao().selectByKey(dbTester.getSession(), MODULE_KEY);
+    Optional<ComponentDto> module = dbClient.componentDao().selectByKey(db.getSession(), MODULE_KEY);
     assertThat(module).isPresent();
     assertThat(module.get().getRootUuid()).isEqualTo(project.uuid());
 
-    Optional<ComponentDto> subModule1 = dbClient.componentDao().selectByKey(dbTester.getSession(), "SUB_MODULE_1_KEY");
+    Optional<ComponentDto> subModule1 = dbClient.componentDao().selectByKey(db.getSession(), "SUB_MODULE_1_KEY");
     assertThat(subModule1).isPresent();
     assertThat(subModule1.get().getRootUuid()).isEqualTo(project.uuid());
 
-    Optional<ComponentDto> subModule2 = dbClient.componentDao().selectByKey(dbTester.getSession(), "SUB_MODULE_2_KEY");
+    Optional<ComponentDto> subModule2 = dbClient.componentDao().selectByKey(db.getSession(), "SUB_MODULE_2_KEY");
     assertThat(subModule2).isPresent();
     assertThat(subModule2.get().getRootUuid()).isEqualTo(project.uuid());
 
-    Optional<ComponentDto> directory = dbClient.componentDao().selectByKey(dbTester.getSession(), "SUB_MODULE_2_KEY:src/main/java/dir");
+    Optional<ComponentDto> directory = dbClient.componentDao().selectByKey(db.getSession(), "SUB_MODULE_2_KEY:src/main/java/dir");
     assertThat(directory).isPresent();
     assertThat(directory.get().getRootUuid()).isEqualTo(subModule2.get().uuid());
   }
 
-  private ReportComponent.Builder asTreeRoot(ComponentDto project) {
-    return builder(PROJECT, 1).setUuid(project.uuid()).setKey(project.getDbKey()).setName(project.name());
-  }
-
-  public ComponentDto insertProject() {
-    return dbTester.components().insertPrivateProject(dbTester.organizations().insert());
-  }
-
   @Test
   public void persist_multi_modules() {
-    ComponentDto project = insertProject();
+    ComponentDto project = prepareProject();
     treeRootHolder.setRoot(
       asTreeRoot(project)
         .setName("Project")
@@ -360,21 +440,21 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
     underTest.execute();
 
-    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4);
+    assertThat(db.countRowsOfTable("projects")).isEqualTo(4);
 
-    ComponentDto moduleA = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_A").get();
+    ComponentDto moduleA = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_A").get();
     assertThat(moduleA.getUuidPath()).isEqualTo(project.getUuidPath() + project.uuid() + UUID_PATH_SEPARATOR);
     assertThat(moduleA.moduleUuid()).isEqualTo(project.uuid());
     assertThat(moduleA.moduleUuidPath()).isEqualTo(project.moduleUuidPath() + moduleA.uuid() + ".");
     assertThat(moduleA.getRootUuid()).isEqualTo(project.uuid());
 
-    ComponentDto subModuleA = dbClient.componentDao().selectByKey(dbTester.getSession(), "SUB_MODULE_A").get();
+    ComponentDto subModuleA = dbClient.componentDao().selectByKey(db.getSession(), "SUB_MODULE_A").get();
     assertThat(subModuleA.getUuidPath()).isEqualTo(moduleA.getUuidPath() + moduleA.uuid() + UUID_PATH_SEPARATOR);
     assertThat(subModuleA.moduleUuid()).isEqualTo(moduleA.uuid());
     assertThat(subModuleA.moduleUuidPath()).isEqualTo(moduleA.moduleUuidPath() + subModuleA.uuid() + ".");
     assertThat(subModuleA.getRootUuid()).isEqualTo(project.uuid());
 
-    ComponentDto moduleB = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_B").get();
+    ComponentDto moduleB = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_B").get();
     assertThat(moduleB.getUuidPath()).isEqualTo(project.getUuidPath() + project.uuid() + UUID_PATH_SEPARATOR);
     assertThat(moduleB.moduleUuid()).isEqualTo(project.uuid());
     assertThat(moduleB.moduleUuidPath()).isEqualTo(project.moduleUuidPath() + moduleB.uuid() + ".");
@@ -383,18 +463,17 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
   @Test
   public void nothing_to_persist() {
-    ComponentDto project = newPrivateProjectDto(dbTester.organizations().insert(), "ABCD").setDbKey(PROJECT_KEY).setName("Project");
-    dbClient.componentDao().insert(dbTester.getSession(), project);
+    ComponentDto project = prepareProject();
     ComponentDto module = ComponentTesting.newModuleDto("BCDE", project).setDbKey(MODULE_KEY).setName("Module");
-    dbClient.componentDao().insert(dbTester.getSession(), module);
+    dbClient.componentDao().insert(db.getSession(), module);
     ComponentDto directory = ComponentTesting.newDirectory(module, "src/main/java/dir").setUuid("CDEF").setDbKey("MODULE_KEY:src/main/java/dir");
     ComponentDto file = ComponentTesting.newFileDto(module, directory, "DEFG").setPath("src/main/java/dir/Foo.java").setName("Foo.java")
       .setDbKey("MODULE_KEY:src/main/java/dir/Foo.java");
-    dbClient.componentDao().insert(dbTester.getSession(), directory, file);
-    dbTester.getSession().commit();
+    dbClient.componentDao().insert(db.getSession(), directory, file);
+    db.getSession().commit();
 
     treeRootHolder.setRoot(
-      builder(PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY)
+      builder(PROJECT, 1).setUuid(project.uuid()).setKey(project.getDbKey())
         .setName("Project")
         .addChildren(
           builder(Component.Type.MODULE, 2).setUuid("BCDE").setKey(MODULE_KEY)
@@ -412,13 +491,13 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
     underTest.execute();
 
-    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4);
-    assertThat(dbClient.componentDao().selectByKey(dbTester.getSession(), PROJECT_KEY).get().getId()).isEqualTo(project.getId());
-    assertThat(dbClient.componentDao().selectByKey(dbTester.getSession(), MODULE_KEY).get().getId()).isEqualTo(module.getId());
-    assertThat(dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir").get().getId()).isEqualTo(directory.getId());
-    assertThat(dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get().getId()).isEqualTo(file.getId());
+    assertThat(db.countRowsOfTable("projects")).isEqualTo(4);
+    assertThat(dbClient.componentDao().selectByKey(db.getSession(), project.getDbKey()).get().getId()).isEqualTo(project.getId());
+    assertThat(dbClient.componentDao().selectByKey(db.getSession(), MODULE_KEY).get().getId()).isEqualTo(module.getId());
+    assertThat(dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir").get().getId()).isEqualTo(directory.getId());
+    assertThat(dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get().getId()).isEqualTo(file.getId());
 
-    ComponentDto projectReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), PROJECT_KEY).get();
+    ComponentDto projectReloaded = dbClient.componentDao().selectByKey(db.getSession(), project.getDbKey()).get();
     assertThat(projectReloaded.getId()).isEqualTo(project.getId());
     assertThat(projectReloaded.uuid()).isEqualTo(project.uuid());
     assertThat(projectReloaded.moduleUuid()).isEqualTo(project.moduleUuid());
@@ -426,7 +505,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(projectReloaded.projectUuid()).isEqualTo(project.projectUuid());
     assertThat(projectReloaded.getRootUuid()).isEqualTo(project.getRootUuid());
 
-    ComponentDto moduleReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), MODULE_KEY).get();
+    ComponentDto moduleReloaded = dbClient.componentDao().selectByKey(db.getSession(), MODULE_KEY).get();
     assertThat(moduleReloaded.getId()).isEqualTo(module.getId());
     assertThat(moduleReloaded.uuid()).isEqualTo(module.uuid());
     assertThat(moduleReloaded.getUuidPath()).isEqualTo(module.getUuidPath());
@@ -435,7 +514,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(moduleReloaded.projectUuid()).isEqualTo(module.projectUuid());
     assertThat(moduleReloaded.getRootUuid()).isEqualTo(module.getRootUuid());
 
-    ComponentDto directoryReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir").get();
+    ComponentDto directoryReloaded = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir").get();
     assertThat(directoryReloaded.uuid()).isEqualTo(directory.uuid());
     assertThat(directoryReloaded.getUuidPath()).isEqualTo(directory.getUuidPath());
     assertThat(directoryReloaded.moduleUuid()).isEqualTo(directory.moduleUuid());
@@ -445,7 +524,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(directoryReloaded.name()).isEqualTo(directory.name());
     assertThat(directoryReloaded.path()).isEqualTo(directory.path());
 
-    ComponentDto fileReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get();
+    ComponentDto fileReloaded = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get();
     assertThat(fileReloaded.uuid()).isEqualTo(file.uuid());
     assertThat(fileReloaded.getUuidPath()).isEqualTo(file.getUuidPath());
     assertThat(fileReloaded.moduleUuid()).isEqualTo(file.moduleUuid());
@@ -458,14 +537,15 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
   @Test
   public void update_module_name_and_description() {
-    ComponentDto project = newPrivateProjectDto(dbTester.getDefaultOrganization(), "ABCD").setDbKey(PROJECT_KEY).setName("Project").setDescription("Project description");
-    dbClient.componentDao().insert(dbTester.getSession(), project);
+    ComponentDto project = prepareProject(p -> p.setName("Project").setDescription("Project description"));
     ComponentDto module = ComponentTesting.newModuleDto("BCDE", project).setDbKey(MODULE_KEY).setName("Module");
-    dbClient.componentDao().insert(dbTester.getSession(), module);
-    dbTester.getSession().commit();
+    dbClient.componentDao().insert(db.getSession(), module);
+    db.getSession().commit();
 
     treeRootHolder.setRoot(
-      builder(PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY)
+      builder(PROJECT, 1)
+        .setUuid(project.uuid())
+        .setKey(project.getDbKey())
         .setName("New Project")
         .setDescription("New project description")
         .addChildren(
@@ -480,31 +560,30 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     underTest.execute();
 
     // functional transaction not finished, "A-fields" are not updated yet
-    assertNameAndDescription(PROJECT_KEY, "Project", "Project description");
+    assertNameAndDescription(project.getDbKey(), "Project", "Project description");
     assertNameAndDescription(MODULE_KEY, "Module", null);
 
     // commit functional transaction -> copies B-fields to A-fields
-    dbClient.componentDao().applyBChangesForRootComponentUuid(dbTester.getSession(), "ABCD");
-    assertNameAndDescription(PROJECT_KEY, "New Project", "New project description");
+    dbClient.componentDao().applyBChangesForRootComponentUuid(db.getSession(), project.uuid());
+    assertNameAndDescription(project.getDbKey(), "New Project", "New project description");
     assertNameAndDescription(MODULE_KEY, "New Module", "New module description");
   }
 
   private void assertNameAndDescription(String key, String expectedName, String expectedDescription) {
-    ComponentDto dto = dbClient.componentDao().selectByKey(dbTester.getSession(), key).get();
+    ComponentDto dto = dbClient.componentDao().selectByKey(db.getSession(), key).get();
     assertThat(dto.name()).isEqualTo(expectedName);
     assertThat(dto.description()).isEqualTo(expectedDescription);
   }
 
   @Test
   public void update_module_path() {
-    ComponentDto project = newPrivateProjectDto(dbTester.organizations().insert(), "ABCD").setDbKey(PROJECT_KEY).setName("Project");
-    dbClient.componentDao().insert(dbTester.getSession(), project);
+    ComponentDto project = prepareProject();
     ComponentDto module = ComponentTesting.newModuleDto("BCDE", project).setDbKey(MODULE_KEY).setName("Module").setPath("path");
-    dbClient.componentDao().insert(dbTester.getSession(), module);
-    dbTester.getSession().commit();
+    dbClient.componentDao().insert(db.getSession(), module);
+    db.getSession().commit();
 
     treeRootHolder.setRoot(
-      builder(PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY)
+      builder(PROJECT, 1).setUuid(project.uuid()).setKey(project.getDbKey())
         .setName("Project")
         .addChildren(
           builder(Component.Type.MODULE, 2).setUuid("BCDE").setKey(MODULE_KEY)
@@ -515,32 +594,31 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
     underTest.execute();
 
-    assertThat(dbClient.componentDao().selectByKey(dbTester.getSession(), MODULE_KEY).get().path()).isEqualTo("path");
+    assertThat(dbClient.componentDao().selectByKey(db.getSession(), MODULE_KEY).get().path()).isEqualTo("path");
 
     // commit the functional transaction
-    dbClient.componentDao().applyBChangesForRootComponentUuid(dbTester.getSession(), project.uuid());
-    assertThat(dbClient.componentDao().selectByKey(dbTester.getSession(), MODULE_KEY).get().path()).isEqualTo("New path");
+    dbClient.componentDao().applyBChangesForRootComponentUuid(db.getSession(), project.uuid());
+    assertThat(dbClient.componentDao().selectByKey(db.getSession(), MODULE_KEY).get().path()).isEqualTo("New path");
   }
 
   @Test
   public void update_module_uuid_when_moving_a_module() {
-    ComponentDto project = newPrivateProjectDto(dbTester.getDefaultOrganization(), "ABCD").setDbKey(PROJECT_KEY).setName("Project");
-    dbClient.componentDao().insert(dbTester.getSession(), project);
+    ComponentDto project = prepareProject();
     ComponentDto moduleA = ComponentTesting.newModuleDto("EDCB", project)
       .setDbKey("MODULE_A")
       .setName("Module A");
     ComponentDto moduleB = ComponentTesting.newModuleDto("BCDE", project)
       .setDbKey("MODULE_B")
       .setName("Module B");
-    dbClient.componentDao().insert(dbTester.getSession(), moduleA, moduleB);
+    dbClient.componentDao().insert(db.getSession(), moduleA, moduleB);
     ComponentDto directory = ComponentTesting.newDirectory(moduleB, "src/main/java/dir").setUuid("CDEF").setDbKey("MODULE_B:src/main/java/dir");
     ComponentDto file = ComponentTesting.newFileDto(moduleB, directory, "DEFG").setPath("src/main/java/dir/Foo.java").setName("Foo.java")
       .setDbKey("MODULE_B:src/main/java/dir/Foo.java");
-    dbClient.componentDao().insert(dbTester.getSession(), directory, file);
-    dbTester.getSession().commit();
+    dbClient.componentDao().insert(db.getSession(), directory, file);
+    db.getSession().commit();
 
     treeRootHolder.setRoot(
-      builder(PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY)
+      builder(PROJECT, 1).setUuid(project.uuid()).setKey(project.getDbKey())
         .setName("Project")
         .addChildren(
           builder(Component.Type.MODULE, 2).setUuid("EDCB").setKey("MODULE_A")
@@ -563,14 +641,14 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     underTest.execute();
 
     // commit the functional transaction
-    dbClient.componentDao().applyBChangesForRootComponentUuid(dbTester.getSession(), project.uuid());
-    dbTester.commit();
+    dbClient.componentDao().applyBChangesForRootComponentUuid(db.getSession(), project.uuid());
+    db.commit();
 
-    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(5);
+    assertThat(db.countRowsOfTable("projects")).isEqualTo(5);
 
-    ComponentDto moduleAreloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_A").get();
+    ComponentDto moduleAreloaded = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_A").get();
 
-    ComponentDto moduleBReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_B").get();
+    ComponentDto moduleBReloaded = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_B").get();
     assertThat(moduleBReloaded).isNotNull();
     assertThat(moduleBReloaded.uuid()).isEqualTo(moduleB.uuid());
     assertThat(moduleBReloaded.getUuidPath()).isEqualTo(moduleBReloaded.getUuidPath());
@@ -579,7 +657,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(moduleBReloaded.projectUuid()).isEqualTo(project.uuid());
     assertThat(moduleBReloaded.getRootUuid()).isEqualTo(project.uuid());
 
-    ComponentDto directoryReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_B:src/main/java/dir").get();
+    ComponentDto directoryReloaded = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_B:src/main/java/dir").get();
     assertThat(directoryReloaded).isNotNull();
     assertThat(directoryReloaded.uuid()).isEqualTo(directory.uuid());
     assertThat(directoryReloaded.getUuidPath()).isEqualTo(directoryReloaded.getUuidPath());
@@ -588,7 +666,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(directoryReloaded.projectUuid()).isEqualTo(project.uuid());
     assertThat(directoryReloaded.getRootUuid()).isEqualTo(moduleBReloaded.uuid());
 
-    ComponentDto fileReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_B:src/main/java/dir/Foo.java").get();
+    ComponentDto fileReloaded = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_B:src/main/java/dir/Foo.java").get();
     assertThat(fileReloaded).isNotNull();
     assertThat(fileReloaded.uuid()).isEqualTo(file.uuid());
     assertThat(fileReloaded.getUuidPath()).isEqualTo(fileReloaded.getUuidPath());
@@ -601,31 +679,29 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
   @Test
   public void do_not_update_created_at_on_existing_component() {
     Date oldDate = DateUtils.parseDate("2015-01-01");
-    ComponentDto project = newPrivateProjectDto(dbTester.getDefaultOrganization(), "ABCD").setDbKey(PROJECT_KEY).setName("Project").setCreatedAt(oldDate);
-    dbClient.componentDao().insert(dbTester.getSession(), project);
+    ComponentDto project = prepareProject(p -> p.setCreatedAt(oldDate));
     ComponentDto module = ComponentTesting.newModuleDto("BCDE", project).setDbKey(MODULE_KEY).setName("Module").setPath("path").setCreatedAt(oldDate);
-    dbClient.componentDao().insert(dbTester.getSession(), module);
-    dbTester.getSession().commit();
+    dbClient.componentDao().insert(db.getSession(), module);
+    db.getSession().commit();
 
     treeRootHolder.setRoot(
-      builder(PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY)
+      builder(PROJECT, 1).setUuid(project.uuid()).setKey(project.getDbKey())
         .build());
 
     underTest.execute();
 
-    Optional<ComponentDto> projectReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), PROJECT_KEY);
+    Optional<ComponentDto> projectReloaded = dbClient.componentDao().selectByUuid(db.getSession(), project.uuid());
     assertThat(projectReloaded.get().getCreatedAt()).isNotEqualTo(now);
   }
 
   @Test
   public void persist_components_that_were_previously_removed() {
-    ComponentDto project = newPrivateProjectDto(dbTester.organizations().insert(), "ABCD").setDbKey(PROJECT_KEY).setName("Project");
-    dbClient.componentDao().insert(dbTester.getSession(), project);
+    ComponentDto project = prepareProject();
     ComponentDto removedModule = ComponentTesting.newModuleDto("BCDE", project)
       .setDbKey(MODULE_KEY)
       .setName("Module")
       .setEnabled(false);
-    dbClient.componentDao().insert(dbTester.getSession(), removedModule);
+    dbClient.componentDao().insert(db.getSession(), removedModule);
     ComponentDto removedDirectory = ComponentTesting.newDirectory(removedModule, "src/main/java/dir")
       .setUuid("CDEF")
       .setDbKey("MODULE_KEY:src/main/java/dir")
@@ -635,11 +711,11 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
       .setName("Foo.java")
       .setDbKey("MODULE_KEY:src/main/java/dir/Foo.java")
       .setEnabled(false);
-    dbClient.componentDao().insert(dbTester.getSession(), removedDirectory, removedFile);
-    dbTester.getSession().commit();
+    dbClient.componentDao().insert(db.getSession(), removedDirectory, removedFile);
+    db.getSession().commit();
 
     treeRootHolder.setRoot(
-      builder(PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY)
+      builder(PROJECT, 1).setUuid(project.uuid()).setKey(project.getDbKey())
         .setName("Project")
         .addChildren(
           builder(Component.Type.MODULE, 2).setUuid("BCDE").setKey(MODULE_KEY)
@@ -657,17 +733,17 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
     underTest.execute();
 
-    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4);
-    assertThat(dbClient.componentDao().selectByKey(dbTester.getSession(), PROJECT_KEY).get().getId()).isEqualTo(project.getId());
-    assertThat(dbClient.componentDao().selectByKey(dbTester.getSession(), MODULE_KEY).get().getId()).isEqualTo(removedModule.getId());
-    assertThat(dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir").get().getId()).isEqualTo(removedDirectory.getId());
-    assertThat(dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get().getId()).isEqualTo(removedFile.getId());
+    assertThat(db.countRowsOfTable("projects")).isEqualTo(4);
+    assertThat(dbClient.componentDao().selectByKey(db.getSession(), project.getDbKey()).get().getId()).isEqualTo(project.getId());
+    assertThat(dbClient.componentDao().selectByKey(db.getSession(), MODULE_KEY).get().getId()).isEqualTo(removedModule.getId());
+    assertThat(dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir").get().getId()).isEqualTo(removedDirectory.getId());
+    assertThat(dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get().getId()).isEqualTo(removedFile.getId());
     assertExistButDisabled(removedModule.getDbKey(), removedDirectory.getDbKey(), removedFile.getDbKey());
 
     // commit the functional transaction
-    dbClient.componentDao().applyBChangesForRootComponentUuid(dbTester.getSession(), project.uuid());
+    dbClient.componentDao().applyBChangesForRootComponentUuid(db.getSession(), project.uuid());
 
-    ComponentDto projectReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), PROJECT_KEY).get();
+    ComponentDto projectReloaded = dbClient.componentDao().selectByKey(db.getSession(), project.getDbKey()).get();
     assertThat(projectReloaded.getId()).isEqualTo(project.getId());
     assertThat(projectReloaded.uuid()).isEqualTo(project.uuid());
     assertThat(projectReloaded.getUuidPath()).isEqualTo(project.getUuidPath());
@@ -677,7 +753,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(projectReloaded.getRootUuid()).isEqualTo(project.getRootUuid());
     assertThat(projectReloaded.isEnabled()).isTrue();
 
-    ComponentDto moduleReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), MODULE_KEY).get();
+    ComponentDto moduleReloaded = dbClient.componentDao().selectByKey(db.getSession(), MODULE_KEY).get();
     assertThat(moduleReloaded.getId()).isEqualTo(removedModule.getId());
     assertThat(moduleReloaded.uuid()).isEqualTo(removedModule.uuid());
     assertThat(moduleReloaded.getUuidPath()).isEqualTo(removedModule.getUuidPath());
@@ -687,7 +763,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(moduleReloaded.getRootUuid()).isEqualTo(removedModule.getRootUuid());
     assertThat(moduleReloaded.isEnabled()).isTrue();
 
-    ComponentDto directoryReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir").get();
+    ComponentDto directoryReloaded = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir").get();
     assertThat(directoryReloaded.getId()).isEqualTo(removedDirectory.getId());
     assertThat(directoryReloaded.uuid()).isEqualTo(removedDirectory.uuid());
     assertThat(directoryReloaded.getUuidPath()).isEqualTo(removedDirectory.getUuidPath());
@@ -699,7 +775,7 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     assertThat(directoryReloaded.path()).isEqualTo(removedDirectory.path());
     assertThat(directoryReloaded.isEnabled()).isTrue();
 
-    ComponentDto fileReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get();
+    ComponentDto fileReloaded = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get();
     assertThat(fileReloaded.getId()).isEqualTo(fileReloaded.getId());
     assertThat(fileReloaded.uuid()).isEqualTo(removedFile.uuid());
     assertThat(fileReloaded.getUuidPath()).isEqualTo(removedFile.getUuidPath());
@@ -714,27 +790,26 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
   private void assertExistButDisabled(String... keys) {
     for (String key : keys) {
-      ComponentDto dto = dbClient.componentDao().selectByKey(dbTester.getSession(), key).get();
+      ComponentDto dto = dbClient.componentDao().selectByKey(db.getSession(), key).get();
       assertThat(dto.isEnabled()).isFalse();
     }
   }
 
   @Test
   public void update_module_uuid_when_reactivating_removed_component() {
-    ComponentDto project = newPrivateProjectDto(dbTester.getDefaultOrganization(), "ABCD").setDbKey(PROJECT_KEY).setName("Project");
-    dbClient.componentDao().insert(dbTester.getSession(), project);
+    ComponentDto project = prepareProject();
     ComponentDto module = ComponentTesting.newModuleDto("BCDE", project).setDbKey(MODULE_KEY).setName("Module");
     ComponentDto removedModule = ComponentTesting.newModuleDto("EDCD", project).setDbKey("REMOVED_MODULE_KEY").setName("Removed Module").setEnabled(false);
-    dbClient.componentDao().insert(dbTester.getSession(), module, removedModule);
+    dbClient.componentDao().insert(db.getSession(), module, removedModule);
     ComponentDto directory = ComponentTesting.newDirectory(module, "src/main/java/dir").setUuid("CDEF").setDbKey("MODULE_KEY:src/main/java/dir");
     // The file was attached to another module
     ComponentDto removedFile = ComponentTesting.newFileDto(removedModule, directory, "DEFG").setPath("src/main/java/dir/Foo.java").setName("Foo.java")
       .setDbKey("MODULE_KEY:src/main/java/dir/Foo.java").setEnabled(false);
-    dbClient.componentDao().insert(dbTester.getSession(), directory, removedFile);
-    dbTester.getSession().commit();
+    dbClient.componentDao().insert(db.getSession(), directory, removedFile);
+    db.getSession().commit();
 
     treeRootHolder.setRoot(
-      builder(PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY)
+      builder(PROJECT, 1).setUuid(project.uuid()).setKey(project.getDbKey())
         .setName("Project")
         .addChildren(
           builder(Component.Type.MODULE, 2).setUuid("BCDE").setKey(MODULE_KEY)
@@ -753,15 +828,15 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
     underTest.execute();
 
     // commit the functional transaction
-    dbClient.componentDao().applyBChangesForRootComponentUuid(dbTester.getSession(), project.uuid());
-    dbTester.commit();
+    dbClient.componentDao().applyBChangesForRootComponentUuid(db.getSession(), project.uuid());
+    db.commit();
 
     // Projects contains 4 components from the report + one removed module
-    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(5);
+    assertThat(db.countRowsOfTable("projects")).isEqualTo(5);
 
-    ComponentDto moduleReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), MODULE_KEY).get();
+    ComponentDto moduleReloaded = dbClient.componentDao().selectByKey(db.getSession(), MODULE_KEY).get();
 
-    ComponentDto fileReloaded = dbClient.componentDao().selectByKey(dbTester.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get();
+    ComponentDto fileReloaded = dbClient.componentDao().selectByKey(db.getSession(), "MODULE_KEY:src/main/java/dir/Foo.java").get();
     assertThat(fileReloaded.getId()).isEqualTo(removedFile.getId());
     assertThat(fileReloaded.uuid()).isEqualTo(removedFile.uuid());
     assertThat(fileReloaded.getUuidPath()).isEqualTo(fileReloaded.getUuidPath());
@@ -775,21 +850,18 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
   @Test
   public void persists_existing_components_with_visibility_of_root_in_db_out_of_functional_transaction() {
-    boolean isRootPrivate = new Random().nextBoolean();
-    OrganizationDto organization = dbTester.organizations().insert();
-    ComponentDto project = newPrivateProjectDto(organization, "ABCD").setDbKey(PROJECT_KEY).setName("Project").setPrivate(isRootPrivate);
-    dbTester.components().insertComponent(project);
-    ComponentDto module = newModuleDto(project).setUuid("BCDE").setDbKey("MODULE").setPrivate(!isRootPrivate);
-    dbTester.components().insertComponent(module);
-    dbTester.components().insertComponent(newDirectory(module, "DEFG", "Directory").setDbKey("DIR").setPrivate(isRootPrivate));
+    ComponentDto project = prepareProject(p -> p.setPrivate(true));
+    ComponentDto module = newModuleDto(project).setPrivate(false);
+    db.components().insertComponent(module);
+    ComponentDto dir = db.components().insertComponent(newDirectory(module, "DEFG", "Directory").setDbKey("DIR").setPrivate(true));
     treeRootHolder.setRoot(createSampleProjectComponentTree(project));
 
     underTest.execute();
 
-    Stream.of("ABCD", "BCDE", "BCDE", "BCDE")
-      .forEach(uuid -> assertThat(dbClient.componentDao().selectByUuid(dbTester.getSession(), uuid).get().isPrivate())
+    Stream.of(project.uuid(), module.uuid(), dir.uuid())
+      .forEach(uuid -> assertThat(dbClient.componentDao().selectByUuid(db.getSession(), uuid).get().isPrivate())
         .describedAs("for uuid " + uuid)
-        .isEqualTo(isRootPrivate));
+        .isEqualTo(true));
   }
 
   private ReportComponent createSampleProjectComponentTree(ComponentDto project) {
@@ -813,4 +885,59 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
           .build())
       .build();
   }
+
+  private ReportComponent.Builder asTreeRoot(ComponentDto project) {
+    return builder(PROJECT, 1).setUuid(project.uuid()).setKey(project.getDbKey()).setName(project.name());
+  }
+
+  private ComponentDto prepareProject(Consumer<ComponentDto>... populators) {
+    ComponentDto dto = db.components().insertPrivateProject(db.organizations().insert(), populators);
+    analysisMetadataHolder.setProject(Project.copyOf(dto));
+    analysisMetadataHolder.setBranch(new MainBranchImpl(null));
+    return dto;
+  }
+
+  private ComponentDto prepareBranch(String branchName, Consumer<ComponentDto>... populators) {
+    ComponentDto dto = db.components().insertPrivateProject(db.organizations().insert(), populators);
+    analysisMetadataHolder.setProject(Project.copyOf(dto));
+    analysisMetadataHolder.setBranch(new BranchImpl(branchName));
+    return dto;
+  }
+
+  private static class BranchImpl implements Branch {
+    private final String name;
+
+    public BranchImpl(String name) {
+      this.name = name;
+    }
+
+    @Override
+    public BranchType getType() {
+      return BranchType.LONG;
+    }
+
+    @Override
+    public boolean isMain() {
+      return false;
+    }
+
+    @Override
+    public java.util.Optional<String> getName() {
+      return java.util.Optional.ofNullable(name);
+    }
+
+    @Override
+    public boolean supportsCrossProjectCpd() {
+      return false;
+    }
+
+    @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 7f91d63cfbd24665af90794ff2c56c8de6a9368e..aadb8186fe90e7655aceab3470d7799f30e04008 100644 (file)
@@ -40,6 +40,8 @@ import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType;
 import org.sonar.server.computation.task.projectanalysis.analysis.Analysis;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
+import org.sonar.server.computation.task.projectanalysis.component.MainBranchImpl;
 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.ReportComponent;
@@ -51,7 +53,7 @@ public class ValidateProjectStepTest {
   static long DEFAULT_ANALYSIS_TIME = 1433131200000L; // 2015-06-01
   static final String PROJECT_KEY = "PROJECT_KEY";
   static final String MODULE_KEY = "MODULE_KEY";
-  static final String DEFAULT_BRANCH = "origin/master";
+  static final Branch DEFAULT_BRANCH = new MainBranchImpl(null);
 
   @Rule
   public DbTester dbTester = DbTester.create(System2.INSTANCE);
@@ -97,36 +99,6 @@ public class ValidateProjectStepTest {
     underTest.execute();
   }
 
-  @Test
-  public void not_fail_on_valid_branch() {
-    analysisMetadataHolder.setBranch(DEFAULT_BRANCH);
-    reportReader.putComponent(ScannerReport.Component.newBuilder()
-      .setRef(1)
-      .setType(ComponentType.PROJECT)
-      .setKey(PROJECT_KEY)
-      .build());
-    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY + ":origin/master").build());
-
-    underTest.execute();
-  }
-
-  @Test
-  public void fail_on_invalid_branch() {
-    analysisMetadataHolder.setBranch("bran#ch");
-    reportReader.putComponent(ScannerReport.Component.newBuilder()
-      .setRef(1)
-      .setType(ComponentType.PROJECT)
-      .setKey(PROJECT_KEY)
-      .build());
-    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY + ":bran#ch").build());
-
-    thrown.expect(MessageException.class);
-    thrown.expectMessage("Validation of project failed:\n" +
-      "  o \"bran#ch\" is not a valid branch name. Allowed characters are alphanumeric, '-', '_', '.' and '/'.");
-
-    underTest.execute();
-  }
-
   @Test
   public void fail_on_invalid_key() {
     String invalidProjectKey = "Project\\Key";
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/VerifyBillingStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/VerifyBillingStepTest.java
new file mode 100644 (file)
index 0000000..5007d2d
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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.step;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.utils.MessageException;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule;
+import org.sonar.server.computation.task.projectanalysis.analysis.Organization;
+import org.sonar.server.organization.BillingValidations;
+import org.sonar.server.organization.BillingValidations.BillingValidationsException;
+import org.sonar.server.organization.BillingValidationsProxy;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
+
+public class VerifyBillingStepTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private OrganizationDto organization = newOrganizationDto();
+  @Rule
+  public MutableAnalysisMetadataHolderRule analysisMetadata = new MutableAnalysisMetadataHolderRule()
+    .setOrganization(Organization.from(organization));
+
+  private BillingValidationsProxy validations = mock(BillingValidationsProxy.class);
+
+  @Test
+  public void execute_fails_with_MessageException_when_organization_is_not_allowed_to_execute_analysis() {
+    doThrow(new BillingValidationsException("This organization cannot execute project analysis"))
+      .when(validations)
+      .checkOnProjectAnalysis(any(BillingValidations.Organization.class));
+
+    VerifyBillingStep underTest = new VerifyBillingStep(analysisMetadata, validations);
+
+    expectedException.expect(MessageException.class);
+    expectedException.expectMessage("This organization cannot execute project analysis");
+
+    underTest.execute();
+
+  }
+
+  @Test
+  public void execute_does_no_fail_when_organization_is_allowed_to_execute_analysis() {
+    ArgumentCaptor<BillingValidations.Organization> orgCaptor = ArgumentCaptor.forClass(BillingValidations.Organization.class);
+
+    VerifyBillingStep underTest = new VerifyBillingStep(analysisMetadata, validations);
+    underTest.execute();
+
+    verify(validations).checkOnProjectAnalysis(orgCaptor.capture());
+    BillingValidations.Organization calledOrg = orgCaptor.getValue();
+    assertThat(calledOrg.getKey()).isEqualTo(organization.getKey());
+  }
+
+}
index 4cd7f3f54be1d299340fd84f619c61bddc2223fa..2c755957dbac52946db2e2f339855f088ec5b501 100644 (file)
@@ -37,6 +37,8 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.server.computation.task.projectanalysis.component.BranchPersisterDelegate;
+import org.sonar.server.computation.task.projectanalysis.component.MainBranchImpl;
 import org.sonar.server.computation.task.projectanalysis.component.MutableDbIdsRepositoryRule;
 import org.sonar.server.computation.task.projectanalysis.component.MutableDisabledComponentsHolder;
 import org.sonar.server.computation.task.projectanalysis.component.ProjectViewAttributes;
@@ -93,6 +95,7 @@ public class ViewsPersistComponentsStepTest extends BaseStepTest {
   private ComponentDbTester componentDbTester = new ComponentDbTester(dbTester);
   private MutableDisabledComponentsHolder disabledComponentsHolder = mock(MutableDisabledComponentsHolder.class, RETURNS_DEEP_STUBS);
   private PersistComponentsStep underTest;
+  private BranchPersisterDelegate branchPersister;
 
   @Before
   public void setup() throws Exception {
@@ -100,7 +103,8 @@ public class ViewsPersistComponentsStepTest extends BaseStepTest {
     when(system2.now()).thenReturn(now.getTime());
 
     dbTester.organizations().insertForUuid(ORGANIZATION_UUID);
-    underTest = new PersistComponentsStep(dbClient, treeRootHolder, dbIdsRepository, system2, disabledComponentsHolder, analysisMetadataHolder);
+    analysisMetadataHolder.setBranch(new MainBranchImpl(null));
+    underTest = new PersistComponentsStep(dbClient, treeRootHolder, dbIdsRepository, system2, disabledComponentsHolder, analysisMetadataHolder, branchPersister);
   }
 
   @Override
index c630e803d16b10c7e425c09bf965149e06bcbd03..044825700f11d15de4d9ad6e750381d8aa6ed262 100644 (file)
@@ -32,7 +32,6 @@ import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
 import org.sonar.server.computation.task.projectanalysis.component.TestSettingsRepository;
-import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.any;
@@ -43,7 +42,6 @@ import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newCeTaskBuilder;
 import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newProjectBuilder;
 import static org.sonar.api.ce.posttask.PostProjectAnalysisTaskTester.newScannerContextBuilder;
-import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.DUMB_PROJECT;
 
 public class WebhookPostTaskTest {
 
@@ -53,9 +51,6 @@ public class WebhookPostTaskTest {
   @Rule
   public LogTester logTester = new LogTester().setLevel(LoggerLevel.DEBUG);
 
-  @Rule
-  public TreeRootHolderRule rootHolder = new TreeRootHolderRule().setRoot(DUMB_PROJECT);
-
   private final MapSettings settings = new MapSettings();
   private final TestWebhookCaller caller = new TestWebhookCaller();
   private final WebhookPayloadFactory payloadFactory = new TestWebhookPayloadFactory();
@@ -131,7 +126,7 @@ public class WebhookPostTaskTest {
 
   private void execute() {
     ConfigurationRepository settingsRepository = new TestSettingsRepository(settings.asConfig());
-    WebhookPostTask task = new WebhookPostTask(rootHolder, settingsRepository, payloadFactory, caller, deliveryStorage);
+    WebhookPostTask task = new WebhookPostTask(settingsRepository, payloadFactory, caller, deliveryStorage);
 
     PostProjectAnalysisTaskTester.of(task)
       .at(new Date())
index 6b673dfadd53536d1e8c857f04f5b3efffc9f262..2b7a933daaddb799fbc527eadcd42fc650c53937 100644 (file)
@@ -451,7 +451,7 @@ public class PermissionTemplateServiceTest {
   }
 
   @Test
-  public void would_user_have_scann_permission_with_empty_template() {
+  public void would_user_have_scan_permission_with_empty_template() {
     PermissionTemplateDto template = templateDb.insertTemplate(dbTester.getDefaultOrganization());
     dbTester.organizations().setDefaultTemplates(template, null);
 
@@ -459,7 +459,7 @@ public class PermissionTemplateServiceTest {
   }
 
   private void checkWouldUserHaveScanPermission(OrganizationDto organization, @Nullable Integer userId, boolean expectedResult) {
-    assertThat(underTest.wouldUserHaveScanPermissionWithDefaultTemplate(session, organization.getUuid(), userId, null, "PROJECT_KEY", Qualifiers.PROJECT))
+    assertThat(underTest.wouldUserHaveScanPermissionWithDefaultTemplate(session, organization.getUuid(), userId, "PROJECT_KEY", Qualifiers.PROJECT))
       .isEqualTo(expectedResult);
   }
 
index dd594cda0990cdf22804feebaa47833c5ba46223..49170c2652a977d7758ddd0ee10874954640b2ca 100644 (file)
@@ -30,6 +30,6 @@ public class ProjectsWsModuleTest {
   public void verify_count_of_added_components() {
     ComponentContainer container = new ComponentContainer();
     new ProjectsWsModule().configure(container);
-    assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 14);
+    assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 15);
   }
 }
index 599a898e273a346277fd8257ce16ab22acfd35a8..562a5e7cf7aa8ac90a1bc832287200f70ddfe1dc 100644 (file)
@@ -19,9 +19,7 @@
  */
 package org.sonar.core.config;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.List;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.PropertyType;
@@ -29,6 +27,7 @@ import org.sonar.api.config.EmailSettings;
 import org.sonar.api.config.PropertyDefinition;
 import org.sonar.api.resources.Qualifiers;
 
+import static java.util.Arrays.asList;
 import static org.sonar.api.PropertyType.BOOLEAN;
 import static org.sonar.api.database.DatabaseProperties.PROP_PASSWORD;
 
@@ -54,7 +53,7 @@ public class CorePropertyDefinitions {
   }
 
   public static List<PropertyDefinition> all() {
-    List<PropertyDefinition> defs = Lists.newArrayList();
+    List<PropertyDefinition> defs = new ArrayList<>();
     defs.addAll(IssueExclusionProperties.all());
     defs.addAll(ExclusionProperties.all());
     defs.addAll(SecurityProperties.all());
@@ -63,8 +62,9 @@ public class CorePropertyDefinitions {
     defs.addAll(EmailSettings.definitions());
     defs.addAll(WebhookProperties.all());
     defs.addAll(TelemetryProperties.all());
+    defs.addAll(ScannerProperties.all());
 
-    defs.addAll(ImmutableList.of(
+    defs.addAll(asList(
       PropertyDefinition.builder(PROP_PASSWORD)
         .type(PropertyType.PASSWORD)
         .hidden()
@@ -102,14 +102,6 @@ public class CorePropertyDefinitions {
         .category(CoreProperties.CATEGORY_GENERAL)
         .hidden()
         .build(),
-      PropertyDefinition.builder(CoreProperties.ANALYSIS_MODE)
-        .name("Analysis mode")
-        .type(PropertyType.SINGLE_SELECT_LIST)
-        .options(Arrays.asList(CoreProperties.ANALYSIS_MODE_ANALYSIS, CoreProperties.ANALYSIS_MODE_PREVIEW, CoreProperties.ANALYSIS_MODE_INCREMENTAL))
-        .category(CoreProperties.CATEGORY_GENERAL)
-        .defaultValue(CoreProperties.ANALYSIS_MODE_ANALYSIS)
-        .hidden()
-        .build(),
       PropertyDefinition.builder(CoreProperties.PREVIEW_INCLUDE_PLUGINS)
         .name("Plugins accepted for Preview mode")
         .description("List of plugin keys. Those plugins will be used during preview analyses.")
@@ -148,20 +140,6 @@ public class CorePropertyDefinitions {
         .defaultValue(String.valueOf(false))
         .hidden()
         .build(),
-      PropertyDefinition.builder(CoreProperties.SCM_DISABLED_KEY)
-        .name("Disable the SCM Sensor")
-        .description("Disable the retrieval of blame information from Source Control Manager")
-        .category(CoreProperties.CATEGORY_SCM)
-        .type(BOOLEAN)
-        .onQualifiers(Qualifiers.PROJECT)
-        .defaultValue(String.valueOf(false))
-        .build(),
-      PropertyDefinition.builder(CoreProperties.SCM_PROVIDER_KEY)
-        .name("Key of the SCM provider for this project")
-        .description("Force the provider to be used to get SCM information for this project. By default auto-detection is done. Example: svn, git.")
-        .category(CoreProperties.CATEGORY_SCM)
-        .onlyOnQualifiers(Qualifiers.PROJECT)
-        .build(),
       PropertyDefinition.builder(DISABLE_NOTIFICATION_ON_BUILT_IN_QPROFILES)
         .name("Avoid quality profiles notification")
         .description("Avoid sending email notification on each update of built-in quality profiles to quality profile administrators.")
index d56cbe687f0dc889cbbcdcac31bb24359aab1ffd..89a210879e10c532dbe52fa68c1a7e105d146079 100644 (file)
  */
 package org.sonar.core.config;
 
-import java.util.Arrays;
 import java.util.List;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.PropertyType;
 import org.sonar.api.config.PropertyDefinition;
 import org.sonar.api.resources.Qualifiers;
 
+import static java.util.Arrays.asList;
+
 public final class PurgeProperties {
 
   private PurgeProperties() {
   }
 
   public static List<PropertyDefinition> all() {
-    return Arrays.asList(
+    return asList(
       PropertyDefinition.builder(PurgeConstants.PROPERTY_CLEAN_DIRECTORY)
         .defaultValue("true")
         .name("Clean directory/package history")
diff --git a/sonar-core/src/main/java/org/sonar/core/config/ScannerProperties.java b/sonar-core/src/main/java/org/sonar/core/config/ScannerProperties.java
new file mode 100644 (file)
index 0000000..e7abb66
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.core.config;
+
+import java.util.List;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.PropertyType;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.resources.Qualifiers;
+
+import static java.util.Arrays.asList;
+import static org.sonar.api.PropertyType.BOOLEAN;
+
+public class ScannerProperties {
+
+  public static final String BRANCH_NAME = "sonar.branch.name";
+  public static final String ORGANIZATION = "sonar.organization";
+
+  private ScannerProperties() {
+    // only static stuff
+  }
+
+  public static List<PropertyDefinition> all() {
+    return asList(
+      PropertyDefinition.builder(CoreProperties.ANALYSIS_MODE)
+        .name("Analysis mode")
+        .type(PropertyType.SINGLE_SELECT_LIST)
+        .options(asList(CoreProperties.ANALYSIS_MODE_ANALYSIS, CoreProperties.ANALYSIS_MODE_PREVIEW, CoreProperties.ANALYSIS_MODE_INCREMENTAL))
+        .category(CoreProperties.CATEGORY_GENERAL)
+        .defaultValue(CoreProperties.ANALYSIS_MODE_ANALYSIS)
+        .hidden()
+        .build(),
+      PropertyDefinition.builder(CoreProperties.SCM_DISABLED_KEY)
+        .name("Disable the SCM Sensor")
+        .description("Disable the retrieval of blame information from Source Control Manager")
+        .category(CoreProperties.CATEGORY_SCM)
+        .type(BOOLEAN)
+        .onQualifiers(Qualifiers.PROJECT)
+        .defaultValue(String.valueOf(false))
+        .build(),
+      PropertyDefinition.builder(CoreProperties.SCM_PROVIDER_KEY)
+        .name("Key of the SCM provider for this project")
+        .description("Force the provider to be used to get SCM information for this project. By default auto-detection is done. Example: svn, git.")
+        .category(CoreProperties.CATEGORY_SCM)
+        .onlyOnQualifiers(Qualifiers.PROJECT)
+        .build(),
+      PropertyDefinition.builder(ORGANIZATION)
+        .name("Organization key")
+        .description("Key of the organization that contains the project being analyzed. If unset, then the organization marked as default is used.")
+        .hidden()
+        .build(),
+      PropertyDefinition.builder(BRANCH_NAME)
+        .name("Optional name of SCM branch")
+        .description("TODO")
+        .hidden()
+        .build()
+      );
+  }
+}
index 5f79551fd1e183ea976318617d4505e61eb9424b..91fb4c141e8ce6ec19788651982e0c2d0e494f42 100644 (file)
  */
 package org.sonar.core.config;
 
-import com.google.common.collect.ImmutableList;
 import java.util.List;
 import org.sonar.api.PropertyType;
 import org.sonar.api.config.PropertyDefinition;
 import org.sonar.api.config.PropertyFieldDefinition;
 import org.sonar.api.resources.Qualifiers;
 
+import static java.util.Arrays.asList;
+
 public class WebhookProperties {
 
   public static final String GLOBAL_KEY = "sonar.webhooks.global";
@@ -56,7 +57,7 @@ public class WebhookProperties {
   }
 
   static List<PropertyDefinition> all() {
-    return ImmutableList.of(
+    return asList(
       PropertyDefinition.builder(GLOBAL_KEY)
         .category(CATEGORY)
         .name("Webhooks")
index 2ba2427e8d639aafae51e3aab88d17ce5b2103bc..b084ba686b07ada5947065630d9f79ff3753e7e4 100644 (file)
@@ -55,7 +55,7 @@ public class PluginLoader {
    * Defines the base keys (defined by {@link #basePluginKey(PluginInfo, Map)}) of the plugins which are allowed to
    * run a full server extensions.
    */
-  private static final Set<String> PRIVILEGED_PLUGINS_BASE_KEYS = ImmutableSet.of("views", "devcockpit", "governance", "billing", "developer", "incremental");
+  private static final Set<String> PRIVILEGED_PLUGINS_BASE_KEYS = ImmutableSet.of("views", "devcockpit", "governance", "billing", "developer", "incremental", "branch");
 
   public static final Version COMPATIBILITY_MODE_MAX_VERSION = Version.create("5.2");
 
index 22b04b1f5e47075558d8be17c1bb2ede0a74b491..897227b77f62a6d832fae335c4a0e8d38925a076 100644 (file)
@@ -33,7 +33,7 @@ public class CorePropertyDefinitionsTest {
   @Test
   public void all() {
     List<PropertyDefinition> defs = CorePropertyDefinitions.all();
-    assertThat(defs).hasSize(61);
+    assertThat(defs).hasSize(63);
   }
 
   @Test
@@ -44,4 +44,12 @@ public class CorePropertyDefinitionsTest {
     assertThat(prop.get().type()).isEqualTo(PropertyType.PASSWORD);
   }
 
+  @Test
+  public void all_includes_scanner_properties() {
+    List<PropertyDefinition> defs = CorePropertyDefinitions.all();
+
+    assertThat(defs.stream()
+      .filter(def -> def.key().equals(ScannerProperties.BRANCH_NAME))
+      .findFirst()).isPresent();
+  }
 }
index 5400fcb441188a8ab96f8b7e1bf52adb86bf9a7a..a93bdd3538159d8d030e1cfb88c1dae5dcb726b2 100644 (file)
@@ -126,11 +126,6 @@ public interface CoreProperties {
   String PROJECT_BRANCH_PROPERTY = "sonar.branch";
   String PROJECT_VERSION_PROPERTY = "sonar.projectVersion";
 
-  /**
-   * @since 6.3
-   */
-  String PROJECT_ORGANIZATION_PROPERTY = "sonar.organization";
-
   /**
    * @since 2.6
    */
index 41786c272da3400106d747a4eaaee169a7603137..680876ef33d4fc39c4daa1188082b890413c7eb8 100644 (file)
@@ -77,14 +77,14 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver
     if (!container.getComponentByType(AnalysisMode.class).isIssues()) {
       Metadata readMetadata = getReportReader().readMetadata();
       int rootComponentRef = readMetadata.getRootComponentRef();
-      storeReportComponents(rootComponentRef, null, readMetadata.getBranch());
+      storeReportComponents(rootComponentRef, null);
     }
 
     storeFs(container);
 
   }
 
-  private void storeReportComponents(int componentRef, String parentModuleKey, String branch) {
+  private void storeReportComponents(int componentRef, String parentModuleKey) {
     Component component = getReportReader().readComponent(componentRef);
     if (isNotEmpty(component.getKey())) {
       reportComponents.put(component.getKey(), component);
@@ -92,7 +92,7 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver
       reportComponents.put(parentModuleKey + ":" + component.getPath(), component);
     }
     for (int childId : component.getChildRefList()) {
-      storeReportComponents(childId, isNotEmpty(component.getKey()) ? component.getKey() : parentModuleKey, branch);
+      storeReportComponents(childId, isNotEmpty(component.getKey()) ? component.getKey() : parentModuleKey);
     }
 
   }
@@ -174,7 +174,7 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver
    * @param lineOffset 0-based offset in file
    */
   public List<TypeOfText> highlightingTypeFor(InputFile file, int line, int lineOffset) {
-    int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
+    int ref = reportComponents.get(file.key()).getRef();
     if (!reader.hasSyntaxHighlighting(ref)) {
       return Collections.emptyList();
     }
@@ -205,7 +205,7 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver
    */
   @CheckForNull
   public List<ScannerReport.TextRange> symbolReferencesFor(InputFile file, int symbolStartLine, int symbolStartLineOffset) {
-    int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
+    int ref = reportComponents.get(file.key()).getRef();
     try (CloseableIterator<Symbol> symbols = getReportReader().readComponentSymbols(ref)) {
       while (symbols.hasNext()) {
         Symbol symbol = symbols.next();
@@ -219,7 +219,7 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver
 
   public List<ScannerReport.Duplication> duplicationsFor(InputFile file) {
     List<ScannerReport.Duplication> result = new ArrayList<>();
-    int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
+    int ref = reportComponents.get(file.key()).getRef();
     try (CloseableIterator<ScannerReport.Duplication> it = getReportReader().readComponentDuplications(ref)) {
       while (it.hasNext()) {
         result.add(it.next());
@@ -232,7 +232,7 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver
 
   public List<ScannerReport.CpdTextBlock> duplicationBlocksFor(InputFile file) {
     List<ScannerReport.CpdTextBlock> result = new ArrayList<>();
-    int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
+    int ref = reportComponents.get(file.key()).getRef();
     try (CloseableIterator<ScannerReport.CpdTextBlock> it = getReportReader().readCpdTextBlocks(ref)) {
       while (it.hasNext()) {
         result.add(it.next());
@@ -245,7 +245,7 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver
 
   @CheckForNull
   public ScannerReport.LineCoverage coverageFor(InputFile file, int line) {
-    int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
+    int ref = reportComponents.get(file.key()).getRef();
     try (CloseableIterator<ScannerReport.LineCoverage> it = getReportReader().readComponentCoverage(ref)) {
       while (it.hasNext()) {
         ScannerReport.LineCoverage coverage = it.next();
@@ -260,7 +260,7 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver
   }
 
   public ScannerReport.Test firstTestExecutionForName(InputFile testFile, String testName) {
-    int ref = reportComponents.get(((DefaultInputFile) testFile).key()).getRef();
+    int ref = reportComponents.get(testFile.key()).getRef();
     try (InputStream inputStream = FileUtils.openInputStream(getReportReader().readTests(ref))) {
       ScannerReport.Test test = ScannerReport.Test.parser().parseDelimitedFrom(inputStream);
       while (test != null) {
@@ -276,7 +276,7 @@ public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver
   }
 
   public ScannerReport.CoverageDetail coveragePerTestFor(InputFile testFile, String testName) {
-    int ref = reportComponents.get(((DefaultInputFile) testFile).key()).getRef();
+    int ref = reportComponents.get(testFile.key()).getRef();
     try (InputStream inputStream = FileUtils.openInputStream(getReportReader().readCoverageDetails(ref))) {
       ScannerReport.CoverageDetail details = ScannerReport.CoverageDetail.parser().parseDelimitedFrom(inputStream);
       while (details != null) {
index 15f0a0cfcf8f777669372bbd843dfb83455715a7..258ac5ada0891562847d63f456c6439d6caad424 100644 (file)
@@ -20,8 +20,8 @@
 package org.sonar.scanner.report;
 
 import java.util.Map.Entry;
-import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.AnalysisMode;
+import java.util.Optional;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
@@ -35,6 +35,9 @@ import org.sonar.scanner.protocol.output.ScannerReportWriter;
 import org.sonar.scanner.rule.ModuleQProfiles;
 import org.sonar.scanner.rule.QProfile;
 
+import static org.sonar.core.config.ScannerProperties.BRANCH_NAME;
+import static org.sonar.core.config.ScannerProperties.ORGANIZATION;
+
 public class MetadataPublisher implements ReportPublisherStep {
 
   private final Configuration settings;
@@ -68,12 +71,10 @@ public class MetadataPublisher implements ReportPublisherStep {
       .setRootComponentRef(rootProject.batchId())
       .setIncremental(mode.isIncremental());
 
-    settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).ifPresent(builder::setOrganizationKey);
+    settings.get(ORGANIZATION).ifPresent(builder::setOrganizationKey);
+    settings.get(BRANCH_NAME).ifPresent(builder::setBranchName);
+    Optional.ofNullable(rootDef.getBranch()).ifPresent(builder::setDeprecatedBranch);
 
-    String branch = rootDef.getBranch();
-    if (branch != null) {
-      builder.setBranch(branch);
-    }
     for (QProfile qp : qProfiles.findAll()) {
       builder.getMutableQprofilesPerLanguage().put(qp.getLanguage(), ScannerReport.Metadata.QProfile.newBuilder()
         .setKey(qp.getKey())
index 0815ffdf81d78b3b7c531586c667e3714805690a..f58536c26548cfb1abb1e7e345d3b0f0272707ec 100644 (file)
@@ -35,7 +35,6 @@ import javax.annotation.Nullable;
 import okhttp3.HttpUrl;
 import org.apache.commons.io.FileUtils;
 import org.picocontainer.Startable;
-import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.ScannerSide;
 import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
 import org.sonar.api.config.Configuration;
@@ -54,6 +53,7 @@ import org.sonarqube.ws.client.HttpException;
 import org.sonarqube.ws.client.PostRequest;
 import org.sonarqube.ws.client.WsResponse;
 
+import static org.sonar.core.config.ScannerProperties.ORGANIZATION;
 import static org.sonar.core.util.FileUtils.deleteQuietly;
 
 @ScannerSide
@@ -167,7 +167,7 @@ public class ReportPublisher implements Startable {
     PostRequest.Part filePart = new PostRequest.Part(MediaTypes.ZIP, report);
     PostRequest post = new PostRequest("api/ce/submit")
       .setMediaType(MediaTypes.PROTOBUF)
-      .setParam("organization", settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).orElse(null))
+      .setParam("organization", settings.get(ORGANIZATION).orElse(null))
       .setParam("projectKey", moduleHierarchy.root().key())
       .setParam("projectName", moduleHierarchy.root().getOriginalName())
       .setParam("projectBranch", moduleHierarchy.root().getBranch())
@@ -204,7 +204,7 @@ public class ReportPublisher implements Startable {
 
       Map<String, String> metadata = new LinkedHashMap<>();
       String effectiveKey = moduleHierarchy.root().getKeyWithBranch();
-      settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).ifPresent(org -> metadata.put("organization", org));
+      settings.get(ORGANIZATION).ifPresent(org -> metadata.put("organization", org));
       metadata.put("projectKey", effectiveKey);
       metadata.put("serverUrl", publicUrl);
       metadata.put("serverVersion", server.getVersion());
index eb86e53237a82515f7be148f9eaf3c8eb6c3bab6..779174757d492d47808e695d718aaa46cbbb3330 100644 (file)
@@ -29,7 +29,6 @@ import java.util.Optional;
 import java.util.function.BinaryOperator;
 import javax.annotation.Nullable;
 import org.apache.commons.io.IOUtils;
-import org.sonar.api.CoreProperties;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.utils.MessageException;
 import org.sonar.scanner.bootstrap.ScannerWsClient;
@@ -39,6 +38,7 @@ import org.sonarqube.ws.client.GetRequest;
 
 import static java.util.function.Function.identity;
 import static java.util.stream.Collectors.toMap;
+import static org.sonar.core.config.ScannerProperties.ORGANIZATION;
 import static org.sonar.scanner.util.ScannerUtils.encodeForUrl;
 
 public class DefaultQualityProfileLoader implements QualityProfileLoader {
@@ -82,7 +82,7 @@ public class DefaultQualityProfileLoader implements QualityProfileLoader {
   }
 
   private Optional<String> getOrganizationKey() {
-    return settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY);
+    return settings.get(ORGANIZATION);
   }
 
   private Map<String, QualityProfile> call(String url) {
index 8761acc1e5c821eeb22e667c7d2b115b24491e35..88020f9eb5d2dcfa14ac40d90763db35df02fe73 100644 (file)
@@ -95,7 +95,7 @@ public class BranchMediumTest {
     DefaultInputFile inputfile = (DefaultInputFile) result.inputFile("src/sample.xoo");
     assertThat(result.getReportReader().readComponent(inputfile.batchId()).getPath()).isEqualTo("src/sample.xoo");
 
-    assertThat(result.getReportReader().readMetadata().getBranch()).isEqualTo("branch");
+    assertThat(result.getReportReader().readMetadata().getDeprecatedBranch()).isEqualTo("branch");
 
     result = tester.newTask()
       .properties(ImmutableMap.<String, String>builder()
@@ -134,7 +134,7 @@ public class BranchMediumTest {
     // no branch in InputModule's key or in report
     assertThat(result.getReportComponent("com.foo.project:moduleA").getKey()).isEqualTo("com.foo.project:moduleA");
 
-    assertThat(result.getReportReader().readMetadata().getBranch()).isEqualTo("branch");
+    assertThat(result.getReportReader().readMetadata().getDeprecatedBranch()).isEqualTo("branch");
 
     result = tester.newTask()
       .properties(ImmutableMap.<String, String>builder()
index a949364d429ba4ad4631ba03c17875210b9817c1..3a4051ece54559d217ce4ee486af6ce8c7d56972 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
 import org.sonar.api.config.internal.MapSettings;
+import org.sonar.core.config.ScannerProperties;
 import org.sonar.scanner.ProjectAnalysisInfo;
 import org.sonar.scanner.bootstrap.ScannerPlugin;
 import org.sonar.scanner.bootstrap.ScannerPluginRepository;
@@ -141,13 +142,13 @@ public class MetadataPublisherTest {
     ScannerReport.Metadata metadata = reader.readMetadata();
     assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L);
     assertThat(metadata.getProjectKey()).isEqualTo("foo");
-    assertThat(metadata.getBranch()).isEqualTo("myBranch");
+    assertThat(metadata.getDeprecatedBranch()).isEqualTo("myBranch");
     assertThat(metadata.getCrossProjectDuplicationActivated()).isFalse();
   }
 
   @Test
   public void write_project_organization() throws Exception {
-    settings.setProperty(CoreProperties.PROJECT_ORGANIZATION_PROPERTY, "SonarSource");
+    settings.setProperty(ScannerProperties.ORGANIZATION, "SonarSource");
 
     File outputDir = temp.newFolder();
     ScannerReportWriter writer = new ScannerReportWriter(outputDir);
index ae65b6d01b751cd2bebfe77e7e0d1840eb0c585f..9138d06f8c452f4223d1163f2978eefd74b8f5bf 100644 (file)
@@ -32,7 +32,6 @@ import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
-import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
@@ -44,6 +43,7 @@ import org.sonar.api.utils.TempFolder;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.core.config.CorePropertyDefinitions;
+import org.sonar.core.config.ScannerProperties;
 import org.sonar.scanner.analysis.DefaultAnalysisMode;
 import org.sonar.scanner.bootstrap.ScannerWsClient;
 import org.sonarqube.ws.WsCe;
@@ -91,7 +91,7 @@ public class ReportPublisherTest {
   public void log_and_dump_information_about_report_uploading() throws IOException {
     ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class),
       new ReportPublisherStep[0]);
-    settings.setProperty(CoreProperties.PROJECT_ORGANIZATION_PROPERTY, "MyOrg");
+    settings.setProperty(ScannerProperties.ORGANIZATION, "MyOrg");
 
     underTest.logSuccess("TASK-123");
 
@@ -202,7 +202,7 @@ public class ReportPublisherTest {
     ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class),
       new ReportPublisherStep[0]);
 
-    settings.setProperty(CoreProperties.PROJECT_ORGANIZATION_PROPERTY, "MyOrg");
+    settings.setProperty(ScannerProperties.ORGANIZATION, "MyOrg");
 
     WsResponse response = mock(WsResponse.class);
 
@@ -232,7 +232,7 @@ public class ReportPublisherTest {
       new ReportPublisherStep[0]);
     
     when(mode.isIncremental()).thenReturn(true);
-    settings.setProperty(CoreProperties.PROJECT_ORGANIZATION_PROPERTY, "MyOrg");
+    settings.setProperty(ScannerProperties.ORGANIZATION, "MyOrg");
     
     WsResponse response = mock(WsResponse.class);
 
index ed5accc8d00160aac973a6b532061290ebae744f..cc200fe05bc6b28aa4982d815bce3fba612e1029 100644 (file)
@@ -239,7 +239,7 @@ public class ScannerReportViewerApp {
   }
 
   private void updateTitle() {
-    frame.setTitle(metadata.getProjectKey() + (StringUtils.isNotEmpty(metadata.getBranch()) ? (" (" + metadata.getBranch() + ")") : "") + " "
+    frame.setTitle(metadata.getProjectKey() + (StringUtils.isNotEmpty(metadata.getBranchName()) ? (" (" + metadata.getBranchName() + ")") : "") + " "
       + sdf.format(new Date(metadata.getAnalysisDate())));
   }
 
index 4cbecc332410af815f70184c0019527a565fc661..5affc43944627b37dcea1c22d69ef89342f24085 100644 (file)
@@ -33,12 +33,14 @@ message Metadata {
   string organization_key = 2;
   // TODO should we keep this project_key here or not ? Because it's a duplication of Component.key
   string project_key = 3;
-  string branch = 4;
+  // maps the property sonar.branch
+  string deprecated_branch = 4;
   int32 root_component_ref = 5;
   bool cross_project_duplication_activated = 6;
   map<string, QProfile> qprofiles_per_language = 7;
   map<string, Plugin> plugins_by_key = 8;
   bool incremental = 9;
+  string branch_name = 10;
 
   message QProfile {
     string key = 1;
index 8668ffc355a34fd08683c6da23ea5a19267e644c..069db2f37f7ba8e8d940a6dedd9b72de32788372 100644 (file)
@@ -84,3 +84,24 @@ message BulkUpdateKeyWsResponse {
     optional bool duplicate = 3;
   }
 }
+
+// WS api/projects/branches
+message BranchesWsResponse {
+  repeated Branch branches = 1;
+
+  message Branch {
+    optional string name = 1;
+    optional bool isMain = 2;
+    optional BranchType type = 3;
+  }
+
+  enum BranchType {
+    // Zero is required in order to not get MAINTAINABILITY as default value
+    // See http://androiddevblog.com/protocol-buffers-pitfall-adding-enum-values/
+    UNKNOWN = 0;
+
+    LONG = 1;
+    SHORT = 2;
+  }
+
+}
diff --git a/tests/projects/shared/xoo-multi-module-sample-without-project-name-version/.scannerwork/.sonar_lock b/tests/projects/shared/xoo-multi-module-sample-without-project-name-version/.scannerwork/.sonar_lock
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/projects/shared/xoo-multi-module-sample-without-project-name-version/.scannerwork/report-task.txt b/tests/projects/shared/xoo-multi-module-sample-without-project-name-version/.scannerwork/report-task.txt
new file mode 100644 (file)
index 0000000..abc19fd
--- /dev/null
@@ -0,0 +1,6 @@
+projectKey=com.sonarsource.it.samples:multi-modules-sample
+serverUrl=http://localhost:9000
+serverVersion=6.6-SNAPSHOT
+dashboardUrl=http://localhost:9000/dashboard/index/com.sonarsource.it.samples:multi-modules-sample
+ceTaskId=AV2ZBK0ixJXLis3IozUo
+ceTaskUrl=http://localhost:9000/api/ce/task?id=AV2ZBK0ixJXLis3IozUo