Browse Source

SONAR-9616 compute engine backend to support branches

tags/6.6-RC1
Simon Brandhof 6 years ago
parent
commit
7515f738fb
100 changed files with 3252 additions and 1415 deletions
  1. 1
    1
      server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
  2. 1
    1
      server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
  3. 2
    1
      server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
  4. 8
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
  5. 24
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
  6. 48
    6
      server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java
  7. 7
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
  8. 30
    3
      server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java
  9. 1
    9
      server/sonar-db-dao/src/main/java/org/sonar/db/property/PropertiesDao.java
  10. 1
    1
      server/sonar-db-dao/src/main/java/org/sonar/db/property/PropertiesMapper.java
  11. 28
    1
      server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
  12. 1
    6
      server/sonar-db-dao/src/main/resources/org/sonar/db/property/PropertiesMapper.xml
  13. 175
    0
      server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java
  14. 45
    0
      server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDtoTest.java
  15. 13
    13
      server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
  16. 0
    21
      server/sonar-db-dao/src/test/java/org/sonar/db/property/PropertiesDaoTest.java
  17. 0
    1
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/AddBranchColumnToProjectsTable.java
  18. 4
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/CreateTableProjectBranches.java
  19. 1
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v66/CreateTableProjectBranchesTest.java
  20. 5
    4
      server/sonar-server/src/main/java/org/sonar/server/computation/queue/ReportSubmitter.java
  21. 21
    2
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolder.java
  22. 19
    4
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java
  23. 45
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Branch.java
  24. 6
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java
  25. 91
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Project.java
  26. 2
    7
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/api/measurecomputer/MeasureComputerContextImpl.java
  27. 57
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoader.java
  28. 30
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoaderDelegate.java
  29. 30
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterDelegate.java
  30. 3
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/Component.java
  31. 30
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentKeyGenerator.java
  32. 204
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilder.java
  33. 8
    10
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentUuidFactory.java
  34. 3
    7
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ConfigurationRepository.java
  35. 11
    21
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ConfigurationRepositoryImpl.java
  36. 82
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MainBranchImpl.java
  37. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/TreeRootHolder.java
  38. 3
    2
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
  39. 7
    4
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/duplication/CrossProjectDuplicationStatusHolderImpl.java
  40. 2
    5
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultAssignee.java
  41. 2
    3
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/filter/IssueFilter.java
  42. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitygate/QualityGateServiceImpl.java
  43. 57
    56
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/BuildComponentTreeStep.java
  44. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadPeriodsStep.java
  45. 5
    24
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStep.java
  46. 65
    35
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java
  47. 145
    108
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistComponentsStep.java
  48. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PurgeDatastoresStep.java
  49. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStep.java
  50. 1
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java
  51. 1
    12
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ValidateProjectStep.java
  52. 57
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/VerifyBillingStep.java
  53. 3
    6
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTask.java
  54. 5
    2
      server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsProxyImpl.java
  55. 2
    5
      server/sonar-server/src/main/java/org/sonar/server/permission/PermissionTemplateService.java
  56. 85
    0
      server/sonar-server/src/main/java/org/sonar/server/project/ws/BranchesAction.java
  57. 1
    0
      server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsModule.java
  58. 10
    11
      server/sonar-server/src/test/java/org/sonar/server/computation/queue/ReportSubmitterTest.java
  59. 3
    3
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisImplTest.java
  60. 32
    12
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java
  61. 19
    4
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java
  62. 17
    4
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java
  63. 65
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/ProjectTest.java
  64. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/api/measurecomputer/MeasureComputerContextImplTest.java
  65. 107
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoaderTest.java
  66. 1
    6
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentFunctionsTest.java
  67. 0
    545
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentRootBuilderTest.java
  68. 735
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilderTest.java
  69. 57
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentUuidFactoryTest.java
  70. 47
    32
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ConfigurationRepositoryTest.java
  71. 92
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/MainBranchImplTest.java
  72. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/TestSettingsRepository.java
  73. 14
    7
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/duplication/CrossProjectDuplicationStatusHolderImplTest.java
  74. 1
    6
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultAssigneeTest.java
  75. 10
    10
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/filter/IssueFilterTest.java
  76. 24
    24
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/BuildComponentTreeStepTest.java
  77. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadPeriodsStepTest.java
  78. 11
    28
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStepTest.java
  79. 41
    92
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java
  80. 3
    1
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistComponentsStepTest.java
  81. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PurgeDatastoresStepTest.java
  82. 264
    137
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportPersistComponentsStepTest.java
  83. 3
    31
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ValidateProjectStepTest.java
  84. 80
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/VerifyBillingStepTest.java
  85. 5
    1
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsPersistComponentsStepTest.java
  86. 1
    6
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTaskTest.java
  87. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/permission/PermissionTemplateServiceTest.java
  88. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java
  89. 5
    27
      sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
  90. 3
    2
      sonar-core/src/main/java/org/sonar/core/config/PurgeProperties.java
  91. 76
    0
      sonar-core/src/main/java/org/sonar/core/config/ScannerProperties.java
  92. 3
    2
      sonar-core/src/main/java/org/sonar/core/config/WebhookProperties.java
  93. 1
    1
      sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java
  94. 9
    1
      sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java
  95. 0
    5
      sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
  96. 10
    10
      sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java
  97. 7
    6
      sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java
  98. 3
    3
      sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java
  99. 2
    2
      sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java
  100. 0
    0
      sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java

+ 1
- 1
server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java View 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(

+ 1
- 1
server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java View 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",

+ 2
- 1
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl View 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");

+ 8
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java View 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);
}

+ 24
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java View 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);
}

+ 48
- 6
server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java View File

@@ -19,19 +19,26 @@
*/
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;
}
}

+ 7
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java View File

@@ -19,10 +19,17 @@
*/
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);
}

+ 30
- 3
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java View 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;
}
}

+ 1
- 9
server/sonar-db-dao/src/main/java/org/sonar/db/property/PropertiesDao.java View 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));
}

+ 1
- 1
server/sonar-db-dao/src/main/java/org/sonar/db/property/PropertiesMapper.java View 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);


+ 28
- 1
server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml View 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},
@@ -30,8 +31,34 @@
#{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>

+ 1
- 6
server/sonar-db-dao/src/main/resources/org/sonar/db/property/PropertiesMapper.xml View File

@@ -123,12 +123,7 @@
<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>

+ 175
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java View File

@@ -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();
}

}

+ 45
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDtoTest.java View File

@@ -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();
}
}

+ 13
- 13
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java View 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));

+ 0
- 21
server/sonar-db-dao/src/test/java/org/sonar/db/property/PropertiesDaoTest.java View 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();

+ 0
- 1
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/AddBranchColumnToProjectsTable.java View 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;

+ 4
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v66/CreateTableProjectBranches.java View 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()
);
}

+ 1
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v66/CreateTableProjectBranchesTest.java View 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");
}


+ 5
- 4
server/sonar-server/src/main/java/org/sonar/server/computation/queue/ReportSubmitter.java View 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();

+ 21
- 2
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolder.java View 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

+ 19
- 4
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java View 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

+ 45
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Branch.java View File

@@ -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();
}

+ 6
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java View 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

+ 91
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Project.java View File

@@ -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());
}
}

+ 2
- 7
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/api/measurecomputer/MeasureComputerContextImpl.java View 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) {

+ 57
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoader.java View File

@@ -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));
}
}
}

+ 30
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoaderDelegate.java View File

@@ -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);

}

+ 30
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterDelegate.java View File

@@ -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);

}

+ 3
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/Component.java View 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();


+ 30
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentKeyGenerator.java View File

@@ -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);

}

+ 204
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilder.java View File

@@ -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);
}
}
}

server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/UuidFactory.java → server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ComponentUuidFactory.java View File

@@ -27,23 +27,21 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;

public class UuidFactory {
public class ComponentUuidFactory {

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());
}
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, else generate a new one
* Get UUID from database if it exists, otherwise generate a new one.
*/
public String getOrCreateForKey(String key) {
String uuid = uuidsByKey.get(key);
return (uuid == null) ? Uuids.create() : uuid;
return uuidsByKey.computeIfAbsent(key, k -> Uuids.create());
}
}

+ 3
- 7
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ConfigurationRepository.java View 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();

}

+ 11
- 21
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ConfigurationRepositoryImpl.java View File

@@ -19,38 +19,28 @@
*/
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();
}

}

+ 82
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MainBranchImpl.java View File

@@ -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()));
}
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/TreeRootHolder.java View 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)
*/

+ 3
- 2
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java View 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

+ 7
- 4
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/duplication/CrossProjectDuplicationStatusHolderImpl.java View File

@@ -19,11 +19,13 @@
*/
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");

+ 2
- 5
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultAssignee.java View 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;
}

+ 2
- 3
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/filter/IssueFilter.java View 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);
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitygate/QualityGateServiceImpl.java View 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;
}

+ 57
- 56
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/BuildComponentTreeStep.java View File

@@ -19,10 +19,11 @@
*/
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()));
}
}
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadPeriodsStep.java View 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) {

+ 5
- 24
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStep.java View File

@@ -20,22 +20,16 @@
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;
}

+ 65
- 35
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java View 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();
}

+ 145
- 108
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistComponentsStep.java View 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) {

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PurgeDatastoresStep.java View 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();
}
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStep.java View 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;
}

+ 1
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java View File

@@ -37,6 +37,7 @@ public class ReportComputationSteps extends AbstractComputationSteps {

// Builds Component tree
LoadReportAnalysisMetadataHolderStep.class,
VerifyBillingStep.class,
BuildComponentTreeStep.class,
ValidateProjectStep.class,


+ 1
- 12
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ValidateProjectStep.java View 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) {

+ 57
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/VerifyBillingStep.java View File

@@ -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";
}
}

+ 3
- 6
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTask.java View 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),

+ 5
- 2
server/sonar-server/src/main/java/org/sonar/server/organization/BillingValidationsProxyImpl.java View File

@@ -20,12 +20,15 @@

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

+ 2
- 5
server/sonar-server/src/main/java/org/sonar/server/permission/PermissionTemplateService.java View 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;

+ 85
- 0
server/sonar-server/src/main/java/org/sonar/server/project/ws/BranchesAction.java View File

@@ -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();
}
}

+ 1
- 0
server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsModule.java View File

@@ -29,6 +29,7 @@ public class ProjectsWsModule extends Module {
ProjectsWs.class,
CreateAction.class,
IndexAction.class,
BranchesAction.class,
BulkDeleteAction.class,
DeleteAction.class,
UpdateKeyAction.class,

+ 10
- 11
server/sonar-server/src/test/java/org/sonar/server/computation/queue/ReportSubmitterTest.java View 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}"));


+ 3
- 3
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisImplTest.java View 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 {

+ 32
- 12
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java View 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

+ 19
- 4
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java View 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

+ 17
- 4
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java View 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);

+ 65
- 0
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/ProjectTest.java View File

@@ -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();
}
}

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/api/measurecomputer/MeasureComputerContextImplTest.java View 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");

+ 107
- 0
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/BranchLoaderTest.java View File

@@ -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);
}
}
}

+ 1
- 6
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentFunctionsTest.java View 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() {

+ 0
- 545
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentRootBuilderTest.java View File

@@ -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;
}
}

+ 735
- 0
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentTreeBuilderTest.java View File

@@ -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;
}
}

+ 57
- 0
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ComponentUuidFactoryTest.java View File

@@ -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);
}

}

+ 47
- 32
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ConfigurationRepositoryTest.java View File

@@ -19,19 +19,17 @@
*/
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()));
}
}

+ 92
- 0
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/MainBranchImplTest.java View File

@@ -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);
}
}

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/TestSettingsRepository.java View File

@@ -34,7 +34,7 @@ public class TestSettingsRepository implements ConfigurationRepository {
}

@Override
public Configuration getConfiguration(Component component) {
public Configuration getConfiguration() {
return config;
}
}

+ 14
- 7
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/duplication/CrossProjectDuplicationStatusHolderImplTest.java View 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;
}
}

+ 1
- 6
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultAssigneeTest.java View 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 {

+ 10
- 10
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/filter/IssueFilterTest.java View 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) {

+ 24
- 24
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/BuildComponentTreeStepTest.java View 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() {

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadPeriodsStepTest.java View 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

server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateLoadingStepTest.java → server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStepTest.java View File

@@ -25,12 +25,7 @@ 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;
@@ -42,57 +37,46 @@ 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();
public class LoadQualityGateStepTest {

@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);
private LoadQualityGateStep underTest = new LoadQualityGateStep(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());
when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().asConfig());

underTest.execute();

verifyNoQualityGate();

// verify only project is processed
verify(settingsRepository).getConfiguration(root);
verify(settingsRepository).getConfiguration();
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")));
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(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());
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() {
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());
when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().setProperty("sonar.qualitygate", 10).asConfig());
when(qualityGateService.findById(10)).thenReturn(Optional.absent());

underTest.execute();

@@ -101,10 +85,9 @@ public class QualityGateLoadingStepTest {

@Test
public void execute_sets_QualityGate_if_it_can_be_found_by_service() {
QualityGate qualityGate = new QualityGate(465, "name", Collections.<Condition>emptyList());
QualityGate qualityGate = new QualityGate(465, "name", Collections.emptyList());

treeRootHolder.setRoot(PROJECT_ALONE);
when(settingsRepository.getConfiguration(PROJECT_ALONE)).thenReturn(new MapSettings().setProperty("sonar.qualitygate", 10).asConfig());
when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().setProperty("sonar.qualitygate", 10).asConfig());
when(qualityGateService.findById(10)).thenReturn(Optional.of(qualityGate));

underTest.execute();

+ 41
- 92
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java View 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() {

+ 3
- 1
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistComponentsStepTest.java View 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();
}
}

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PurgeDatastoresStepTest.java View 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();

+ 264
- 137
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportPersistComponentsStepTest.java View 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()));
}
}
}

+ 3
- 31
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ValidateProjectStepTest.java View 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";

+ 80
- 0
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/VerifyBillingStepTest.java View File

@@ -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());
}

}

+ 5
- 1
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsPersistComponentsStepTest.java View 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

+ 1
- 6
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/webhook/WebhookPostTaskTest.java View 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())

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/permission/PermissionTemplateServiceTest.java View 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);
}


+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java View 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);
}
}

+ 5
- 27
sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java View 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.")

+ 3
- 2
sonar-core/src/main/java/org/sonar/core/config/PurgeProperties.java View File

@@ -19,20 +19,21 @@
*/
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")

+ 76
- 0
sonar-core/src/main/java/org/sonar/core/config/ScannerProperties.java View File

@@ -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()
);
}
}

+ 3
- 2
sonar-core/src/main/java/org/sonar/core/config/WebhookProperties.java View File

@@ -19,13 +19,14 @@
*/
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")

+ 1
- 1
sonar-core/src/main/java/org/sonar/core/platform/PluginLoader.java View 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");


+ 9
- 1
sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java View 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();
}
}

+ 0
- 5
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java View 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
*/

+ 10
- 10
sonar-scanner-engine/src/main/java/org/sonar/scanner/mediumtest/TaskResult.java View 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) {

+ 7
- 6
sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java View 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())

+ 3
- 3
sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java View 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());

+ 2
- 2
sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java View 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) {

+ 0
- 0
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save