aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-scanner-engine
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2020-05-12 14:14:38 -0500
committersonartech <sonartech@sonarsource.com>2020-06-11 20:04:56 +0000
commit3352e9f378dfb2929a19d362f4b5ae21bd33f0db (patch)
tree561d899ce84e2faaffc8bfca6718c1158443c613 /sonar-scanner-engine
parent46a49f0b5ef205f5632b44dc07221eed79ec803d (diff)
downloadsonarqube-3352e9f378dfb2929a19d362f4b5ae21bd33f0db.tar.gz
sonarqube-3352e9f378dfb2929a19d362f4b5ae21bd33f0db.zip
SONAR-13390 SONAR-13391 New Code Reference Branch
Diffstat (limited to 'sonar-scanner-engine')
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalContainer.java3
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java21
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoader.java55
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ForkDateSupplier.java93
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/NewCodePeriodLoader.java26
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java2
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java4
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java6
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/WsTestUtil.java5
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java22
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java2
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java7
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoaderTest.java68
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/ForkDateSupplierTest.java167
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();
+ }
+}