From 47ebff25c379b9162943121a49278553967f2dfe Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Wed, 16 Jan 2019 14:19:36 +0100 Subject: [PATCH] SONAR-11631 replace Component.version by Metadata.projectVersion in scanner report --- .../component/ComponentTreeBuilder.java | 23 +-- .../step/BuildComponentTreeStep.java | 4 +- .../component/ComponentTreeBuilderTest.java | 53 +++--- .../batch/bootstrap/ProjectDefinition.java | 23 ++- .../fs/internal/AbstractProjectOrModule.java | 13 -- .../batch/bootstrap/ProjectBuilderTest.java | 2 +- .../bootstrap/ProjectDefinitionTest.java | 4 +- .../fs/internal/DefaultInputProjectTest.java | 7 +- ...jectAnalysisInfo.java => ProjectInfo.java} | 32 +++- .../scanner/issue/DefaultFilterableIssue.java | 10 +- .../org/sonar/scanner/issue/IssueFilters.java | 14 +- .../issue/tracking/IssueTransition.java | 12 +- .../scanner/report/ComponentsPublisher.java | 5 - .../scanner/report/MetadataPublisher.java | 21 ++- .../scanner/scan/InputProjectProvider.java | 2 +- .../scanner/scan/ProjectReactorValidator.java | 5 - .../scanner/scan/ProjectScanContainer.java | 4 +- ...ysisInfoTest.java => ProjectInfoTest.java} | 49 +++-- .../issue/DefaultFilterableIssueTest.java | 12 +- .../report/ComponentsPublisherTest.java | 25 ++- .../scanner/report/MetadataPublisherTest.java | 39 +++- .../scan/ProjectReactorValidatorTest.java | 176 +++++++++--------- .../src/main/protobuf/scanner_report.proto | 6 +- 23 files changed, 296 insertions(+), 245 deletions(-) rename sonar-scanner-engine/src/main/java/org/sonar/scanner/{ProjectAnalysisInfo.java => ProjectInfo.java} (75%) rename sonar-scanner-engine/src/test/java/org/sonar/scanner/{ProjectAnalysisInfoTest.java => ProjectInfoTest.java} (64%) diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilder.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilder.java index 22f42cc5610..7a8f1951bb3 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilder.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilder.java @@ -70,6 +70,8 @@ public class ComponentTreeBuilder { private final Branch branch; @Nullable private final SnapshotDto baseAnalysis; + @Nullable + private final String projectVersion; private final IssueRelocationToRoot issueRelocationToRoot; private ScannerReport.Component rootComponent; @@ -81,7 +83,9 @@ public class ComponentTreeBuilder { Function uuidSupplier, Function scannerComponentSupplier, Project project, - Branch branch, @Nullable SnapshotDto baseAnalysis, IssueRelocationToRoot issueRelocationToRoot) { + Branch branch, @Nullable SnapshotDto baseAnalysis, + @Nullable String projectVersion, + IssueRelocationToRoot issueRelocationToRoot) { this.keyGenerator = keyGenerator; this.publicKeyGenerator = publicKeyGenerator; @@ -90,6 +94,7 @@ public class ComponentTreeBuilder { this.project = project; this.branch = branch; this.baseAnalysis = baseAnalysis; + this.projectVersion = projectVersion; this.issueRelocationToRoot = issueRelocationToRoot; } @@ -193,7 +198,7 @@ public class ComponentTreeBuilder { .setDbKey(projectKey) .setKey(projectPublicKey) .setStatus(convertStatus(rootComponent.getStatus())) - .setProjectAttributes(new ProjectAttributes(createProjectVersion(rootComponent))) + .setProjectAttributes(new ProjectAttributes(createProjectVersion())) .setReportAttributes(createAttributesBuilder(rootComponent.getRef(), rootComponent.getProjectRelativePath(), scmBasePath).build()) .addChildren(children); setNameAndDescription(rootComponent, builder); @@ -334,16 +339,12 @@ public class ComponentTreeBuilder { 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; + private String createProjectVersion() { + String cleanedProjectVersion = trimToNull(this.projectVersion); + if (cleanedProjectVersion != null) { + return cleanedProjectVersion; } + // FIXME SONAR-11631 code below applies to the analysisVersion, not the project version, fix it if (baseAnalysis != null) { return firstNonNull(baseAnalysis.getVersion(), DEFAULT_PROJECT_VERSION); } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java index dfe95b5264c..70497549fd0 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java @@ -90,7 +90,9 @@ public class BuildComponentTreeStep implements ComputationStep { reportReader::readComponent, analysisMetadataHolder.getProject(), analysisMetadataHolder.getBranch(), - baseAnalysis, issueRelocationToRoot); + baseAnalysis, + metadata.getProjectVersion(), + issueRelocationToRoot); String relativePathFromScmRoot = metadata.getRelativePathFromScmRoot(); Component reportTreeRoot = builder.buildProject(reportProject, relativePathFromScmRoot); diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java index 28a85493634..cea06e5b41b 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Random; import java.util.function.Function; import javax.annotation.Nullable; import org.junit.Rule; @@ -65,6 +66,8 @@ public class ComponentTreeBuilderTest { private static final Function UUID_SUPPLIER = (componentKey) -> componentKey + "_uuid"; private static final EnumSet REPORT_TYPES = EnumSet.of(PROJECT, MODULE, DIRECTORY, FILE); private static final String NO_SCM_BASE_PATH = ""; + // both no project as "" or null should be supported + private static final String NO_PROJECT_VERSION = new Random().nextBoolean() ? "" : null; private IssueRelocationToRoot issueRelocationToRoot = mock(IssueRelocationToRoot.class); @Rule @@ -95,7 +98,7 @@ public class ComponentTreeBuilderTest { .setProjectRelativePath("src") .setLines(1)); try { - call(project); + call(project, NO_SCM_BASE_PATH, null); fail("Should have thrown a IllegalArgumentException"); } catch (IllegalArgumentException e) { assertThat(e).hasMessage("Unsupported component type '" + type + "'"); @@ -130,8 +133,7 @@ public class ComponentTreeBuilderTest { .setRef(42) .setName(nameInReport) .setDescription(descriptionInReport) - .setVersion("6.5") - .build()); + .build(), NO_SCM_BASE_PATH, "6.5"); assertThat(root.getUuid()).isEqualTo("generated_K1_uuid"); assertThat(root.getDbKey()).isEqualTo("generated_K1"); @@ -149,7 +151,7 @@ public class ComponentTreeBuilderTest { public void project_name_is_loaded_from_db_if_absent_from_report() { Component root = call(newBuilder() .setType(PROJECT) - .build()); + .build(), NO_SCM_BASE_PATH, NO_PROJECT_VERSION); assertThat(root.getName()).isEqualTo(projectInDb.getName()); } @@ -162,7 +164,7 @@ public class ComponentTreeBuilderTest { .setName(reportName) .build(); - Component root = newUnderTest(null, true).buildProject(reportProject, NO_SCM_BASE_PATH); + Component root = newUnderTest(null, true, NO_PROJECT_VERSION).buildProject(reportProject, NO_SCM_BASE_PATH); assertThat(root.getName()).isEqualTo(reportName); } @@ -175,7 +177,8 @@ public class ComponentTreeBuilderTest { .setName(reportName) .build(); - Component root = newUnderTest(null, false).buildProject(reportProject, NO_SCM_BASE_PATH); + Component root = newUnderTest(null, false, null) + .buildProject(reportProject, NO_SCM_BASE_PATH); assertThat(root.getName()).isEqualTo(projectInDb.getName()); } @@ -185,7 +188,7 @@ public class ComponentTreeBuilderTest { SnapshotDto baseAnalysis = new SnapshotDto().setVersion("6.5"); Component root = call(newBuilder() .setType(PROJECT) - .build(), baseAnalysis); + .build(), baseAnalysis, NO_SCM_BASE_PATH, NO_PROJECT_VERSION); assertThat(root.getProjectAttributes().getVersion()).isEqualTo("6.5"); } @@ -195,8 +198,7 @@ public class ComponentTreeBuilderTest { SnapshotDto baseAnalysis = new SnapshotDto().setVersion("6.5"); Component root = call(newBuilder() .setType(PROJECT) - .setVersion("") - .build(), baseAnalysis); + .build(), baseAnalysis, NO_SCM_BASE_PATH, NO_PROJECT_VERSION); assertThat(root.getProjectAttributes().getVersion()).isEqualTo("6.5"); } @@ -205,7 +207,7 @@ public class ComponentTreeBuilderTest { public void project_version_is_hardcoded_if_absent_from_report_and_db() { Component root = call(newBuilder() .setType(PROJECT) - .build()); + .build(), NO_SCM_BASE_PATH, NO_PROJECT_VERSION); assertThat(root.getProjectAttributes().getVersion()).isEqualTo("not provided"); } @@ -214,7 +216,7 @@ public class ComponentTreeBuilderTest { public void project_description_is_loaded_from_db_if_absent_from_report() { Component root = call(newBuilder() .setType(PROJECT) - .build()); + .build(), NO_SCM_BASE_PATH, NO_PROJECT_VERSION); assertThat(root.getDescription()).isEqualTo(projectInDb.getDescription()); } @@ -227,7 +229,7 @@ public class ComponentTreeBuilderTest { .setDescription(reportDescription) .build(); - Component root = newUnderTest(null, true).buildProject(reportProject, NO_SCM_BASE_PATH); + Component root = newUnderTest(null, true, null).buildProject(reportProject, NO_SCM_BASE_PATH); assertThat(root.getDescription()).isEqualTo(reportDescription); } @@ -240,7 +242,7 @@ public class ComponentTreeBuilderTest { .setDescription(reportDescription) .build(); - Component root = newUnderTest(null, false).buildProject(reportProject, NO_SCM_BASE_PATH); + Component root = newUnderTest(null, false, null).buildProject(reportProject, NO_SCM_BASE_PATH); assertThat(root.getDescription()).isEqualTo(projectInDb.getDescription()); } @@ -249,7 +251,7 @@ public class ComponentTreeBuilderTest { public void project_scmPath_is_empty_if_scmBasePath_is_empty() { Component root = call(newBuilder() .setType(PROJECT) - .build(), NO_SCM_BASE_PATH); + .build(), NO_SCM_BASE_PATH, NO_PROJECT_VERSION); assertThat(root.getReportAttributes().getScmPath()).isEmpty(); } @@ -269,7 +271,7 @@ public class ComponentTreeBuilderTest { .setProjectRelativePath("src/js/Foo.js") .setLines(1)); - Component root = call(project, NO_SCM_BASE_PATH); + Component root = call(project, NO_SCM_BASE_PATH, NO_PROJECT_VERSION); assertThat(root.getReportAttributes().getScmPath()) .contains("root"); @@ -286,7 +288,7 @@ public class ComponentTreeBuilderTest { ScannerReport.Component project = createProject(); String scmBasePath = randomAlphabetic(10); - Component root = call(project, scmBasePath); + Component root = call(project, scmBasePath, NO_PROJECT_VERSION); assertThat(root.getReportAttributes().getScmPath()) .contains(scmBasePath); Component directory = root.getChildren().iterator().next(); @@ -901,25 +903,22 @@ public class ComponentTreeBuilderTest { } private Component call(ScannerReport.Component project) { - return call(project, NO_SCM_BASE_PATH); + return call(project, NO_SCM_BASE_PATH, NO_PROJECT_VERSION); } - private Component call(ScannerReport.Component project, String scmBasePath) { - return newUnderTest(null, true).buildProject(project, scmBasePath); + private Component call(ScannerReport.Component project, String scmBasePath, @Nullable String projectVersion) { + return call(project, null, scmBasePath, projectVersion); } - private Component call(ScannerReport.Component project, @Nullable SnapshotDto baseAnalysis) { - return call(project, baseAnalysis, NO_SCM_BASE_PATH); + private Component call(ScannerReport.Component project, @Nullable SnapshotDto baseAnalysis, String scmBasePath, @Nullable String projectVersion) { + return newUnderTest(baseAnalysis, true, projectVersion).buildProject(project, scmBasePath); } - private Component call(ScannerReport.Component project, @Nullable SnapshotDto baseAnalysis, String scmBasePath) { - return newUnderTest(baseAnalysis, true).buildProject(project, scmBasePath); - } - - private ComponentTreeBuilder newUnderTest(@Nullable SnapshotDto baseAnalysis, boolean mainBranch) { + private ComponentTreeBuilder newUnderTest(@Nullable SnapshotDto baseAnalysis, boolean mainBranch, @Nullable String projectVersion) { Branch branch = mock(Branch.class); when(branch.isMain()).thenReturn(mainBranch); - return new ComponentTreeBuilder(KEY_GENERATOR, PUBLIC_KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, projectInDb, branch, baseAnalysis, + return new ComponentTreeBuilder(KEY_GENERATOR, PUBLIC_KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, + projectInDb, branch, baseAnalysis, projectVersion, issueRelocationToRoot); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java index a846b206bc1..cad32b8d980 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java @@ -48,8 +48,6 @@ public class ProjectDefinition { public static final String TESTS_PROPERTY = "sonar.tests"; - public static final String BUILD_DIR_PROPERTY = "sonar.buildDir"; - private static final char SEPARATOR = ','; private File baseDir; @@ -149,7 +147,7 @@ public class ProjectDefinition { return this; } - public ProjectDefinition setVersion(String s) { + public ProjectDefinition setProjectVersion(String s) { properties.put(CoreProperties.PROJECT_VERSION_PROPERTY, StringUtils.defaultString(s)); return this; } @@ -191,12 +189,29 @@ public class ProjectDefinition { return null; } + /** + * @deprecated since 7.7, use {@link #getOriginalProjectVersion()} instead + */ + @Deprecated @CheckForNull public String getOriginalVersion() { - return properties.get(CoreProperties.PROJECT_VERSION_PROPERTY); + return getOriginalProjectVersion(); } + /** + * @deprecated since 7.7, use {@link #getProjectVersion()} instead + */ + @Deprecated public String getVersion() { + return getProjectVersion(); + } + + @CheckForNull + public String getOriginalProjectVersion() { + return properties.get(CoreProperties.PROJECT_VERSION_PROPERTY); + } + + public String getProjectVersion() { String version = properties.get(CoreProperties.PROJECT_VERSION_PROPERTY); if (StringUtils.isBlank(version)) { version = "not provided"; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbstractProjectOrModule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbstractProjectOrModule.java index 8bdb040b0f0..caae97c3494 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbstractProjectOrModule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/AbstractProjectOrModule.java @@ -43,9 +43,7 @@ public abstract class AbstractProjectOrModule extends DefaultInputComponent { private final Path baseDir; private final Path workDir; private final String name; - private final String version; private final String originalName; - private final String originalVersion; private final String description; private final String keyWithBranch; private final String branch; @@ -61,8 +59,6 @@ public abstract class AbstractProjectOrModule extends DefaultInputComponent { this.workDir = initWorkingDir(definition); this.name = definition.getName(); this.originalName = definition.getOriginalName(); - this.version = definition.getVersion(); - this.originalVersion = definition.getOriginalVersion(); this.description = definition.getDescription(); this.keyWithBranch = definition.getKeyWithBranch(); this.branch = definition.getBranch(); @@ -145,15 +141,6 @@ public abstract class AbstractProjectOrModule extends DefaultInputComponent { return properties; } - @CheckForNull - public String getOriginalVersion() { - return originalVersion; - } - - public String getVersion() { - return version; - } - @CheckForNull public String getOriginalName() { return originalName; diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectBuilderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectBuilderTest.java index 2272df4a304..48f3746bd35 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectBuilderTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectBuilderTest.java @@ -66,7 +66,7 @@ public class ProjectBuilderTest { subProject.setBaseDir(baseDir); subProject.setWorkDir(new File(baseDir, "target/.sonar")); subProject.setKey("groupId:parentProjectId"); - subProject.setVersion(root.getOriginalVersion()); + subProject.setProjectVersion(root.getOriginalVersion()); subProject.setName("Sub Project"); subProject.setSources("src"); root.addSubProject(subProject); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectDefinitionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectDefinitionTest.java index 7120ff49d47..a69dad61ada 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectDefinitionTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/bootstrap/ProjectDefinitionTest.java @@ -37,14 +37,14 @@ public class ProjectDefinitionTest { @Test public void shouldSetVersion() { ProjectDefinition def = ProjectDefinition.create(); - def.setVersion("2.0-SNAPSHOT"); + def.setProjectVersion("2.0-SNAPSHOT"); assertThat(def.getVersion()).isEqualTo("2.0-SNAPSHOT"); } @Test public void shouldSupportNoVersion() { ProjectDefinition def = ProjectDefinition.create(); - def.setVersion(null); + def.setProjectVersion(null); assertThat(def.getVersion()).isEqualTo("not provided"); assertThat(def.getOriginalVersion()).isEqualTo(""); } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputProjectTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputProjectTest.java index 33751cd6151..8a4a33bdce3 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputProjectTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputProjectTest.java @@ -42,7 +42,6 @@ public class DefaultInputProjectTest { def.setName("projectName"); File baseDir = temp.newFolder(); def.setBaseDir(baseDir); - def.setVersion("version"); def.setDescription("desc"); File workDir = temp.newFolder(); def.setWorkDir(workDir); @@ -57,13 +56,11 @@ public class DefaultInputProjectTest { assertThat(project.getBranch()).isNull(); assertThat(project.getBaseDir()).isEqualTo(baseDir.toPath()); assertThat(project.getKeyWithBranch()).isEqualTo("projectKey"); - assertThat(project.getVersion()).isEqualTo("version"); - assertThat(project.getOriginalVersion()).isEqualTo("version"); assertThat(project.getDescription()).isEqualTo("desc"); assertThat(project.getWorkDir()).isEqualTo(workDir.toPath()); assertThat(project.getEncoding()).isEqualTo(Charset.defaultCharset()); - assertThat(project.properties()).hasSize(6); + assertThat(project.properties()).hasSize(5); assertThat(project.isFile()).isFalse(); } @@ -75,7 +72,7 @@ public class DefaultInputProjectTest { def.setName("projectName"); File baseDir = temp.newFolder(); def.setBaseDir(baseDir); - def.setVersion("version"); + def.setProjectVersion("version"); def.setDescription("desc"); File workDir = temp.newFolder(); def.setWorkDir(workDir); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectInfo.java similarity index 75% rename from sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java rename to sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectInfo.java index 3cd2928bb72..71c0e77b692 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectInfo.java @@ -22,24 +22,28 @@ package org.sonar.scanner; import java.time.Clock; import java.util.Date; import java.util.Optional; +import javax.annotation.CheckForNull; import org.picocontainer.Startable; import org.sonar.api.CoreProperties; import org.sonar.api.config.Configuration; import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.MessageException; + +import static java.lang.String.format; /** * @since 6.3 - * + * * Immutable after {@link #start()} */ -public class ProjectAnalysisInfo implements Startable { +public class ProjectInfo implements Startable { private final Clock clock; private Configuration settings; private Date analysisDate; - private String analysisVersion; + private String projectVersion; - public ProjectAnalysisInfo(Configuration settings, Clock clock) { + public ProjectInfo(Configuration settings, Clock clock) { this.settings = settings; this.clock = clock; } @@ -48,8 +52,9 @@ public class ProjectAnalysisInfo implements Startable { return analysisDate; } - public String analysisVersion() { - return analysisVersion; + @CheckForNull + public String projectVersion() { + return projectVersion; } private Date loadAnalysisDate() { @@ -71,14 +76,23 @@ public class ProjectAnalysisInfo implements Startable { } } - private String loadAnalysisVersion() { - return settings.get(CoreProperties.PROJECT_VERSION_PROPERTY).orElse(null); + @CheckForNull + private String loadProjectVersion() { + return settings.get(CoreProperties.PROJECT_VERSION_PROPERTY) + .filter(version -> { + if (version.length() > 100) { + throw MessageException.of(format("\"%s\" is not a valid project version. " + + "The maximum length for version numbers is 100 characters.", version)); + } + return true; + }) + .orElse(null); } @Override public void start() { this.analysisDate = loadAnalysisDate(); - this.analysisVersion = loadAnalysisVersion(); + this.projectVersion = loadProjectVersion(); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java index bb92a02f31c..d6c925c6f50 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java @@ -30,19 +30,19 @@ import org.sonar.api.batch.fs.internal.DefaultTextPointer; import org.sonar.api.batch.fs.internal.DefaultTextRange; import org.sonar.api.rule.RuleKey; import org.sonar.api.scan.issue.filter.FilterableIssue; -import org.sonar.scanner.ProjectAnalysisInfo; +import org.sonar.scanner.ProjectInfo; import org.sonar.scanner.protocol.output.ScannerReport.Issue; @ThreadSafe public class DefaultFilterableIssue implements FilterableIssue { private final Issue rawIssue; private final InputComponent component; - private final ProjectAnalysisInfo projectAnalysisInfo; + private final ProjectInfo projectInfo; private DefaultInputProject project; - public DefaultFilterableIssue(DefaultInputProject project, ProjectAnalysisInfo projectAnalysisInfo, Issue rawIssue, InputComponent component) { + public DefaultFilterableIssue(DefaultInputProject project, ProjectInfo projectInfo, Issue rawIssue, InputComponent component) { this.project = project; - this.projectAnalysisInfo = projectAnalysisInfo; + this.projectInfo = projectInfo; this.rawIssue = rawIssue; this.component = component; } @@ -91,7 +91,7 @@ public class DefaultFilterableIssue implements FilterableIssue { @Override public Date creationDate() { - return projectAnalysisInfo.analysisDate(); + return projectInfo.analysisDate(); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java index b4ade123839..2a3e6374dc0 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java @@ -24,7 +24,7 @@ import org.sonar.api.batch.fs.internal.DefaultInputProject; import org.sonar.api.scan.issue.filter.FilterableIssue; import org.sonar.api.scan.issue.filter.IssueFilter; import org.sonar.api.scan.issue.filter.IssueFilterChain; -import org.sonar.scanner.ProjectAnalysisInfo; +import org.sonar.scanner.ProjectInfo; import org.sonar.scanner.protocol.output.ScannerReport; /** @@ -34,20 +34,20 @@ import org.sonar.scanner.protocol.output.ScannerReport; public class IssueFilters { private final IssueFilterChain filterChain; private final DefaultInputProject project; - private final ProjectAnalysisInfo projectAnalysisInfo; + private final ProjectInfo projectInfo; - public IssueFilters(DefaultInputProject project, ProjectAnalysisInfo projectAnalysisInfo, IssueFilter[] exclusionFilters) { + public IssueFilters(DefaultInputProject project, ProjectInfo projectInfo, IssueFilter[] exclusionFilters) { this.project = project; this.filterChain = new DefaultIssueFilterChain(exclusionFilters); - this.projectAnalysisInfo = projectAnalysisInfo; + this.projectInfo = projectInfo; } - public IssueFilters(DefaultInputProject project, ProjectAnalysisInfo projectAnalysisInfo) { - this(project, projectAnalysisInfo, new IssueFilter[0]); + public IssueFilters(DefaultInputProject project, ProjectInfo projectInfo) { + this(project, projectInfo, new IssueFilter[0]); } public boolean accept(InputComponent component, ScannerReport.Issue rawIssue) { - FilterableIssue fIssue = new DefaultFilterableIssue(project, projectAnalysisInfo, rawIssue, component); + FilterableIssue fIssue = new DefaultFilterableIssue(project, projectInfo, rawIssue, component); return filterChain.accept(fIssue); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/IssueTransition.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/IssueTransition.java index 737a4f6614a..fb90c7f5a0e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/IssueTransition.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/IssueTransition.java @@ -28,7 +28,7 @@ import javax.annotation.Nullable; import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.internal.DefaultInputComponent; import org.sonar.core.util.CloseableIterator; -import org.sonar.scanner.ProjectAnalysisInfo; +import org.sonar.scanner.ProjectInfo; import org.sonar.scanner.issue.IssueCache; import org.sonar.scanner.issue.IssueTransformer; import org.sonar.scanner.protocol.output.ScannerReport; @@ -45,17 +45,17 @@ public class IssueTransition { @Nullable private final LocalIssueTracking localIssueTracking; - public IssueTransition(InputComponentStore inputComponentCache, ProjectAnalysisInfo projectAnalysisInfo, IssueCache issueCache, ReportPublisher reportPublisher, - @Nullable LocalIssueTracking localIssueTracking) { + public IssueTransition(InputComponentStore inputComponentCache, ProjectInfo projectInfo, IssueCache issueCache, ReportPublisher reportPublisher, + @Nullable LocalIssueTracking localIssueTracking) { this.inputComponentStore = inputComponentCache; this.issueCache = issueCache; this.reportPublisher = reportPublisher; this.localIssueTracking = localIssueTracking; - this.analysisDate = projectAnalysisInfo.analysisDate(); + this.analysisDate = projectInfo.analysisDate(); } - public IssueTransition(InputComponentStore inputComponentCache, ProjectAnalysisInfo projectAnalysisInfo, IssueCache issueCache, ReportPublisher reportPublisher) { - this(inputComponentCache, projectAnalysisInfo, issueCache, reportPublisher, null); + public IssueTransition(InputComponentStore inputComponentCache, ProjectInfo projectInfo, IssueCache issueCache, ReportPublisher reportPublisher) { + this(inputComponentCache, projectInfo, issueCache, reportPublisher, null); } public void execute() { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java index a9d042489b9..dfd8163cb52 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java @@ -96,11 +96,6 @@ public class ComponentsPublisher implements ReportPublisherStep { projectBuilder.setDescription(description); } - String version = project.getOriginalVersion(); - if (version != null) { - projectBuilder.setVersion(version); - } - writeLinks(project, projectBuilder); return projectBuilder; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java index fbbecab9fa1..2c83efeeb90 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java @@ -23,7 +23,6 @@ import java.io.File; import java.nio.file.Path; import java.util.LinkedList; import java.util.Map.Entry; -import java.util.Optional; import java.util.regex.Pattern; import javax.annotation.Nullable; import org.sonar.api.batch.fs.internal.AbstractProjectOrModule; @@ -32,7 +31,7 @@ import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.batch.scm.ScmProvider; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.scanner.ProjectAnalysisInfo; +import org.sonar.scanner.ProjectInfo; import org.sonar.scanner.bootstrap.ScannerPlugin; import org.sonar.scanner.bootstrap.ScannerPluginRepository; import org.sonar.scanner.cpd.CpdSettings; @@ -45,13 +44,16 @@ import org.sonar.scanner.scan.ScanProperties; import org.sonar.scanner.scan.branch.BranchConfiguration; import org.sonar.scanner.scm.ScmConfiguration; +import static java.util.Optional.ofNullable; +import static org.apache.commons.lang.StringUtils.trimToNull; + public class MetadataPublisher implements ReportPublisherStep { private static final Logger LOG = Loggers.get(MetadataPublisher.class); private final ScanProperties properties; private final QualityProfiles qProfiles; - private final ProjectAnalysisInfo projectAnalysisInfo; + private final ProjectInfo projectInfo; private final InputModuleHierarchy moduleHierarchy; private final CpdSettings cpdSettings; private final ScannerPluginRepository pluginRepository; @@ -60,10 +62,10 @@ public class MetadataPublisher implements ReportPublisherStep { @Nullable private final ScmConfiguration scmConfiguration; - public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, ScanProperties properties, + public MetadataPublisher(ProjectInfo projectInfo, InputModuleHierarchy moduleHierarchy, ScanProperties properties, QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration, @Nullable ScmConfiguration scmConfiguration) { - this.projectAnalysisInfo = projectAnalysisInfo; + this.projectInfo = projectInfo; this.moduleHierarchy = moduleHierarchy; this.properties = properties; this.qProfiles = qProfiles; @@ -73,20 +75,21 @@ public class MetadataPublisher implements ReportPublisherStep { this.scmConfiguration = scmConfiguration; } - public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, ScanProperties properties, + public MetadataPublisher(ProjectInfo projectInfo, InputModuleHierarchy moduleHierarchy, ScanProperties properties, QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration) { - this(projectAnalysisInfo, moduleHierarchy, properties, qProfiles, cpdSettings, pluginRepository, branchConfiguration, null); + this(projectInfo, moduleHierarchy, properties, qProfiles, cpdSettings, pluginRepository, branchConfiguration, null); } @Override public void publish(ScannerReportWriter writer) { AbstractProjectOrModule rootProject = moduleHierarchy.root(); ScannerReport.Metadata.Builder builder = ScannerReport.Metadata.newBuilder() - .setAnalysisDate(projectAnalysisInfo.analysisDate().getTime()) + .setAnalysisDate(projectInfo.analysisDate().getTime()) // Here we want key without branch .setProjectKey(rootProject.key()) .setCrossProjectDuplicationActivated(cpdSettings.isCrossProjectDuplicationEnabled()) .setRootComponentRef(rootProject.scannerId()); + ofNullable(trimToNull(projectInfo.projectVersion())).ifPresent(builder::setProjectVersion); properties.organizationKey().ifPresent(builder::setOrganizationKey); @@ -94,7 +97,7 @@ public class MetadataPublisher implements ReportPublisherStep { addBranchInformation(builder); } - Optional.ofNullable(rootProject.getBranch()).ifPresent(builder::setDeprecatedBranch); + ofNullable(rootProject.getBranch()).ifPresent(builder::setDeprecatedBranch); if (scmConfiguration != null) { addScmInformation(builder); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/InputProjectProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/InputProjectProvider.java index cfb03e5a4b4..08303ac0a0b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/InputProjectProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/InputProjectProvider.java @@ -35,7 +35,7 @@ public class InputProjectProvider extends ProviderAdapter { private DefaultInputProject project = null; public DefaultInputProject provide(ProjectBuildersExecutor projectBuildersExecutor, ProjectReactorValidator validator, - ProjectReactor projectReactor, ScannerComponentIdGenerator scannerComponentIdGenerator) { + ProjectReactor projectReactor, ScannerComponentIdGenerator scannerComponentIdGenerator) { if (project == null) { // 1 Apply project builders projectBuildersExecutor.execute(projectReactor); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java index 5eba211bbca..fb67d7dc0fa 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java @@ -120,11 +120,6 @@ public class ProjectReactorValidator { validationMessages.add(format("\"%s\" is not a valid project or module key. " + "Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", moduleDef.getKey())); } - String originalVersion = moduleDef.getOriginalVersion(); - if (originalVersion != null && originalVersion.length() > 100) { - validationMessages.add(format("\"%s\" is not a valid version name for module \"%s\". " + - "The maximum length for version numbers is 100 characters.", originalVersion, moduleDef.getKey())); - } } private static void validateBranch(List validationMessages, @Nullable String branch) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java index 049d49cdc04..09900d136c1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java @@ -37,7 +37,7 @@ import org.sonar.core.extension.CoreExtensionsInstaller; import org.sonar.core.metric.ScannerMetrics; import org.sonar.core.platform.ComponentContainer; import org.sonar.scanner.DefaultFileLinesContextFactory; -import org.sonar.scanner.ProjectAnalysisInfo; +import org.sonar.scanner.ProjectInfo; import org.sonar.scanner.analysis.AnalysisTempFolderProvider; import org.sonar.scanner.analysis.DefaultAnalysisMode; import org.sonar.scanner.bootstrap.ExtensionInstaller; @@ -167,7 +167,7 @@ public class ProjectScanContainer extends ComponentContainer { ResourceTypes.class, ProjectReactorValidator.class, MetricProvider.class, - ProjectAnalysisInfo.class, + ProjectInfo.class, Storages.class, new RulesProvider(), new BranchConfigurationProvider(), diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/ProjectAnalysisInfoTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/ProjectInfoTest.java similarity index 64% rename from sonar-scanner-engine/src/test/java/org/sonar/scanner/ProjectAnalysisInfoTest.java rename to sonar-scanner-engine/src/test/java/org/sonar/scanner/ProjectInfoTest.java index 4063707181a..8c00cd4597b 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/ProjectAnalysisInfoTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/ProjectInfoTest.java @@ -30,51 +30,64 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.CoreProperties; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.utils.MessageException; +import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; -public class ProjectAnalysisInfoTest { +public class ProjectInfoTest { @Rule public ExpectedException thrown = ExpectedException.none(); + private MapSettings settings = new MapSettings(); + private Clock clock = mock(Clock.class); + private ProjectInfo underTest = new ProjectInfo(settings.asConfig(), clock); + @Test public void testSimpleDateTime() { - MapSettings settings = new MapSettings(); + OffsetDateTime date = OffsetDateTime.of(2017, 1, 1, 12, 13, 14, 0, ZoneOffset.ofHours(2)); settings.appendProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2017-01-01T12:13:14+0200"); settings.appendProperty(CoreProperties.PROJECT_VERSION_PROPERTY, "version"); - Clock clock = mock(Clock.class); - ProjectAnalysisInfo info = new ProjectAnalysisInfo(settings.asConfig(), clock); - info.start(); - OffsetDateTime date = OffsetDateTime.of(2017, 1, 1, 12, 13, 14, 0, ZoneOffset.ofHours(2)); - assertThat(info.analysisDate()).isEqualTo(Date.from(date.toInstant())); - assertThat(info.analysisVersion()).isEqualTo("version"); + underTest.start(); + + assertThat(underTest.analysisDate()).isEqualTo(Date.from(date.toInstant())); + assertThat(underTest.projectVersion()).isEqualTo("version"); } @Test public void testSimpleDate() { - MapSettings settings = new MapSettings(); - settings.appendProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2017-01-01"); - Clock clock = mock(Clock.class); - ProjectAnalysisInfo info = new ProjectAnalysisInfo(settings.asConfig(), clock); - info.start(); LocalDate date = LocalDate.of(2017, 1, 1); + settings.appendProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2017-01-01"); + + underTest.start(); - assertThat(info.analysisDate()).isEqualTo(Date.from(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())); + assertThat(underTest.analysisDate()) + .isEqualTo(Date.from(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())); } @Test public void emptyDate() { - MapSettings settings = new MapSettings(); settings.appendProperty(CoreProperties.PROJECT_DATE_PROPERTY, ""); settings.appendProperty(CoreProperties.PROJECT_VERSION_PROPERTY, "version"); - Clock clock = mock(Clock.class); - ProjectAnalysisInfo info = new ProjectAnalysisInfo(settings.asConfig(), clock); thrown.expect(RuntimeException.class); - info.start(); + underTest.start(); + } + + @Test + public void fail_with_too_long_version() { + String version = randomAlphabetic(101); + settings.appendProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2017-01-01"); + settings.appendProperty(CoreProperties.PROJECT_VERSION_PROPERTY, version); + + thrown.expect(MessageException.class); + thrown.expectMessage("\"" + version +"\" is not a valid project version. " + + "The maximum length for version numbers is 100 characters."); + + underTest.start(); } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java index 240624f63c3..3b8a6ee26d9 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java @@ -24,7 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.internal.DefaultInputProject; -import org.sonar.scanner.ProjectAnalysisInfo; +import org.sonar.scanner.ProjectInfo; import org.sonar.scanner.protocol.Constants.Severity; import org.sonar.scanner.protocol.output.ScannerReport.Issue; import org.sonar.scanner.protocol.output.ScannerReport.TextRange; @@ -36,14 +36,14 @@ import static org.mockito.Mockito.when; public class DefaultFilterableIssueTest { private DefaultFilterableIssue issue; private DefaultInputProject mockedProject; - private ProjectAnalysisInfo projectAnalysisInfo; + private ProjectInfo projectInfo; private InputComponent component; private Issue rawIssue; @Before public void setUp() { mockedProject = mock(DefaultInputProject.class); - projectAnalysisInfo = mock(ProjectAnalysisInfo.class); + projectInfo = mock(ProjectInfo.class); component = mock(InputComponent.class); when(component.key()).thenReturn("foo"); } @@ -70,9 +70,9 @@ public class DefaultFilterableIssueTest { @Test public void testRoundTrip() { rawIssue = createIssue(); - issue = new DefaultFilterableIssue(mockedProject, projectAnalysisInfo, rawIssue, component); + issue = new DefaultFilterableIssue(mockedProject, projectInfo, rawIssue, component); - when(projectAnalysisInfo.analysisDate()).thenReturn(new Date(10_000)); + when(projectInfo.analysisDate()).thenReturn(new Date(10_000)); when(mockedProject.key()).thenReturn("projectKey"); assertThat(issue.componentKey()).isEqualTo(component.key()); @@ -90,7 +90,7 @@ public class DefaultFilterableIssueTest { @Test public void nullValues() { rawIssue = createIssueWithoutFields(); - issue = new DefaultFilterableIssue(mockedProject, projectAnalysisInfo, rawIssue, component); + issue = new DefaultFilterableIssue(mockedProject, projectInfo, rawIssue, component); assertThat(issue.line()).isNull(); assertThat(issue.gap()).isNull(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java index 5f026d2b8eb..b0d47a56931 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java @@ -35,7 +35,7 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputProject; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.utils.DateUtils; -import org.sonar.scanner.ProjectAnalysisInfo; +import org.sonar.scanner.ProjectInfo; import org.sonar.scanner.protocol.output.FileStructure; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.Component; @@ -73,8 +73,8 @@ public class ComponentsPublisherTest { @Test public void add_components_to_report() throws Exception { - ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class); - when(projectAnalysisInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); + ProjectInfo projectInfo = mock(ProjectInfo.class); + when(projectInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); ProjectDefinition rootDef = ProjectDefinition.create() .setKey("foo") @@ -128,7 +128,6 @@ public class ComponentsPublisherTest { Component rootProtobuf = reader.readComponent(1); assertThat(rootProtobuf.getKey()).isEqualTo("foo"); assertThat(rootProtobuf.getDescription()).isEqualTo("Root description"); - assertThat(rootProtobuf.getVersion()).isEqualTo("1.0"); assertThat(rootProtobuf.getLinkCount()).isEqualTo(0); assertThat(reader.readComponent(4).getStatus()).isEqualTo(FileStatus.SAME); @@ -138,8 +137,8 @@ public class ComponentsPublisherTest { @Test public void should_set_modified_name_with_branch() throws IOException { - ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class); - when(projectAnalysisInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); + ProjectInfo projectInfo = mock(ProjectInfo.class); + when(projectInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); ProjectDefinition rootDef = ProjectDefinition.create() .setKey("foo") @@ -162,8 +161,8 @@ public class ComponentsPublisherTest { @Test public void publish_unchanged_components_even_in_short_branches() throws IOException { when(branchConfiguration.isShortOrPullRequest()).thenReturn(true); - ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class); - when(projectAnalysisInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); + ProjectInfo projectInfo = mock(ProjectInfo.class); + when(projectInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); Path baseDir = temp.newFolder().toPath(); ProjectDefinition rootDef = ProjectDefinition.create() @@ -202,8 +201,8 @@ public class ComponentsPublisherTest { @Test public void publish_project_without_version_and_name() throws IOException { - ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class); - when(projectAnalysisInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); + ProjectInfo projectInfo = mock(ProjectInfo.class); + when(projectInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); ProjectDefinition rootDef = ProjectDefinition.create() .setKey("foo") @@ -223,14 +222,13 @@ public class ComponentsPublisherTest { assertThat(rootProtobuf.getKey()).isEqualTo("foo"); assertThat(rootProtobuf.getName()).isEqualTo(""); assertThat(rootProtobuf.getDescription()).isEqualTo("Root description"); - assertThat(rootProtobuf.getVersion()).isEqualTo(""); assertThat(rootProtobuf.getLinkCount()).isEqualTo(0); } @Test public void publish_project_with_links_and_branch() throws Exception { - ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class); - when(projectAnalysisInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); + ProjectInfo projectInfo = mock(ProjectInfo.class); + when(projectInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12")); ProjectDefinition rootDef = ProjectDefinition.create() .setKey("foo") @@ -250,7 +248,6 @@ public class ComponentsPublisherTest { ScannerReportReader reader = new ScannerReportReader(outputDir); Component rootProtobuf = reader.readComponent(1); - assertThat(rootProtobuf.getVersion()).isEqualTo("1.0"); assertThat(rootProtobuf.getLinkCount()).isEqualTo(2); assertThat(rootProtobuf.getLink(0).getType()).isEqualTo(ComponentLinkType.HOME); assertThat(rootProtobuf.getLink(0).getHref()).isEqualTo("http://home"); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java index 466e016e62a..534051a35b2 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java @@ -20,6 +20,9 @@ package org.sonar.scanner.report; import com.google.common.collect.ImmutableMap; +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -28,17 +31,19 @@ import java.nio.file.Paths; import java.util.Collections; import java.util.Date; import java.util.Optional; +import javax.annotation.Nullable; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.scm.ScmProvider; -import org.sonar.scanner.ProjectAnalysisInfo; +import org.sonar.scanner.ProjectInfo; import org.sonar.scanner.bootstrap.ScannerPlugin; import org.sonar.scanner.bootstrap.ScannerPluginRepository; import org.sonar.scanner.cpd.CpdSettings; @@ -60,6 +65,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +@RunWith(DataProviderRunner.class) public class MetadataPublisherTest { @Rule @@ -69,7 +75,7 @@ public class MetadataPublisherTest { private MetadataPublisher underTest; private ScanProperties properties = mock(ScanProperties.class); private QualityProfiles qProfiles = mock(QualityProfiles.class); - private ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class); + private ProjectInfo projectInfo = mock(ProjectInfo.class); private CpdSettings cpdSettings = mock(CpdSettings.class); private InputModuleHierarchy inputModuleHierarchy; private ScannerPluginRepository pluginRepository = mock(ScannerPluginRepository.class); @@ -79,7 +85,7 @@ public class MetadataPublisherTest { @Before public void prepare() throws IOException { - when(projectAnalysisInfo.analysisDate()).thenReturn(new Date(1234567L)); + when(projectInfo.analysisDate()).thenReturn(new Date(1234567L)); when(scmProvider.relativePathFromScmRoot(any(Path.class))).thenReturn(Paths.get("dummy/path")); when(scmProvider.revisionId(any(Path.class))).thenReturn("dummy-sha1"); @@ -107,7 +113,7 @@ public class MetadataPublisherTest { branches = mock(BranchConfiguration.class); scmConfiguration = mock(ScmConfiguration.class); when(scmConfiguration.provider()).thenReturn(scmProvider); - underTest = new MetadataPublisher(projectAnalysisInfo, inputModuleHierarchy, properties, qProfiles, cpdSettings, + underTest = new MetadataPublisher(projectInfo, inputModuleHierarchy, properties, qProfiles, cpdSettings, pluginRepository, branches, scmConfiguration); } @@ -180,6 +186,31 @@ public class MetadataPublisherTest { assertThat(metadata.getOrganizationKey()).isEqualTo("SonarSource"); } + @Test + @UseDataProvider("projectVersions") + public void write_project_version(@Nullable String projectVersion, String expected) throws Exception { + when(projectInfo.projectVersion()).thenReturn(projectVersion); + when(properties.organizationKey()).thenReturn(Optional.of("SonarSource")); + + File outputDir = temp.newFolder(); + ScannerReportWriter writer = new ScannerReportWriter(outputDir); + + underTest.publish(writer); + + ScannerReportReader reader = new ScannerReportReader(outputDir); + ScannerReport.Metadata metadata = reader.readMetadata(); + assertThat(metadata.getProjectVersion()).isEqualTo(expected); + } + + @DataProvider + public static Object[][] projectVersions() { + return new Object[][] { + {null, ""}, + {"", ""}, + {"5.6.3", "5.6.3"} + }; + } + @Test public void write_long_lived_branch_info() throws Exception { String branchName = "long-lived"; diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java index 0a15ae1a078..f105ce86517 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java @@ -19,96 +19,83 @@ */ package org.sonar.scanner.scan; +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; import java.util.Arrays; -import java.util.function.Consumer; import java.util.Optional; +import java.util.function.Consumer; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; import org.sonar.api.CoreProperties; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.utils.MessageException; import org.sonar.core.config.ScannerProperties; +import org.sonar.scanner.ProjectInfo; import org.sonar.scanner.bootstrap.GlobalConfiguration; -import static org.apache.commons.lang.StringUtils.repeat; +import static org.apache.commons.lang.RandomStringUtils.randomAscii; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +@RunWith(DataProviderRunner.class) public class ProjectReactorValidatorTest { @Rule public ExpectedException thrown = ExpectedException.none(); - private AnalysisMode mode; - private ProjectReactorValidator validator; - private GlobalConfiguration settings; + private AnalysisMode mode = mock(AnalysisMode.class); + private GlobalConfiguration settings = mock(GlobalConfiguration.class); + private ProjectInfo projectInfo = mock(ProjectInfo.class); + private ProjectReactorValidator underTest = new ProjectReactorValidator(mode, settings); @Before public void prepare() { - mode = mock(AnalysisMode.class); - settings = mock(GlobalConfiguration.class); when(settings.get(anyString())).thenReturn(Optional.empty()); - validator = new ProjectReactorValidator(mode, settings); } @Test - public void not_fail_with_valid_key() { - validator.validate(createProjectReactor("foo")); - validator.validate(createProjectReactor("123foo")); - validator.validate(createProjectReactor("foo123")); - validator.validate(createProjectReactor("1Z3")); - validator.validate(createProjectReactor("a123")); - validator.validate(createProjectReactor("123a")); - validator.validate(createProjectReactor("1:2")); - validator.validate(createProjectReactor("3-3")); - validator.validate(createProjectReactor("-:")); + @UseDataProvider("validKeys") + public void not_fail_with_valid_key(String validKey) { + underTest.validate(createProjectReactor(validKey)); + } + + @DataProvider + public static Object[][] validKeys() { + return new Object[][] { + {"foo"}, + {"123foo"}, + {"foo123"}, + {"1Z3"}, + {"a123"}, + {"123a"}, + {"1:2"}, + {"3-3"}, + {"-:"}, + {"Foobar2"}, + {"foo.bar"}, + {"foo-bar"}, + {"foo:bar"}, + {"foo_bar"} + }; } @Test public void allow_slash_issues_mode() { when(mode.isIssues()).thenReturn(true); - validator.validate(createProjectReactor("project/key")); + underTest.validate(createProjectReactor("project/key")); when(mode.isIssues()).thenReturn(false); thrown.expect(MessageException.class); thrown.expectMessage("is not a valid project or module key"); - validator.validate(createProjectReactor("project/key")); - } - - @Test - public void not_fail_with_alphanumeric_key() { - ProjectReactor reactor = createProjectReactor("Foobar2"); - validator.validate(reactor); - } - - @Test - public void should_not_fail_with_dot_key() { - ProjectReactor reactor = createProjectReactor("foo.bar"); - validator.validate(reactor); - } - - @Test - public void not_fail_with_dash_key() { - ProjectReactor reactor = createProjectReactor("foo-bar"); - validator.validate(reactor); - } - - @Test - public void not_fail_with_colon_key() { - ProjectReactor reactor = createProjectReactor("foo:bar"); - validator.validate(reactor); - } - - @Test - public void not_fail_with_underscore_key() { - ProjectReactor reactor = createProjectReactor("foo_bar"); - validator.validate(reactor); + underTest.validate(createProjectReactor("project/key")); } @Test @@ -117,7 +104,7 @@ public class ProjectReactorValidatorTest { thrown.expect(MessageException.class); thrown.expectMessage("\"foo$bar\" is not a valid project or module key"); - validator.validate(reactor); + underTest.validate(reactor); } @Test @@ -126,33 +113,46 @@ public class ProjectReactorValidatorTest { thrown.expect(MessageException.class); thrown.expectMessage("\"foo\\bar\" is not a valid project or module key"); - validator.validate(reactor); + underTest.validate(reactor); } @Test - public void not_fail_with_valid_branch() { - validator.validate(createProjectReactor("foo", "branch")); - validator.validate(createProjectReactor("foo", "Branch2")); - validator.validate(createProjectReactor("foo", "bra.nch")); - validator.validate(createProjectReactor("foo", "bra-nch")); - validator.validate(createProjectReactor("foo", "1")); - validator.validate(createProjectReactor("foo", "bra_nch")); + @UseDataProvider("validBranches") + public void not_fail_with_valid_branch(String validBranch) { + ProjectReactor reactor = createProjectReactor("foo", validBranch); + + underTest.validate(reactor); } - @Test - public void fail_with_invalid_branch() { - ProjectReactor reactor = createProjectReactor("foo", "bran#ch"); - thrown.expect(MessageException.class); - thrown.expectMessage("\"bran#ch\" is not a valid branch name"); - validator.validate(reactor); + @DataProvider + public static Object[][] validBranches() { + return new Object[][] { + {"branch"}, + {"Branch2"}, + {"bra.nch"}, + {"bra-nch"}, + {"1"}, + {"bra_nch"} + }; } @Test - public void fail_with_colon_in_branch() { - ProjectReactor reactor = createProjectReactor("foo", "bran:ch"); + @UseDataProvider("invalidBranches") + public void fail_with_invalid_branch(String invalidBranch) { + ProjectReactor reactor = createProjectReactor("foo", invalidBranch); + thrown.expect(MessageException.class); - thrown.expectMessage("\"bran:ch\" is not a valid branch name"); - validator.validate(reactor); + thrown.expectMessage("\"" + invalidBranch + "\" is not a valid branch name"); + + underTest.validate(reactor); + } + + @DataProvider + public static Object[][] invalidBranches() { + return new Object[][] { + {"bran#ch"}, + {"bran:ch"} + }; } @Test @@ -161,7 +161,8 @@ public class ProjectReactorValidatorTest { thrown.expect(MessageException.class); thrown.expectMessage("\"12345\" is not a valid project or module key"); - validator.validate(reactor); + + underTest.validate(reactor); } @Test @@ -174,7 +175,7 @@ public class ProjectReactorValidatorTest { thrown.expect(MessageException.class); thrown.expectMessage("the branch plugin is required but not installed"); - validator.validate(reactor); + underTest.validate(reactor); } @Test @@ -187,7 +188,7 @@ public class ProjectReactorValidatorTest { thrown.expect(MessageException.class); thrown.expectMessage("the branch plugin is required but not installed"); - validator.validate(reactor); + underTest.validate(reactor); } @Test @@ -200,7 +201,7 @@ public class ProjectReactorValidatorTest { thrown.expect(MessageException.class); thrown.expectMessage("the branch plugin is required but not installed"); - validator.validate(reactor); + underTest.validate(reactor); } @Test @@ -213,7 +214,7 @@ public class ProjectReactorValidatorTest { thrown.expect(MessageException.class); thrown.expectMessage("the branch plugin is required but not installed"); - validator.validate(reactor); + underTest.validate(reactor); } @Test @@ -226,25 +227,24 @@ public class ProjectReactorValidatorTest { thrown.expect(MessageException.class); thrown.expectMessage("the branch plugin is required but not installed"); - validator.validate(reactor); - } - - @Test - public void not_fail_with_valid_version() { - validator.validate(createProjectReactor("foo", def -> def.setVersion("1.0"))); - validator.validate(createProjectReactor("foo", def -> def.setVersion("2017-10-16"))); - validator.validate(createProjectReactor("foo", def -> def.setVersion(repeat("a", 100)))); + underTest.validate(reactor); } @Test - public void fail_with_too_long_version() { - ProjectReactor reactor = createProjectReactor("foo", def -> def.setVersion(repeat("a", 101))); + @UseDataProvider("validVersions") + public void not_fail_with_valid_version(String validVersion) { + when(projectInfo.projectVersion()).thenReturn(validVersion); - thrown.expect(MessageException.class); - thrown.expectMessage("\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\" is not a valid version name for module \"foo\". " + - "The maximum length for version numbers is 100 characters."); + underTest.validate(createProjectReactor("foo")); + } - validator.validate(reactor); + @DataProvider + public static Object[][] validVersions() { + return new Object[][] { + {"1.0"}, + {"2017-10-16"}, + {randomAscii(100)} + }; } private ProjectReactor createProjectReactor(String projectKey, String branch) { diff --git a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto index 92e917a65f1..9c7812eea02 100644 --- a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto +++ b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto @@ -49,6 +49,8 @@ message Metadata { string pull_request_key = 14; map modules_project_relative_path_by_key = 15; + string projectVersion = 16; + message QProfile { string key = 1; string name = 2; @@ -108,7 +110,7 @@ message Component { string language = 6; repeated int32 child_ref = 7 [packed = true]; repeated ComponentLink link = 8; - // Only available on PROJECT and MODULE types + // FIXME SONAR-11631 delete this property after deployment on SC of metadata.projectVersion string version = 9; // Only available on PROJECT and MODULE types // TODO rename this property -> moduleKey ? @@ -121,7 +123,7 @@ message Component { // Path relative to project base directory string project_relative_path = 14; - + enum ComponentType { UNSET = 0; PROJECT = 1; -- 2.39.5