diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2020-05-12 14:14:38 -0500 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2020-06-11 20:04:56 +0000 |
commit | 3352e9f378dfb2929a19d362f4b5ae21bd33f0db (patch) | |
tree | 561d899ce84e2faaffc8bfca6718c1158443c613 /sonar-scanner-engine | |
parent | 46a49f0b5ef205f5632b44dc07221eed79ec803d (diff) | |
download | sonarqube-3352e9f378dfb2929a19d362f4b5ae21bd33f0db.tar.gz sonarqube-3352e9f378dfb2929a19d362f4b5ae21bd33f0db.zip |
SONAR-13390 SONAR-13391 New Code Reference Branch
Diffstat (limited to 'sonar-scanner-engine')
14 files changed, 473 insertions, 8 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalContainer.java index 02cb795c25b..6a7f5c663ff 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalContainer.java @@ -49,8 +49,10 @@ import org.sonar.core.util.UuidFactoryImpl; import org.sonar.scanner.extension.ScannerCoreExtensionsInstaller; import org.sonar.scanner.platform.DefaultServer; import org.sonar.scanner.repository.DefaultMetricsRepositoryLoader; +import org.sonar.scanner.repository.DefaultNewCodePeriodLoader; import org.sonar.scanner.repository.MetricsRepositoryLoader; import org.sonar.scanner.repository.MetricsRepositoryProvider; +import org.sonar.scanner.repository.NewCodePeriodLoader; import org.sonar.scanner.repository.settings.DefaultGlobalSettingsLoader; import org.sonar.scanner.repository.settings.GlobalSettingsLoader; import org.sonar.scanner.scan.ProjectScanContainer; @@ -118,6 +120,7 @@ public class GlobalContainer extends ComponentContainer { addIfMissing(ScannerPluginInstaller.class, PluginInstaller.class); add(CoreExtensionRepositoryImpl.class, CoreExtensionsLoader.class, ScannerCoreExtensionsInstaller.class); addIfMissing(DefaultGlobalSettingsLoader.class, GlobalSettingsLoader.class); + addIfMissing(DefaultNewCodePeriodLoader.class, NewCodePeriodLoader.class); addIfMissing(DefaultMetricsRepositoryLoader.class, MetricsRepositoryLoader.class); } 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 76b6d52c589..3eb0ed89517 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 @@ -21,6 +21,7 @@ package org.sonar.scanner.report; import java.io.File; import java.nio.file.Path; +import java.time.Instant; import java.util.LinkedList; import java.util.Map.Entry; import java.util.regex.Pattern; @@ -38,6 +39,7 @@ import org.sonar.scanner.fs.InputModuleHierarchy; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.Metadata.BranchType; import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.repository.ForkDateSupplier; import org.sonar.scanner.rule.QProfile; import org.sonar.scanner.rule.QualityProfiles; import org.sonar.scanner.scan.ScanProperties; @@ -57,13 +59,13 @@ public class MetadataPublisher implements ReportPublisherStep { private final ScannerPluginRepository pluginRepository; private final BranchConfiguration branchConfiguration; private final ScmRevision scmRevision; - + private final ForkDateSupplier forkDateSupplier; @Nullable private final ScmConfiguration scmConfiguration; public MetadataPublisher(ProjectInfo projectInfo, InputModuleHierarchy moduleHierarchy, ScanProperties properties, QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration, - ScmRevision scmRevision, @Nullable ScmConfiguration scmConfiguration) { + ScmRevision scmRevision, ForkDateSupplier forkDateSupplier, @Nullable ScmConfiguration scmConfiguration) { this.projectInfo = projectInfo; this.moduleHierarchy = moduleHierarchy; this.properties = properties; @@ -72,12 +74,13 @@ public class MetadataPublisher implements ReportPublisherStep { this.pluginRepository = pluginRepository; this.branchConfiguration = branchConfiguration; this.scmRevision = scmRevision; + this.forkDateSupplier = forkDateSupplier; this.scmConfiguration = scmConfiguration; } - public MetadataPublisher(ProjectInfo projectInfo, InputModuleHierarchy moduleHierarchy, ScanProperties properties, - QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration, ScmRevision scmRevision) { - this(projectInfo, moduleHierarchy, properties, qProfiles, cpdSettings, pluginRepository, branchConfiguration, scmRevision, null); + public MetadataPublisher(ProjectInfo projectInfo, InputModuleHierarchy moduleHierarchy, ScanProperties properties, QualityProfiles qProfiles, + CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration, ScmRevision scmRevision, ForkDateSupplier forkDateSupplier) { + this(projectInfo, moduleHierarchy, properties, qProfiles, cpdSettings, pluginRepository, branchConfiguration, scmRevision, forkDateSupplier, null); } @Override @@ -99,6 +102,7 @@ public class MetadataPublisher implements ReportPublisherStep { } addScmInformation(builder); + addForkPoint(builder); for (QProfile qp : qProfiles.findAll()) { builder.putQprofilesPerLanguage(qp.getLanguage(), ScannerReport.Metadata.QProfile.newBuilder() @@ -118,6 +122,13 @@ public class MetadataPublisher implements ReportPublisherStep { writer.writeMetadata(builder.build()); } + private void addForkPoint(ScannerReport.Metadata.Builder builder) { + Instant date = forkDateSupplier.get(); + if (date != null) { + builder.setForkDate(date.toEpochMilli()); + } + } + private void addModulesRelativePaths(ScannerReport.Metadata.Builder builder) { LinkedList<DefaultInputModule> queue = new LinkedList<>(); queue.add(moduleHierarchy.root()); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoader.java new file mode 100644 index 00000000000..456e43a4aa9 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoader.java @@ -0,0 +1,55 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scanner.repository; + +import java.io.IOException; +import java.io.InputStream; +import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonarqube.ws.NewCodePeriods; +import org.sonarqube.ws.client.GetRequest; +import org.sonarqube.ws.client.HttpException; + +import static org.sonar.api.impl.utils.ScannerUtils.encodeForUrl; + +public class DefaultNewCodePeriodLoader implements NewCodePeriodLoader { + private static final String WS_URL = "/api/new_code_periods/show.protobuf"; + + private final DefaultScannerWsClient wsClient; + + public DefaultNewCodePeriodLoader(DefaultScannerWsClient wsClient) { + this.wsClient = wsClient; + } + + @Override public NewCodePeriods.ShowWSResponse load(String projectKey, String branchName) { + String url = WS_URL + "?project=" + encodeForUrl(projectKey) + "&branch=" + encodeForUrl(branchName); + try { + return call(url); + } catch (HttpException | IOException e) { + throw new IllegalStateException("Failed to get the New Code definition: " + e.getMessage(), e); + } + } + + private NewCodePeriods.ShowWSResponse call(String url) throws IOException { + GetRequest getRequest = new GetRequest(url); + try (InputStream is = wsClient.call(getRequest).contentStream()) { + return NewCodePeriods.ShowWSResponse.parseFrom(is); + } + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ForkDateSupplier.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ForkDateSupplier.java new file mode 100644 index 00000000000..9bbed47083e --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ForkDateSupplier.java @@ -0,0 +1,93 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scanner.repository; + +import java.time.Instant; +import javax.annotation.CheckForNull; +import org.sonar.api.batch.fs.internal.DefaultInputProject; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.api.utils.log.Profiler; +import org.sonar.scanner.report.ChangedLinesPublisher; +import org.sonar.scanner.scan.branch.BranchConfiguration; +import org.sonar.scanner.scan.branch.ProjectBranches; +import org.sonar.scanner.scm.ScmConfiguration; +import org.sonarqube.ws.NewCodePeriods; + +public class ForkDateSupplier { + private static final Logger LOG = Loggers.get(ChangedLinesPublisher.class); + private static final String LOG_MSG_WS = "Load New Code definition"; + + private final NewCodePeriodLoader newCodePeriodLoader; + private final BranchConfiguration branchConfiguration; + private final DefaultInputProject project; + private final ScmConfiguration scmConfiguration; + private final ProjectBranches branches; + private final AnalysisWarnings analysisWarnings; + + public ForkDateSupplier(NewCodePeriodLoader newCodePeriodLoader, BranchConfiguration branchConfiguration, + DefaultInputProject project, ScmConfiguration scmConfiguration, ProjectBranches branches, AnalysisWarnings analysisWarnings) { + this.newCodePeriodLoader = newCodePeriodLoader; + this.branchConfiguration = branchConfiguration; + this.project = project; + this.scmConfiguration = scmConfiguration; + this.branches = branches; + this.analysisWarnings = analysisWarnings; + } + + @CheckForNull + public Instant get() { + // branches will be empty in CE + if (branchConfiguration.isPullRequest() || branches.isEmpty()) { + return null; + } + + Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG_WS); + String branchName = branchConfiguration.branchName() != null ? branchConfiguration.branchName() : branches.defaultBranchName(); + NewCodePeriods.ShowWSResponse newCode = newCodePeriodLoader.load(project.key(), branchName); + profiler.stopInfo(); + if (newCode.getType() != NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH) { + return null; + } + + String referenceBranchName = newCode.getValue(); + if (branchName.equals(referenceBranchName)) { + LOG.warn("New Code reference branch is set to the branch being analyzed. Skipping the computation of New Code"); + return null; + } + + LOG.info("Computing New Code since fork with '{}'", referenceBranchName); + if (scmConfiguration.isDisabled() || scmConfiguration.provider() == null) { + LOG.warn("SCM provider is disabled. No New Code will be computed."); + analysisWarnings.addUnique("The scanner failed to compute New Code because no SCM provider was found. Please check your scanner logs."); + return null; + } + + Instant forkdate = scmConfiguration.provider().forkDate(referenceBranchName, project.getBaseDir()); + if (forkdate != null) { + LOG.debug("Fork detected at '{}'", referenceBranchName, forkdate); + } else { + analysisWarnings.addUnique("The scanner failed to compute New Code. Please check your scanner logs."); + LOG.warn("Failed to detect fork date. No New Code will be computed.", referenceBranchName); + } + return forkdate; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/NewCodePeriodLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/NewCodePeriodLoader.java new file mode 100644 index 00000000000..e6a7d63fd85 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/NewCodePeriodLoader.java @@ -0,0 +1,26 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scanner.repository; + +import org.sonarqube.ws.NewCodePeriods; + +public interface NewCodePeriodLoader { + NewCodePeriods.ShowWSResponse load(String projectKey, String branchName); +} 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 263913d0062..7e14856626d 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 @@ -91,6 +91,7 @@ import org.sonar.scanner.report.TestExecutionPublisher; import org.sonar.scanner.repository.ContextPropertiesCache; import org.sonar.scanner.repository.DefaultProjectRepositoriesLoader; import org.sonar.scanner.repository.DefaultQualityProfileLoader; +import org.sonar.scanner.repository.ForkDateSupplier; import org.sonar.scanner.repository.ProjectRepositoriesLoader; import org.sonar.scanner.repository.ProjectRepositoriesSupplier; import org.sonar.scanner.repository.QualityProfileLoader; @@ -239,6 +240,7 @@ public class ProjectScanContainer extends ComponentContainer { ProjectCoverageAndDuplicationExclusions.class, // Report + ForkDateSupplier.class, ScannerMetrics.class, ReportPublisher.class, AnalysisContextReportPublisher.class, diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java index 616ee42f83f..0e4cc266258 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java @@ -19,7 +19,7 @@ */ package org.sonar.scanner.scan.branch; -import com.google.common.collect.ImmutableList; +import java.util.Collections; import org.picocontainer.annotations.Nullable; import org.picocontainer.injectors.ProviderAdapter; import org.sonar.api.utils.log.Logger; @@ -40,7 +40,7 @@ public class ProjectBranchesProvider extends ProviderAdapter { } if (loader == null) { - this.branches = new ProjectBranches(ImmutableList.of()); + this.branches = new ProjectBranches(Collections.emptyList()); return this.branches; } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java index 0628ed9525a..fc1d6a5d68a 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java @@ -19,6 +19,9 @@ */ package org.sonar.batch.bootstrapper; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -27,8 +30,11 @@ import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; public class BatchTest { + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"); + @Test public void testBuilder() { + System.out.println(FORMATTER.format(LocalDate.parse("2019-05-02").atStartOfDay(ZoneId.systemDefault()))); Batch batch = newBatch(); assertNotNull(batch); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/WsTestUtil.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/WsTestUtil.java index 30986ae83bc..e8e3f273c81 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/WsTestUtil.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/WsTestUtil.java @@ -92,5 +92,10 @@ public class WsTestUtil { } return StringUtils.equals(item.getPath(), path); } + + @Override + public String toString() { + return "Request with path: " + path; + } } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java index a3a1024621d..ce2224dc165 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java @@ -60,6 +60,7 @@ import org.sonar.scanner.report.CeTaskReportDataHolder; import org.sonar.scanner.repository.FileData; import org.sonar.scanner.repository.MetricsRepository; import org.sonar.scanner.repository.MetricsRepositoryLoader; +import org.sonar.scanner.repository.NewCodePeriodLoader; import org.sonar.scanner.repository.ProjectRepositories; import org.sonar.scanner.repository.ProjectRepositoriesLoader; import org.sonar.scanner.repository.QualityProfileLoader; @@ -74,6 +75,7 @@ import org.sonar.scanner.scan.branch.BranchConfigurationLoader; import org.sonar.scanner.scan.branch.BranchType; import org.sonar.scanner.scan.branch.ProjectBranches; import org.sonar.scanner.scan.branch.ProjectPullRequests; +import org.sonarqube.ws.NewCodePeriods; import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; import org.sonarqube.ws.Rules.ListResponse.Rule; @@ -91,6 +93,7 @@ public class ScannerMediumTester extends ExternalResource { private final FakePluginInstaller pluginInstaller = new FakePluginInstaller(); private final FakeGlobalSettingsLoader globalSettingsLoader = new FakeGlobalSettingsLoader(); private final FakeProjectSettingsLoader projectSettingsLoader = new FakeProjectSettingsLoader(); + private final FakeNewCodePeriodLoader newCodePeriodLoader = new FakeNewCodePeriodLoader(); private final FakeRulesLoader rulesLoader = new FakeRulesLoader(); private final FakeQualityProfileLoader qualityProfiles = new FakeQualityProfileLoader(); private final FakeActiveRulesLoader activeRules = new FakeActiveRulesLoader(); @@ -226,6 +229,11 @@ public class ScannerMediumTester extends ExternalResource { return this; } + public ScannerMediumTester setNewCodePeriod(NewCodePeriods.NewCodePeriodType type, String value) { + newCodePeriodLoader.set(NewCodePeriods.ShowWSResponse.newBuilder().setType(type).setValue(value).build()); + return this; + } + @Override protected void before() { try { @@ -294,6 +302,7 @@ public class ScannerMediumTester extends ExternalResource { tester.activeRules, tester.globalSettingsLoader, tester.projectSettingsLoader, + tester.newCodePeriodLoader, tester.sonarRuntime, tester.reportMetadataHolder, result) @@ -511,6 +520,19 @@ public class ScannerMediumTester extends ExternalResource { } } + private static class FakeNewCodePeriodLoader implements NewCodePeriodLoader { + private NewCodePeriods.ShowWSResponse response; + + @Override + public NewCodePeriods.ShowWSResponse load(String projectKey, String branchName) { + return response; + } + + public void set(NewCodePeriods.ShowWSResponse response) { + this.response = response; + } + } + private static class FakeProjectSettingsLoader implements ProjectSettingsLoader { private Map<String, String> projectSettings = new HashMap<>(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java index 19668da6de3..917d721ae3d 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java @@ -40,6 +40,7 @@ import org.sonar.scanner.repository.FileData; import org.sonar.scanner.scan.branch.BranchType; import org.sonar.xoo.XooPlugin; import org.sonar.xoo.rule.XooRulesDefinition; +import org.sonarqube.ws.NewCodePeriods; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -74,6 +75,7 @@ public class BranchMediumTest { .readMetadata(Files.newInputStream(filepath), StandardCharsets.UTF_8, FILE_PATH) .hash(); tester.addFileData(FILE_PATH, new FileData(md5sum, "1.1")); + tester.setNewCodePeriod(NewCodePeriods.NewCodePeriodType.PREVIOUS_VERSION, ""); } @Test 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 934eefaa7a3..53d37e99363 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 @@ -28,6 +28,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.Instant; import java.util.Collections; import java.util.Date; import java.util.Optional; @@ -49,6 +50,7 @@ import org.sonar.scanner.fs.InputModuleHierarchy; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReportReader; import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.repository.ForkDateSupplier; import org.sonar.scanner.rule.QProfile; import org.sonar.scanner.rule.QualityProfiles; import org.sonar.scanner.scan.ScanProperties; @@ -78,6 +80,7 @@ public class MetadataPublisherTest { private ProjectInfo projectInfo = mock(ProjectInfo.class); private CpdSettings cpdSettings = mock(CpdSettings.class); private InputModuleHierarchy inputModuleHierarchy; + private ForkDateSupplier forkDateSupplier = mock(ForkDateSupplier.class); private ScannerPluginRepository pluginRepository = mock(ScannerPluginRepository.class); private BranchConfiguration branches; private ScmConfiguration scmConfiguration; @@ -115,12 +118,13 @@ public class MetadataPublisherTest { scmConfiguration = mock(ScmConfiguration.class); when(scmConfiguration.provider()).thenReturn(scmProvider); underTest = new MetadataPublisher(projectInfo, inputModuleHierarchy, properties, qProfiles, cpdSettings, - pluginRepository, branches, scmRevision, scmConfiguration); + pluginRepository, branches, scmRevision, forkDateSupplier, scmConfiguration); } @Test public void write_metadata() throws Exception { Date date = new Date(); + when(forkDateSupplier.get()).thenReturn(Instant.ofEpochMilli(123456789L)); when(qProfiles.findAll()).thenReturn(Collections.singletonList(new QProfile("q1", "Q1", "java", date))); when(pluginRepository.getPluginsByKey()).thenReturn(ImmutableMap.of( "java", new ScannerPlugin("java", 12345L, null), @@ -132,6 +136,7 @@ public class MetadataPublisherTest { ScannerReportReader reader = new ScannerReportReader(outputDir); ScannerReport.Metadata metadata = reader.readMetadata(); + assertThat(metadata.getForkDate()).isEqualTo(123456789L); assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); assertThat(metadata.getProjectKey()).isEqualTo("root"); assertThat(metadata.getModulesProjectRelativePathByKeyMap()).containsOnly(entry("module", "modulePath"), entry("root", "")); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoaderTest.java new file mode 100644 index 00000000000..6299aa05c6e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoaderTest.java @@ -0,0 +1,68 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scanner.repository; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.scanner.WsTestUtil; +import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonarqube.ws.NewCodePeriods; + +import static org.mockito.Mockito.mock; + +public class DefaultNewCodePeriodLoaderTest { + @Rule + public ExpectedException exception = ExpectedException.none(); + + private DefaultScannerWsClient wsClient = mock(DefaultScannerWsClient.class); + private DefaultNewCodePeriodLoader underTest = new DefaultNewCodePeriodLoader(wsClient); + + @Test + public void loads_new_code_period() throws IOException { + prepareCallWithResults(); + underTest.load("project", "branch"); + verifyCalledPath("/api/new_code_periods/show.protobuf?project=project&branch=branch"); + } + + private void verifyCalledPath(String expectedPath) { + WsTestUtil.verifyCall(wsClient, expectedPath); + } + + private void prepareCallWithResults() throws IOException { + WsTestUtil.mockStream(wsClient, createResponse(NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH, "master")); + } + + private InputStream createResponse(NewCodePeriods.NewCodePeriodType type, String value) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + NewCodePeriods.ShowWSResponse response = NewCodePeriods.ShowWSResponse.newBuilder() + .setType(type) + .setValue(value) + .build(); + + response.writeTo(os); + return new ByteArrayInputStream(os.toByteArray()); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/ForkDateSupplierTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/ForkDateSupplierTest.java new file mode 100644 index 00000000000..895d81634c9 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/ForkDateSupplierTest.java @@ -0,0 +1,167 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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.scanner.repository; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.fs.internal.DefaultInputProject; +import org.sonar.api.batch.scm.ScmProvider; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.scanner.scan.branch.BranchConfiguration; +import org.sonar.scanner.scan.branch.BranchType; +import org.sonar.scanner.scan.branch.ProjectBranches; +import org.sonar.scanner.scm.ScmConfiguration; +import org.sonarqube.ws.NewCodePeriods; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class ForkDateSupplierTest { + private final static String PROJECT_KEY = "project"; + private final static String BRANCH_KEY = "branch"; + private final static Path BASE_DIR = Paths.get("root"); + + private NewCodePeriodLoader newCodePeriodLoader = mock(NewCodePeriodLoader.class); + private BranchConfiguration branchConfiguration = mock(BranchConfiguration.class); + private DefaultInputProject project = mock(DefaultInputProject.class); + private ScmConfiguration scmConfiguration = mock(ScmConfiguration.class); + private ScmProvider scmProvider = mock(ScmProvider.class); + private ProjectBranches projectBranches = mock(ProjectBranches.class); + private AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class); + private ForkDateSupplier forkDateSupplier = new ForkDateSupplier(newCodePeriodLoader, branchConfiguration, project, scmConfiguration, projectBranches, analysisWarnings); + + @Before + public void setUp() { + when(projectBranches.isEmpty()).thenReturn(false); + when(project.key()).thenReturn(PROJECT_KEY); + when(project.getBaseDir()).thenReturn(BASE_DIR); + when(scmConfiguration.isDisabled()).thenReturn(false); + when(scmConfiguration.provider()).thenReturn(scmProvider); + } + + @Test + public void returns_forkDate_for_branches_with_ref() { + Instant date = Instant.now(); + when(branchConfiguration.branchType()).thenReturn(BranchType.BRANCH); + when(branchConfiguration.branchName()).thenReturn(BRANCH_KEY); + when(scmProvider.forkDate("master", BASE_DIR)).thenReturn(date); + when(newCodePeriodLoader.load(PROJECT_KEY, BRANCH_KEY)).thenReturn(createResponse(NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH, "master")); + + assertThat(forkDateSupplier.get()).isEqualTo(date); + } + + @Test + public void uses_default_branch_if_no_branch_specified() { + Instant date = Instant.now(); + when(branchConfiguration.branchType()).thenReturn(BranchType.BRANCH); + when(branchConfiguration.branchName()).thenReturn(null); + when(projectBranches.defaultBranchName()).thenReturn("default"); + when(newCodePeriodLoader.load(PROJECT_KEY, "default")).thenReturn(createResponse(NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH, "master")); + when(scmProvider.forkDate("master", BASE_DIR)).thenReturn(date); + + assertThat(forkDateSupplier.get()).isEqualTo(date); + + verifyNoInteractions(analysisWarnings); + } + + @Test + public void returns_null_if_no_branches() { + when(projectBranches.isEmpty()).thenReturn(true); + + assertThat(forkDateSupplier.get()).isNull(); + + verify(branchConfiguration).isPullRequest(); + verify(projectBranches).isEmpty(); + verifyNoMoreInteractions(branchConfiguration); + verifyNoInteractions(scmConfiguration, scmProvider, analysisWarnings, newCodePeriodLoader); + } + + @Test + public void returns_null_if_scm_disabled() { + when(branchConfiguration.branchType()).thenReturn(BranchType.BRANCH); + when(branchConfiguration.branchName()).thenReturn(BRANCH_KEY); + when(scmConfiguration.isDisabled()).thenReturn(true); + when(newCodePeriodLoader.load(PROJECT_KEY, BRANCH_KEY)).thenReturn(createResponse(NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH, "master")); + + assertThat(forkDateSupplier.get()).isNull(); + + verify(scmConfiguration).isDisabled(); + verify(branchConfiguration, times(2)).branchName(); + verify(branchConfiguration).isPullRequest(); + verify(analysisWarnings).addUnique(anyString()); + + verifyNoInteractions(scmProvider); + verifyNoMoreInteractions(branchConfiguration); + } + + @Test + public void returns_null_if_reference_branch_is_the_branch_being_analyzed() { + when(branchConfiguration.branchType()).thenReturn(BranchType.BRANCH); + when(branchConfiguration.branchName()).thenReturn(BRANCH_KEY); + when(newCodePeriodLoader.load(PROJECT_KEY, BRANCH_KEY)).thenReturn(createResponse(NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH, BRANCH_KEY)); + + assertThat(forkDateSupplier.get()).isNull(); + + verify(branchConfiguration, times(2)).branchName(); + verify(branchConfiguration).isPullRequest(); + verify(newCodePeriodLoader).load(PROJECT_KEY, BRANCH_KEY); + + verifyNoInteractions(scmProvider, analysisWarnings, scmConfiguration); + verifyNoMoreInteractions(branchConfiguration); + } + + @Test + public void returns_null_if_pull_request() { + when(branchConfiguration.isPullRequest()).thenReturn(true); + assertThat(forkDateSupplier.get()).isNull(); + + verify(branchConfiguration).isPullRequest(); + + verifyNoInteractions(newCodePeriodLoader, analysisWarnings, scmProvider, scmConfiguration); + verifyNoMoreInteractions(branchConfiguration); + } + + @Test + public void returns_null_if_new_code_period_is_not_ref() { + when(branchConfiguration.isPullRequest()).thenReturn(true); + when(branchConfiguration.branchName()).thenReturn(BRANCH_KEY); + when(newCodePeriodLoader.load(PROJECT_KEY, BRANCH_KEY)).thenReturn(createResponse(NewCodePeriods.NewCodePeriodType.NUMBER_OF_DAYS, "2")); + + assertThat(forkDateSupplier.get()).isNull(); + + verifyNoInteractions(scmProvider, analysisWarnings, scmConfiguration); + } + + private NewCodePeriods.ShowWSResponse createResponse(NewCodePeriods.NewCodePeriodType type, String value) { + return NewCodePeriods.ShowWSResponse.newBuilder() + .setType(type) + .setValue(value) + .build(); + } +} |