aboutsummaryrefslogtreecommitdiffstats
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
parent46a49f0b5ef205f5632b44dc07221eed79ec803d (diff)
downloadsonarqube-3352e9f378dfb2929a19d362f4b5ae21bd33f0db.tar.gz
sonarqube-3352e9f378dfb2929a19d362f4b5ae21bd33f0db.zip
SONAR-13390 SONAR-13391 New Code Reference Branch
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolder.java7
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java15
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java5
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java4
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregator.java4
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/NewCodePeriodResolver.java27
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/Period.java25
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolder.java8
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImpl.java6
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewReliabilityAndSecurityRatingMeasuresVisitor.java2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java4
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/NewLinesRepository.java6
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStep.java16
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java1
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistAnalysisStep.java2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java27
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java22
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregatorTest.java14
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImplTest.java12
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderRule.java5
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodTest.java4
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStepTest.java37
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java12
-rw-r--r--server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java24
-rw-r--r--server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java12
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodType.java3
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SnapshotDtoToWsPeriod.java8
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ListAction.java2
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/SetAction.java13
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ShowAction.java39
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java7
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/SetActionTest.java5
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/ShowActionTest.java57
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmProvider.java12
-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
-rw-r--r--sonar-scanner-protocol/src/main/protobuf/scanner_report.proto2
-rw-r--r--sonar-ws/src/main/protobuf/ws-newcodeperiods.proto1
50 files changed, 809 insertions, 122 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolder.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolder.java
index da91f4797eb..d5b3b9dbf61 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolder.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolder.java
@@ -50,6 +50,13 @@ public interface AnalysisMetadataHolder {
long getAnalysisDate();
/**
+ * @throws IllegalStateException if no fork date has been set
+ */
+ @CheckForNull
+ Long getForkDate();
+
+
+ /**
* Tell whether the analysisDate has been set.
*/
boolean hasAnalysisDateBeenSet();
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java
index b12b79b0139..ef17288e84e 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java
@@ -50,6 +50,7 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder
private final InitializedProperty<Map<String, QualityProfile>> qProfilesPerLanguage = new InitializedProperty<>();
private final InitializedProperty<Map<String, ScannerPlugin>> pluginsByKey = new InitializedProperty<>();
private final InitializedProperty<String> scmRevision = new InitializedProperty<>();
+ private final InitializedProperty<Long> forkDate = new InitializedProperty<>();
private final PlatformEditionProvider editionProvider;
@@ -117,6 +118,20 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder
}
@Override
+ public MutableAnalysisMetadataHolder setForkDate(@Nullable Long date) {
+ checkState(!forkDate.isInitialized(), "Fork date has already been set");
+ this.forkDate.setProperty(date);
+ return this;
+ }
+
+ @Override
+ @CheckForNull
+ public Long getForkDate() {
+ checkState(forkDate.isInitialized(), "Fork date has not been set");
+ return this.forkDate.getProperty();
+ }
+
+ @Override
public boolean isFirstAnalysis() {
return getBaseAnalysis() == null;
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java
index 61a098f9e3b..7ffa27cd47a 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java
@@ -47,6 +47,11 @@ public interface MutableAnalysisMetadataHolder extends AnalysisMetadataHolder {
MutableAnalysisMetadataHolder setAnalysisDate(long date);
/**
+ * @throws IllegalStateException if the fork date has already been set
+ */
+ MutableAnalysisMetadataHolder setForkDate(@Nullable Long date);
+
+ /**
* @throws IllegalStateException if baseAnalysis has already been set
*/
MutableAnalysisMetadataHolder setBaseAnalysis(@Nullable Analysis baseAnalysis);
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java
index 91fadde60ca..bae5df00769 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java
@@ -150,7 +150,7 @@ public class IssueCounter extends IssueVisitor {
currentCounters.add(issue);
if (analysisMetadataHolder.isPullRequest()) {
currentCounters.addOnPeriod(issue);
- } else if (periodHolder.hasPeriod()) {
+ } else if (periodHolder.hasPeriodDate()) {
Period period = periodHolder.getPeriod();
if (period.isOnPeriod(issue.creationDate())){
currentCounters.addOnPeriod(issue);
@@ -196,7 +196,7 @@ public class IssueCounter extends IssueVisitor {
}
private void addNewMeasures(Component component) {
- if (!periodHolder.hasPeriod() && !analysisMetadataHolder.isPullRequest()) {
+ if (!periodHolder.hasPeriodDate() && !analysisMetadataHolder.isPullRequest()) {
return;
}
double unresolvedVariations = currentCounters.counterForPeriod().unresolved;
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregator.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregator.java
index 1bd97ca7542..722d6342f27 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregator.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregator.java
@@ -84,7 +84,7 @@ public class NewEffortAggregator extends IssueVisitor {
if (issue.resolution() == null && issue.effortInMinutes() != null) {
if (analysisMetadataHolder.isPullRequest()) {
counter.add(issue, null);
- } else if (periodHolder.hasPeriod()) {
+ } else if (periodHolder.hasPeriodDate()) {
counter.add(issue, periodHolder.getPeriod());
}
}
@@ -92,7 +92,7 @@ public class NewEffortAggregator extends IssueVisitor {
@Override
public void afterComponent(Component component) {
- if (periodHolder.hasPeriod() || analysisMetadataHolder.isPullRequest()) {
+ if (periodHolder.hasPeriodDate() || analysisMetadataHolder.isPullRequest()) {
computeMeasure(component, newMaintainabilityEffortMetric, counter.maintainabilitySum);
computeMeasure(component, newReliabilityEffortMetric, counter.reliabilitySum);
computeMeasure(component, newSecurityEffortMetric, counter.securitySum);
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/NewCodePeriodResolver.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/NewCodePeriodResolver.java
index 3f4c0a6f61b..101c3f020e1 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/NewCodePeriodResolver.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/NewCodePeriodResolver.java
@@ -25,11 +25,13 @@ import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.MessageException;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.SnapshotDto;
@@ -50,31 +52,44 @@ public class NewCodePeriodResolver {
private static final Logger LOG = Loggers.get(NewCodePeriodResolver.class);
private final DbClient dbClient;
+ private final AnalysisMetadataHolder analysisMetadataHolder;
- public NewCodePeriodResolver(DbClient dbClient) {
+ public NewCodePeriodResolver(DbClient dbClient, AnalysisMetadataHolder analysisMetadataHolder) {
this.dbClient = dbClient;
+ this.analysisMetadataHolder = analysisMetadataHolder;
}
- public Period resolve(DbSession dbSession, String branchUuid, NewCodePeriodDto newCodePeriodDto, long referenceDate, String projectVersion) {
- return toPeriod(newCodePeriodDto.getType(), newCodePeriodDto.getValue(), dbSession, projectVersion, branchUuid, referenceDate);
+ @CheckForNull
+ public Period resolve(DbSession dbSession, String branchUuid, NewCodePeriodDto newCodePeriodDto, String projectVersion) {
+ return toPeriod(newCodePeriodDto.getType(), newCodePeriodDto.getValue(), dbSession, projectVersion, branchUuid);
}
- private Period toPeriod(NewCodePeriodType type, @Nullable String value, DbSession dbSession, String projectVersion, String rootUuid, long referenceDate) {
+ @CheckForNull
+ private Period toPeriod(NewCodePeriodType type, @Nullable String value, DbSession dbSession, String projectVersion, String rootUuid) {
switch (type) {
case NUMBER_OF_DAYS:
checkNotNullValue(value, type);
Integer days = NewCodePeriodParser.parseDays(value);
- return resolveByDays(dbSession, rootUuid, days, value, referenceDate);
+ return resolveByDays(dbSession, rootUuid, days, value, analysisMetadataHolder.getAnalysisDate());
case PREVIOUS_VERSION:
return resolveByPreviousVersion(dbSession, rootUuid, projectVersion);
case SPECIFIC_ANALYSIS:
checkNotNullValue(value, type);
return resolveBySpecificAnalysis(dbSession, rootUuid, value);
+ case REFERENCE_BRANCH:
+ checkNotNullValue(value, type);
+ return resolveByReferenceBranch(value);
default:
throw new IllegalStateException("Unexpected type: " + type);
}
}
+ private Period resolveByReferenceBranch(String value) {
+ Long forkDate = analysisMetadataHolder.getForkDate();
+ // forkDate can be null if the scanner failed to find it
+ return newPeriod(NewCodePeriodType.REFERENCE_BRANCH, value, forkDate);
+ }
+
private Period resolveBySpecificAnalysis(DbSession dbSession, String rootUuid, String value) {
SnapshotDto baseline = dbClient.snapshotDao().selectByUuid(dbSession, value)
.filter(t -> t.getComponentUuid().equals(rootUuid))
@@ -135,7 +150,7 @@ public class NewCodePeriodResolver {
return period.get();
}
- private static Period newPeriod(NewCodePeriodType type, @Nullable String value, long date) {
+ private static Period newPeriod(NewCodePeriodType type, @Nullable String value, @Nullable Long date) {
return new Period(type.name(), value, date);
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/Period.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/Period.java
index 474adf14536..4ef0713a81c 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/Period.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/Period.java
@@ -33,14 +33,13 @@ import static org.sonar.api.utils.DateUtils.truncateToSeconds;
@Immutable
public class Period {
private final String mode;
- @CheckForNull
private final String modeParameter;
- private final long snapshotDate;
+ private final Long date;
- public Period(String mode, @Nullable String modeParameter, long snapshotDate) {
+ public Period(String mode, @Nullable String modeParameter, @Nullable Long date) {
this.mode = requireNonNull(mode);
this.modeParameter = modeParameter;
- this.snapshotDate = snapshotDate;
+ this.date = date;
}
public String getMode() {
@@ -52,8 +51,9 @@ public class Period {
return modeParameter;
}
- public long getSnapshotDate() {
- return snapshotDate;
+ @CheckForNull
+ public Long getDate() {
+ return date;
}
@Override
@@ -65,18 +65,19 @@ public class Period {
return false;
}
Period period = (Period) o;
- return snapshotDate == period.snapshotDate
- && mode.equals(period.mode)
- && Objects.equals(modeParameter, period.modeParameter);
+ return Objects.equals(date, period.date) && Objects.equals(mode, period.mode) && Objects.equals(modeParameter, period.modeParameter);
}
public boolean isOnPeriod(Date date) {
- return date.getTime() > truncateToSeconds(snapshotDate);
+ if (this.date == null) {
+ return false;
+ }
+ return date.getTime() > truncateToSeconds(this.date);
}
@Override
public int hashCode() {
- return hash(mode, modeParameter, snapshotDate);
+ return hash(mode, modeParameter, date);
}
@Override
@@ -84,7 +85,7 @@ public class Period {
return toStringHelper(this)
.add("mode", mode)
.add("modeParameter", modeParameter)
- .add("snapshotDate", snapshotDate)
+ .add("date", date)
.toString();
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolder.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolder.java
index 9a5a4c49509..da825b3e580 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolder.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolder.java
@@ -35,6 +35,14 @@ public interface PeriodHolder {
*/
boolean hasPeriod();
+
+ /**
+ * Finds out whether the holder contains a Period with a date
+ *
+ * @throws IllegalStateException if the periods haven't been initialized
+ */
+ boolean hasPeriodDate();
+
/**
* Retrieve the period from the Holder.
*
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImpl.java
index a88294aebdf..9330d635d16 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImpl.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImpl.java
@@ -48,6 +48,12 @@ public class PeriodHolderImpl implements PeriodHolder {
}
@Override
+ public boolean hasPeriodDate() {
+ checkHolderIsInitialized();
+ return period != null && period.getDate() != null;
+ }
+
+ @Override
public Period getPeriod() {
checkHolderIsInitialized();
checkState(period != null, "There is no period. Use hasPeriod() before calling this method");
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewReliabilityAndSecurityRatingMeasuresVisitor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewReliabilityAndSecurityRatingMeasuresVisitor.java
index c9d70906bbf..601742564ee 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewReliabilityAndSecurityRatingMeasuresVisitor.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewReliabilityAndSecurityRatingMeasuresVisitor.java
@@ -104,7 +104,7 @@ public class NewReliabilityAndSecurityRatingMeasuresVisitor extends PathAwareVis
}
private void computeAndSaveMeasures(Component component, Path<Counter> path) {
- if (!periodHolder.hasPeriod() && !analysisMetadataHolder.isPullRequest()) {
+ if (!periodHolder.hasPeriodDate() && !analysisMetadataHolder.isPullRequest()) {
return;
}
initRatingsToA(path);
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java
index cbd94f0f20b..b3e1c92de6d 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java
@@ -67,7 +67,7 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Se
@Override
public void visitProject(Component project, Path<SecurityReviewCounter> path) {
computeMeasure(project, path);
- if (!periodHolder.hasPeriod() && !analysisMetadataHolder.isPullRequest()) {
+ if (!periodHolder.hasPeriodDate() && !analysisMetadataHolder.isPullRequest()) {
return;
}
// The following measures are only computed on projects level as they are required to compute the others measures on applications
@@ -86,7 +86,7 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Se
}
private void computeMeasure(Component component, Path<SecurityReviewCounter> path) {
- if (!periodHolder.hasPeriod() && !analysisMetadataHolder.isPullRequest()) {
+ if (!periodHolder.hasPeriodDate() && !analysisMetadataHolder.isPullRequest()) {
return;
}
componentIssuesRepository.getIssues(component)
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/NewLinesRepository.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/NewLinesRepository.java
index 6f6ad4fac23..9ec855d4621 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/NewLinesRepository.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/NewLinesRepository.java
@@ -48,7 +48,7 @@ public class NewLinesRepository {
}
public boolean newLinesAvailable() {
- return analysisMetadataHolder.isPullRequest() || periodHolder.hasPeriod();
+ return analysisMetadataHolder.isPullRequest() || periodHolder.hasPeriodDate();
}
public Optional<Set<Integer>> getNewLines(Component file) {
@@ -66,7 +66,7 @@ public class NewLinesRepository {
* if a line is new or not.
*/
private Optional<Set<Integer>> computeNewLinesFromScm(Component component) {
- if (!periodHolder.hasPeriod() && !analysisMetadataHolder.isPullRequest()) {
+ if (!periodHolder.hasPeriodDate() && !analysisMetadataHolder.isPullRequest()) {
return Optional.empty();
}
@@ -80,7 +80,7 @@ public class NewLinesRepository {
Set<Integer> lines = new HashSet<>();
// in PRs, we consider changes introduced in this analysis as new, hence subtracting 1.
- long referenceDate = analysisMetadataHolder.isPullRequest() ? analysisMetadataHolder.getAnalysisDate() - 1 : periodHolder.getPeriod().getSnapshotDate();
+ long referenceDate = analysisMetadataHolder.isPullRequest() ? analysisMetadataHolder.getAnalysisDate() - 1 : periodHolder.getPeriod().getDate();
for (int i=0; i<allChangesets.length; i++) {
if (isLineInPeriod(allChangesets[i].getDate(), referenceDate)) {
lines.add(i+1);
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStep.java
index 8e8d003b2d2..bf2e27c5e92 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStep.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStep.java
@@ -34,6 +34,7 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.newcodeperiod.NewCodePeriodDao;
import org.sonar.db.newcodeperiod.NewCodePeriodDto;
+import org.sonar.db.newcodeperiod.NewCodePeriodType;
/**
* Populates the {@link PeriodHolder}
@@ -53,7 +54,7 @@ public class LoadPeriodsStep implements ComputationStep {
private final NewCodePeriodResolver resolver;
public LoadPeriodsStep(AnalysisMetadataHolder analysisMetadataHolder, NewCodePeriodDao newCodePeriodDao, TreeRootHolder treeRootHolder,
- PeriodHolderImpl periodsHolder, DbClient dbClient, NewCodePeriodResolver resolver) {
+ PeriodHolderImpl periodsHolder, DbClient dbClient, NewCodePeriodResolver resolver) {
this.analysisMetadataHolder = analysisMetadataHolder;
this.newCodePeriodDao = newCodePeriodDao;
this.treeRootHolder = treeRootHolder;
@@ -69,7 +70,7 @@ public class LoadPeriodsStep implements ComputationStep {
@Override
public void execute(ComputationStep.Context context) {
- if (analysisMetadataHolder.isFirstAnalysis() || !analysisMetadataHolder.isBranch()) {
+ if (!analysisMetadataHolder.isBranch()) {
periodsHolder.setPeriod(null);
return;
}
@@ -85,9 +86,14 @@ public class LoadPeriodsStep implements ComputationStep {
() -> getGlobalSetting(dbSession)
));
- long analysisDate = analysisMetadataHolder.getAnalysisDate();
- Period period = dto.map(d -> resolver.resolve(dbSession, branchUuid, d, analysisDate, projectVersion))
- .orElseGet(() -> resolver.resolve(dbSession, branchUuid, NewCodePeriodDto.defaultInstance(), analysisDate, projectVersion));
+ NewCodePeriodDto newCodePeriod = dto.orElse(NewCodePeriodDto.defaultInstance());
+
+ if (analysisMetadataHolder.isFirstAnalysis() && newCodePeriod.getType() != NewCodePeriodType.REFERENCE_BRANCH) {
+ periodsHolder.setPeriod(null);
+ return;
+ }
+
+ Period period = resolver.resolve(dbSession, branchUuid, newCodePeriod, projectVersion);
periodsHolder.setPeriod(period);
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java
index 86d5c3d89db..9fe44ff2796 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java
@@ -92,6 +92,7 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep {
}
private void loadMetadata(ScannerReport.Metadata reportMetadata) {
+ analysisMetadata.setForkDate(reportMetadata.getForkDate() > 0 ? reportMetadata.getForkDate() : null);
analysisMetadata.setAnalysisDate(reportMetadata.getAnalysisDate());
analysisMetadata.setRootComponentRef(reportMetadata.getRootComponentRef());
analysisMetadata.setCrossProjectDuplicationEnabled(reportMetadata.getCrossProjectDuplicationActivated());
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistAnalysisStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistAnalysisStep.java
index b5af1a33a9b..8f730c72cba 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistAnalysisStep.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistAnalysisStep.java
@@ -97,7 +97,7 @@ public class PersistAnalysisStep implements ComputationStep {
Period period = periodHolder.getPeriod();
snapshotDto.setPeriodMode(period.getMode());
snapshotDto.setPeriodParam(period.getModeParameter());
- snapshotDto.setPeriodDate(period.getSnapshotDate());
+ snapshotDto.setPeriodDate(period.getDate());
}
private SnapshotDto createAnalysis(String snapshotUuid, Component component) {
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java
index b44c61bdc63..8f17bc111fb 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java
@@ -139,6 +139,33 @@ public class AnalysisMetadataHolderImplTest {
}
@Test
+ public void getForkDate_returns_date_with_same_time_as_the_one_set_with_setForkDate() {
+
+ underTest.setForkDate(SOME_DATE);
+
+ assertThat(underTest.getForkDate()).isEqualTo(SOME_DATE);
+ }
+
+ @Test
+ public void getForkDate_throws_ISE_when_holder_is_not_initialized() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Fork date has not been set");
+
+ new AnalysisMetadataHolderImpl(editionProvider).getForkDate();
+ }
+
+ @Test
+ public void setForkDate_throws_ISE_when_called_twice() {
+ AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(editionProvider);
+ underTest.setForkDate(SOME_DATE);
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Fork date has already been set");
+
+ underTest.setForkDate(SOME_DATE);
+ }
+
+ @Test
public void hasAnalysisDateBeenSet_returns_false_when_holder_is_not_initialized() {
assertThat(new AnalysisMetadataHolderImpl(editionProvider).hasAnalysisDateBeenSet()).isFalse();
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java
index 9e8f6f17d0e..8943b9682fe 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java
@@ -285,15 +285,15 @@ public class IssueCounterTest {
underTest.beforeComponent(FILE1);
// created before -> existing issues (so ignored)
- underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER, period.getSnapshotDate() - 1000000L).setType(RuleType.CODE_SMELL));
+ underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER, period.getDate() - 1000000L).setType(RuleType.CODE_SMELL));
// created during the first analysis starting the period -> existing issues (so ignored)
- underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER, period.getSnapshotDate()).setType(RuleType.BUG));
+ underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER, period.getDate()).setType(RuleType.BUG));
// created after -> 4 new issues but 1 is closed
- underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, CRITICAL, period.getSnapshotDate() + 100000L).setType(RuleType.CODE_SMELL));
- underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, CRITICAL, period.getSnapshotDate() + 100000L).setType(RuleType.BUG));
- underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR, period.getSnapshotDate() + 200000L).setType(RuleType.BUG));
- underTest.onIssue(FILE1, createSecurityHotspot(period.getSnapshotDate() + 100000L));
- underTest.onIssue(FILE1, createSecurityHotspot(period.getSnapshotDate() + 100000L).setResolution(RESOLUTION_WONT_FIX).setStatus(STATUS_CLOSED));
+ underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, CRITICAL, period.getDate() + 100000L).setType(RuleType.CODE_SMELL));
+ underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, CRITICAL, period.getDate() + 100000L).setType(RuleType.BUG));
+ underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR, period.getDate() + 200000L).setType(RuleType.BUG));
+ underTest.onIssue(FILE1, createSecurityHotspot(period.getDate() + 100000L));
+ underTest.onIssue(FILE1, createSecurityHotspot(period.getDate() + 100000L).setResolution(RESOLUTION_WONT_FIX).setStatus(STATUS_CLOSED));
underTest.afterComponent(FILE1);
underTest.beforeComponent(FILE2);
@@ -368,15 +368,15 @@ public class IssueCounterTest {
underTest.beforeComponent(FILE1);
// created before -> existing issues (so ignored)
- underTest.onIssue(FILE1, createSecurityHotspot(period.getSnapshotDate() - 1000000L));
+ underTest.onIssue(FILE1, createSecurityHotspot(period.getDate() - 1000000L));
// created during the first analysis starting the period -> existing issues (so ignored)
- underTest.onIssue(FILE1, createSecurityHotspot(period.getSnapshotDate()));
+ underTest.onIssue(FILE1, createSecurityHotspot(period.getDate()));
// created after, but closed
- underTest.onIssue(FILE1, createSecurityHotspot(period.getSnapshotDate() + 100000L).setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_WONT_FIX));
+ underTest.onIssue(FILE1, createSecurityHotspot(period.getDate() + 100000L).setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_WONT_FIX));
for (String severity : Arrays.asList(CRITICAL, BLOCKER, MAJOR)) {
- DefaultIssue issue = createSecurityHotspot(period.getSnapshotDate() + 100000L);
+ DefaultIssue issue = createSecurityHotspot(period.getDate() + 100000L);
issue.setSeverity(severity);
underTest.onIssue(FILE1, issue);
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregatorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregatorTest.java
index 305e85d6e3f..e0478a02e5c 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregatorTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewEffortAggregatorTest.java
@@ -55,9 +55,9 @@ public class NewEffortAggregatorTest {
private static final Period PERIOD = new Period(NewCodePeriodType.PREVIOUS_VERSION.name(), null, 1_500_000_000L);
private static final long[] OLD_ISSUES_DATES = new long[]{
- PERIOD.getSnapshotDate(),
- PERIOD.getSnapshotDate() - 1,
- PERIOD.getSnapshotDate() - 1_200_000L,
+ PERIOD.getDate(),
+ PERIOD.getDate() - 1,
+ PERIOD.getDate() - 1_200_000L,
};
private static final Component FILE = ReportComponent.builder(Component.Type.FILE, 1).setUuid("FILE").build();
@@ -296,7 +296,7 @@ public class NewEffortAggregatorTest {
return newCodeSmellIssueWithoutEffort()
.setEffort(Duration.create(effort))
.setType(RuleType.CODE_SMELL)
- .setCreationDate(new Date(PERIOD.getSnapshotDate() + 10_000L));
+ .setCreationDate(new Date(PERIOD.getDate() + 10_000L));
}
private DefaultIssue oldCodeSmellIssue(long effort) {
@@ -310,7 +310,7 @@ public class NewEffortAggregatorTest {
return newCodeSmellIssueWithoutEffort()
.setEffort(Duration.create(effort))
.setType(RuleType.BUG)
- .setCreationDate(new Date(PERIOD.getSnapshotDate() + 10_000L));
+ .setCreationDate(new Date(PERIOD.getDate() + 10_000L));
}
private DefaultIssue oldBugIssue(long effort) {
@@ -324,7 +324,7 @@ public class NewEffortAggregatorTest {
return newCodeSmellIssueWithoutEffort()
.setEffort(Duration.create(effort))
.setType(RuleType.VULNERABILITY)
- .setCreationDate(new Date(PERIOD.getSnapshotDate() + 10_000L));
+ .setCreationDate(new Date(PERIOD.getDate() + 10_000L));
}
private DefaultIssue oldVulnerabilityIssue(long effort) {
@@ -337,7 +337,7 @@ public class NewEffortAggregatorTest {
private static DefaultIssue newCodeSmellIssueWithoutEffort() {
return new DefaultIssue()
.setType(CODE_SMELL)
- .setCreationDate(new Date(PERIOD.getSnapshotDate() + 10_000L));
+ .setCreationDate(new Date(PERIOD.getDate() + 10_000L));
}
private static DefaultIssue newBugIssueWithoutEffort() {
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImplTest.java
index 59a9dbe754e..0e91051b4f5 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImplTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderImplTest.java
@@ -64,9 +64,17 @@ public class PeriodHolderImplTest {
}
@Test
+ public void hasPeriodDate_returns_false_if_date_is_null() {
+ underTest.setPeriod(createPeriodWithoutDate());
+ assertThat(underTest.hasPeriod()).isTrue();
+ assertThat(underTest.hasPeriodDate()).isFalse();
+ }
+
+ @Test
public void hasPeriod_returns_true_only_if_period_exists_in_holder() {
underTest.setPeriod(createPeriod());
assertThat(underTest.hasPeriod()).isTrue();
+ assertThat(underTest.hasPeriodDate()).isTrue();
}
@Test
@@ -80,4 +88,8 @@ public class PeriodHolderImplTest {
private static Period createPeriod() {
return new Period(1 + "mode", null, 1000L);
}
+
+ private static Period createPeriodWithoutDate() {
+ return new Period(1 + "mode", null, null);
+ }
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderRule.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderRule.java
index e8d70adb878..b68559c35de 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderRule.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodHolderRule.java
@@ -57,6 +57,11 @@ public class PeriodHolderRule implements TestRule, PeriodHolder {
}
@Override
+ public boolean hasPeriodDate() {
+ return delegate.hasPeriodDate();
+ }
+
+ @Override
public Period getPeriod() {
return delegate.getPeriod();
}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodTest.java
index b47743e7eb1..feb1b049231 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/period/PeriodTest.java
@@ -40,13 +40,13 @@ public class PeriodTest {
assertThat(period.getMode()).isEqualTo(NewCodePeriodType.PREVIOUS_VERSION.name());
assertThat(period.getModeParameter()).isEqualTo(SOME_MODE_PARAM);
- assertThat(period.getSnapshotDate()).isEqualTo(SOME_SNAPSHOT_DATE);
+ assertThat(period.getDate()).isEqualTo(SOME_SNAPSHOT_DATE);
}
@Test
public void verify_to_string() {
assertThat(new Period(NewCodePeriodType.PREVIOUS_VERSION.name(), "2.3", 1420034400000L).toString())
- .isEqualTo("Period{mode=PREVIOUS_VERSION, modeParameter=2.3, snapshotDate=1420034400000}");
+ .isEqualTo("Period{mode=PREVIOUS_VERSION, modeParameter=2.3, date=1420034400000}");
}
@Test
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStepTest.java
index b58ffb3c481..56ec2a33de2 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadPeriodsStepTest.java
@@ -87,7 +87,7 @@ public class LoadPeriodsStepTest extends BaseStepTest {
private PeriodHolderImpl periodsHolder = new PeriodHolderImpl();
private System2 system2Mock = mock(System2.class);
private NewCodePeriodDao dao = new NewCodePeriodDao(system2Mock, new SequenceUuidFactory());
- private NewCodePeriodResolver newCodePeriodResolver = new NewCodePeriodResolver(dbTester.getDbClient());
+ private NewCodePeriodResolver newCodePeriodResolver = new NewCodePeriodResolver(dbTester.getDbClient(), analysisMetadataHolder);
private ZonedDateTime analysisDate = ZonedDateTime.of(2019, 3, 20, 5, 30, 40, 0, ZoneId.systemDefault());
private LoadPeriodsStep underTest = new LoadPeriodsStep(analysisMetadataHolder, dao, treeRootHolder, periodsHolder, dbTester.getDbClient(), newCodePeriodResolver);
@@ -112,20 +112,23 @@ public class LoadPeriodsStepTest extends BaseStepTest {
@Test
public void no_period_on_first_analysis() {
+ setupRoot(project);
+
when(analysisMetadataHolder.isFirstAnalysis()).thenReturn(true);
underTest.execute(new TestComputationStepContext());
verify(analysisMetadataHolder).isFirstAnalysis();
+ verify(analysisMetadataHolder).isBranch();
+ verify(analysisMetadataHolder).getProject();
assertThat(periodsHolder.hasPeriod()).isFalse();
verifyNoMoreInteractions(analysisMetadataHolder);
}
@Test
- public void no_period_if_not_LLB() {
+ public void no_period_date_if_not_branch() {
when(analysisMetadataHolder.isBranch()).thenReturn(false);
underTest.execute(new TestComputationStepContext());
- verify(analysisMetadataHolder).isFirstAnalysis();
verify(analysisMetadataHolder).isBranch();
assertThat(periodsHolder.hasPeriod()).isFalse();
verifyNoMoreInteractions(analysisMetadataHolder);
@@ -170,6 +173,30 @@ public class LoadPeriodsStepTest extends BaseStepTest {
testNumberOfDays(branch);
}
+ @Test
+ public void load_reference_branch() {
+ ComponentDto branch = dbTester.components().insertProjectBranch(project);
+ setupRoot(branch);
+
+ setProjectPeriod(project.uuid(), NewCodePeriodType.REFERENCE_BRANCH, "master");
+ when(analysisMetadataHolder.getForkDate()).thenReturn(123456789L);
+
+ underTest.execute(new TestComputationStepContext());
+ assertPeriod(NewCodePeriodType.REFERENCE_BRANCH, "master", 123456789L);
+ }
+
+ @Test
+ public void load_reference_branch_without_fork_date_in_report() {
+ ComponentDto branch = dbTester.components().insertProjectBranch(project);
+ setupRoot(branch);
+
+ setProjectPeriod(project.uuid(), NewCodePeriodType.REFERENCE_BRANCH, "master");
+ when(analysisMetadataHolder.getForkDate()).thenReturn(null);
+
+ underTest.execute(new TestComputationStepContext());
+ assertPeriod(NewCodePeriodType.REFERENCE_BRANCH, "master", null);
+ }
+
private void testNumberOfDays(ComponentDto projectOrBranch) {
setupRoot(projectOrBranch);
@@ -455,12 +482,12 @@ public class LoadPeriodsStepTest extends BaseStepTest {
dbTester.newCodePeriods().insert(type, value);
}
- private void assertPeriod(NewCodePeriodType type, @Nullable String value, long snapshotDate) {
+ private void assertPeriod(NewCodePeriodType type, @Nullable String value, @Nullable Long date) {
Period period = periodsHolder.getPeriod();
assertThat(period).isNotNull();
assertThat(period.getMode()).isEqualTo(type.name());
assertThat(period.getModeParameter()).isEqualTo(value);
- assertThat(period.getSnapshotDate()).isEqualTo(snapshotDate);
+ assertThat(period.getDate()).isEqualTo(date);
}
private void verifyDebugLogs(String log, String... otherLogs) {
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java
index d70e526d92a..17d681d58e9 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java
@@ -116,6 +116,18 @@ public class LoadReportAnalysisMetadataHolderStepTest {
}
@Test
+ public void set_fork_date() {
+ reportReader.setMetadata(
+ newBatchReportBuilder()
+ .setForkDate(ANALYSIS_DATE)
+ .build());
+
+ underTest.execute(new TestComputationStepContext());
+
+ assertThat(analysisMetadataHolder.getForkDate()).isEqualTo(ANALYSIS_DATE);
+ }
+
+ @Test
public void set_project_from_dto() {
reportReader.setMetadata(
newBatchReportBuilder()
diff --git a/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java b/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java
index bb15edf1fc0..24fe8bd4ca6 100644
--- a/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java
+++ b/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java
@@ -39,29 +39,18 @@ import static org.apache.commons.lang.StringUtils.defaultIfBlank;
public class AnalysisMetadataHolderRule extends ExternalResource implements MutableAnalysisMetadataHolder {
private final InitializedProperty<Boolean> organizationsEnabled = new InitializedProperty<>();
-
private final InitializedProperty<Organization> organization = new InitializedProperty<>();
-
private final InitializedProperty<String> uuid = new InitializedProperty<>();
-
private final InitializedProperty<Long> analysisDate = new InitializedProperty<>();
-
+ private final InitializedProperty<Long> forkDate = new InitializedProperty<>();
private final InitializedProperty<Analysis> baseAnalysis = new InitializedProperty<>();
-
private final InitializedProperty<Boolean> crossProjectDuplicationEnabled = new InitializedProperty<>();
-
private final InitializedProperty<Branch> branch = new InitializedProperty<>();
-
private final InitializedProperty<String> pullRequestId = new InitializedProperty<>();
-
private final InitializedProperty<Project> project = new InitializedProperty<>();
-
private final InitializedProperty<Integer> rootComponentRef = new InitializedProperty<>();
-
private final InitializedProperty<Map<String, QualityProfile>> qProfilesPerLanguage = new InitializedProperty<>();
-
private final InitializedProperty<Map<String, ScannerPlugin>> pluginsByKey = new InitializedProperty<>();
-
private final InitializedProperty<String> scmRevision = new InitializedProperty<>();
@Override
@@ -121,12 +110,23 @@ public class AnalysisMetadataHolderRule extends ExternalResource implements Muta
return this;
}
+ @Override public MutableAnalysisMetadataHolder setForkDate(@Nullable Long date) {
+ forkDate.setProperty(date);
+ return this;
+ }
+
@Override
public long getAnalysisDate() {
checkState(analysisDate.isInitialized(), "Analysis date has not been set");
return this.analysisDate.getProperty();
}
+ @CheckForNull
+ @Override
+ public Long getForkDate() {
+ return forkDate.getProperty();
+ }
+
@Override
public boolean hasAnalysisDateBeenSet() {
return analysisDate.isInitialized();
diff --git a/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java b/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java
index 05ccaba95d9..e8f09ed99d9 100644
--- a/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java
+++ b/server/sonar-ce-task-projectanalysis/src/testFixtures/java/org/sonar/ce/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java
@@ -77,11 +77,21 @@ public class MutableAnalysisMetadataHolderRule extends ExternalResource implemen
return this;
}
+ @Override public MutableAnalysisMetadataHolder setForkDate(@Nullable Long date) {
+ return delegate.setForkDate(date);
+ }
+
@Override
public long getAnalysisDate() {
return delegate.getAnalysisDate();
}
+ @CheckForNull
+ @Override
+ public Long getForkDate() {
+ return delegate.getForkDate();
+ }
+
@Override
public boolean hasAnalysisDateBeenSet() {
return delegate.hasAnalysisDateBeenSet();
@@ -181,8 +191,6 @@ public class MutableAnalysisMetadataHolderRule extends ExternalResource implemen
return delegate.getScannerPluginsByKey();
}
-
-
@Override
public MutableAnalysisMetadataHolder setScmRevision(String scmRevisionId) {
delegate.setScmRevision(scmRevisionId);
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodType.java b/server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodType.java
index 232f1f36d91..d74a4cdece3 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodType.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/newcodeperiod/NewCodePeriodType.java
@@ -22,5 +22,6 @@ package org.sonar.db.newcodeperiod;
public enum NewCodePeriodType {
PREVIOUS_VERSION,
NUMBER_OF_DAYS,
- SPECIFIC_ANALYSIS
+ SPECIFIC_ANALYSIS,
+ REFERENCE_BRANCH
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SnapshotDtoToWsPeriod.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SnapshotDtoToWsPeriod.java
index 541a0302568..4f0214825a2 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SnapshotDtoToWsPeriod.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SnapshotDtoToWsPeriod.java
@@ -32,15 +32,11 @@ class SnapshotDtoToWsPeriod {
}
static Optional<Measures.Period> snapshotToWsPeriods(@Nullable SnapshotDto snapshot) {
- if (snapshot == null) {
+ if (snapshot == null || snapshot.getPeriodMode() == null) {
return Optional.empty();
}
- if (snapshot.getPeriodDate() != null) {
- return Optional.of(snapshotDtoToWsPeriod(snapshot));
- }
-
- return Optional.empty();
+ return Optional.of(snapshotDtoToWsPeriod(snapshot));
}
private static Measures.Period snapshotDtoToWsPeriod(SnapshotDto snapshot) {
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ListAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ListAction.java
index 161afe2d542..13326b68047 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ListAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ListAction.java
@@ -157,6 +157,8 @@ public class ListAction implements NewCodePeriodsWsAction {
return NewCodePeriods.NewCodePeriodType.PREVIOUS_VERSION;
case SPECIFIC_ANALYSIS:
return NewCodePeriods.NewCodePeriodType.SPECIFIC_ANALYSIS;
+ case REFERENCE_BRANCH:
+ return NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH;
default:
throw new IllegalStateException("Unexpected type: " + type);
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/SetAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/SetAction.java
index b79f029d3a0..c82efac609b 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/SetAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/SetAction.java
@@ -47,6 +47,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static org.sonar.db.newcodeperiod.NewCodePeriodType.NUMBER_OF_DAYS;
import static org.sonar.db.newcodeperiod.NewCodePeriodType.PREVIOUS_VERSION;
+import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH;
import static org.sonar.db.newcodeperiod.NewCodePeriodType.SPECIFIC_ANALYSIS;
public class SetAction implements NewCodePeriodsWsAction {
@@ -60,8 +61,8 @@ public class SetAction implements NewCodePeriodsWsAction {
private static final String END_ITEM_LIST = "</li>";
private static final Set<NewCodePeriodType> OVERALL_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS);
- private static final Set<NewCodePeriodType> PROJECT_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS);
- private static final Set<NewCodePeriodType> BRANCH_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS, SPECIFIC_ANALYSIS);
+ private static final Set<NewCodePeriodType> PROJECT_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS, REFERENCE_BRANCH);
+ private static final Set<NewCodePeriodType> BRANCH_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS, SPECIFIC_ANALYSIS, REFERENCE_BRANCH);
private final DbClient dbClient;
private final UserSession userSession;
@@ -105,7 +106,8 @@ public class SetAction implements NewCodePeriodsWsAction {
BEGIN_LIST +
BEGIN_ITEM_LIST + SPECIFIC_ANALYSIS.name() + " - can be set at branch level only" + END_ITEM_LIST +
BEGIN_ITEM_LIST + PREVIOUS_VERSION.name() + " - can be set at any level (global, project, branch)" + END_ITEM_LIST +
- BEGIN_ITEM_LIST + NUMBER_OF_DAYS.name() + " - can be set can be set at any level (global, project, branch)" + END_ITEM_LIST +
+ BEGIN_ITEM_LIST + NUMBER_OF_DAYS.name() + " - can be set at any level (global, project, branch)" + END_ITEM_LIST +
+ BEGIN_ITEM_LIST + REFERENCE_BRANCH.name() + " - can only be set for projects and branches" + END_ITEM_LIST +
END_LIST
);
action.createParam(PARAM_VALUE)
@@ -115,6 +117,7 @@ public class SetAction implements NewCodePeriodsWsAction {
BEGIN_ITEM_LIST + "the uuid of an analysis, when type is " + SPECIFIC_ANALYSIS.name() + END_ITEM_LIST +
BEGIN_ITEM_LIST + "no value, when type is " + PREVIOUS_VERSION.name() + END_ITEM_LIST +
BEGIN_ITEM_LIST + "a number, when type is " + NUMBER_OF_DAYS.name() + END_ITEM_LIST +
+ BEGIN_ITEM_LIST + "a string, when type is " + REFERENCE_BRANCH.name() + END_ITEM_LIST +
END_LIST
);
}
@@ -182,6 +185,10 @@ public class SetAction implements NewCodePeriodsWsAction {
SnapshotDto analysis = getAnalysis(dbSession, value, project, branch);
dto.setValue(analysis.getUuid());
break;
+ case REFERENCE_BRANCH:
+ requireValue(type, value);
+ dto.setValue(value);
+ break;
default:
throw new IllegalStateException("Unexpected type: " + type);
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ShowAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ShowAction.java
index 43163e09d5d..ca79dfe3966 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ShowAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ShowAction.java
@@ -38,6 +38,8 @@ import org.sonar.server.user.UserSession;
import org.sonarqube.ws.NewCodePeriods;
import static java.lang.String.format;
+import static org.sonar.db.permission.OrganizationPermission.SCAN;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.NewCodePeriods.ShowWSResponse;
@@ -60,11 +62,12 @@ public class ShowAction implements NewCodePeriodsWsAction {
@Override
public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction("show")
- .setDescription("Shows a setting for the New Code Period.<br>" +
- "Requires one of the following permissions: " +
+ .setDescription("Shows a setting for the New Code Period.<br> "
+ + "If the component requested doesn't exist or if no new code period is set for it, a value is inherited from the project or from the global setting." +
+ "Requires one of the following permissions if a component is specified: " +
"<ul>" +
- "<li>'Administer System'</li>" +
"<li>'Administer' rights on the specified component</li>" +
+ "<li>'Execute analysis' rights on the specified component</li>" +
"</ul>")
.setSince("8.0")
.setResponseExample(getClass().getResource("show-example.json"))
@@ -88,16 +91,25 @@ public class ShowAction implements NewCodePeriodsWsAction {
try (DbSession dbSession = dbClient.openSession(false)) {
ProjectDto project = null;
BranchDto branch = null;
+ boolean inherited = false;
if (projectKey != null) {
- project = getProject(dbSession, projectKey);
- userSession.checkProjectPermission(UserRole.ADMIN, project);
- if (branchKey != null) {
- branch = getBranch(dbSession, project, branchKey);
+ try {
+ project = getProject(dbSession, projectKey);
+ checkPermission(project);
+ if (branchKey != null) {
+ try {
+ branch = getBranch(dbSession, project, branchKey);
+ } catch (NotFoundException e) {
+ inherited = true;
+ }
+ }
+ } catch (NotFoundException e) {
+ inherited = true;
}
}
- ShowWSResponse.Builder builder = get(dbSession, project, branch, false);
+ ShowWSResponse.Builder builder = get(dbSession, project, branch, inherited);
if (project != null) {
builder.setProjectKey(project.getKey());
@@ -109,6 +121,15 @@ public class ShowAction implements NewCodePeriodsWsAction {
}
}
+ private void checkPermission(ProjectDto project) {
+ if (userSession.hasProjectPermission(UserRole.SCAN, project) ||
+ userSession.hasProjectPermission(UserRole.ADMIN, project) ||
+ userSession.hasPermission(SCAN, project.getOrganizationUuid())) {
+ return;
+ }
+ throw insufficientPrivilegesException();
+ }
+
private ShowWSResponse.Builder get(DbSession dbSession, @Nullable ProjectDto project, @Nullable BranchDto branch, boolean inherited) {
if (project == null) {
Optional<NewCodePeriodDto> dto = newCodePeriodDao.selectGlobal(dbSession);
@@ -151,6 +172,8 @@ public class ShowAction implements NewCodePeriodsWsAction {
return NewCodePeriods.NewCodePeriodType.PREVIOUS_VERSION;
case SPECIFIC_ANALYSIS:
return NewCodePeriods.NewCodePeriodType.SPECIFIC_ANALYSIS;
+ case REFERENCE_BRANCH:
+ return NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH;
default:
throw new IllegalStateException("Unexpected type: " + type);
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java
index 8928346d324..391c97d91c3 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java
@@ -49,8 +49,7 @@ public class QualityGateDetailsFormatter {
return newResponseWithoutQualityGateDetails();
}
- JsonParser parser = new JsonParser();
- JsonObject json = parser.parse(optionalMeasureData.get()).getAsJsonObject();
+ JsonObject json = JsonParser.parseString(optionalMeasureData.get()).getAsJsonObject();
ProjectStatusResponse.Status qualityGateStatus = measureLevelToQualityGateStatus(json.get("level").getAsString());
projectStatusBuilder.setStatus(qualityGateStatus);
@@ -80,8 +79,8 @@ public class QualityGateDetailsFormatter {
periodBuilder.clear();
SnapshotDto snapshot = this.optionalSnapshot.get();
- String periodMode = snapshot.getPeriodMode();
- if (isNullOrEmpty(periodMode)) {
+
+ if (isNullOrEmpty(snapshot.getPeriodMode())) {
return;
}
periodBuilder.setIndex(1);
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/SetActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/SetActionTest.java
index 2ebdbdbf1a5..122414be00f 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/SetActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/SetActionTest.java
@@ -126,7 +126,7 @@ public class SetActionTest {
logInAsProjectAdministrator(project);
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Invalid type 'SPECIFIC_ANALYSIS'. Projects can only be set with types: [PREVIOUS_VERSION, NUMBER_OF_DAYS]");
+ expectedException.expectMessage("Invalid type 'SPECIFIC_ANALYSIS'. Projects can only be set with types: [PREVIOUS_VERSION, NUMBER_OF_DAYS, REFERENCE_BRANCH]");
ws.newRequest()
.setParam("project", project.getKey())
@@ -322,7 +322,8 @@ public class SetActionTest {
return new Object[][]{
{NewCodePeriodType.NUMBER_OF_DAYS, "5"},
{NewCodePeriodType.SPECIFIC_ANALYSIS, "analysis-uuid"},
- {NewCodePeriodType.PREVIOUS_VERSION, null}
+ {NewCodePeriodType.PREVIOUS_VERSION, null},
+ {NewCodePeriodType.REFERENCE_BRANCH, "master"}
};
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/ShowActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/ShowActionTest.java
index e2cbfdea650..b67fe6cb374 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/ShowActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/ShowActionTest.java
@@ -38,7 +38,6 @@ import org.sonar.db.newcodeperiod.NewCodePeriodType;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.component.TestComponentFinder;
import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.NewCodePeriods;
@@ -88,31 +87,21 @@ public class ShowActionTest {
}
@Test
- public void throw_NFE_if_project_not_found() {
- expectedException.expect(NotFoundException.class);
- expectedException.expectMessage("Project 'unknown' not found");
-
- ws.newRequest()
- .setParam("project", "unknown")
- .execute();
- }
-
- @Test
- public void throw_NFE_if_branch_not_found() {
+ public void throw_FE_if_no_project_permission() {
ComponentDto project = componentDb.insertPublicProject();
- logInAsProjectAdministrator(project);
- expectedException.expect(NotFoundException.class);
- expectedException.expectMessage("Branch 'unknown' in project '" + project.getKey() + "' not found");
+ expectedException.expect(ForbiddenException.class);
+ expectedException.expectMessage("Insufficient privileges");
ws.newRequest()
.setParam("project", project.getKey())
- .setParam("branch", "unknown")
.execute();
}
@Test
- public void throw_FE_if_no_project_permission() {
+ public void throw_FE_if_project_issue_admin() {
ComponentDto project = componentDb.insertPublicProject();
+ logInAsProjectIssueAdmin(project);
+
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");
@@ -217,6 +206,32 @@ public class ShowActionTest {
assertResponse(response, project.getKey(), "branch", NewCodePeriods.NewCodePeriodType.NUMBER_OF_DAYS, "3", true);
}
+ @Test
+ public void show_inherited_if_project_not_found() {
+ tester.insert(new NewCodePeriodDto().setType(NewCodePeriodType.NUMBER_OF_DAYS).setValue("3"));
+
+ ShowWSResponse response = ws.newRequest()
+ .setParam("project", "unknown")
+ .executeProtobuf(ShowWSResponse.class);
+
+ assertResponse(response, "", "", NewCodePeriods.NewCodePeriodType.NUMBER_OF_DAYS, "3", true);
+ }
+
+ @Test
+ public void show_inherited_if_branch_not_found() {
+ ComponentDto project = componentDb.insertPublicProject();
+ logInAsProjectScan(project);
+
+ tester.insert(project.projectUuid(), NewCodePeriodType.NUMBER_OF_DAYS, "3");
+
+ ShowWSResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("branch", "unknown")
+ .executeProtobuf(ShowWSResponse.class);
+
+ assertResponse(response, project.getKey(), "", NewCodePeriods.NewCodePeriodType.NUMBER_OF_DAYS, "3", true);
+ }
+
private void assertResponse(ShowWSResponse response, String projectKey, String branchKey, NewCodePeriods.NewCodePeriodType type, String value, boolean inherited) {
assertThat(response.getBranchKey()).isEqualTo(branchKey);
assertThat(response.getProjectKey()).isEqualTo(projectKey);
@@ -229,4 +244,12 @@ public class ShowActionTest {
userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
}
+ private void logInAsProjectScan(ComponentDto project) {
+ userSession.logIn().addProjectPermission(UserRole.SCAN, project);
+ }
+
+ private void logInAsProjectIssueAdmin(ComponentDto project) {
+ userSession.logIn().addProjectPermission(UserRole.ISSUE_ADMIN, project);
+ }
+
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmProvider.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmProvider.java
index be44d13eb41..379a3cfddf3 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmProvider.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/scm/ScmProvider.java
@@ -21,6 +21,7 @@ package org.sonar.api.batch.scm;
import java.io.File;
import java.nio.file.Path;
+import java.time.Instant;
import java.util.Map;
import java.util.Set;
import javax.annotation.CheckForNull;
@@ -82,6 +83,17 @@ public abstract class ScmProvider {
}
/**
+ * Find the date of the merge base between the current branch and the given reference branch.
+ *
+ * @return null if the SCM provider was not able to compute the date
+ * @since 8.4
+ */
+ @CheckForNull
+ public Instant forkDate(String referenceBranchName, Path rootBaseDir) {
+ return null;
+ }
+
+ /**
* The relative path from SCM root
* @since 7.0
*/
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();
+ }
+}
diff --git a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
index 7aad1a281af..b984bf7abbb 100644
--- a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
+++ b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
@@ -56,6 +56,8 @@ message Metadata {
string target_branch_name = 18;
+ int64 forkDate = 19;
+
message QProfile {
string key = 1;
string name = 2;
diff --git a/sonar-ws/src/main/protobuf/ws-newcodeperiods.proto b/sonar-ws/src/main/protobuf/ws-newcodeperiods.proto
index 73b7fdc5350..56d874e7b8f 100644
--- a/sonar-ws/src/main/protobuf/ws-newcodeperiods.proto
+++ b/sonar-ws/src/main/protobuf/ws-newcodeperiods.proto
@@ -44,4 +44,5 @@ enum NewCodePeriodType {
PREVIOUS_VERSION = 1;
NUMBER_OF_DAYS = 2;
SPECIFIC_ANALYSIS = 3;
+ REFERENCE_BRANCH = 4;
}