aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2016-03-21 18:52:14 +0100
committerJulien HENRY <julien.henry@sonarsource.com>2016-03-24 09:57:28 +0100
commit0fd2f2204c44b6b5ec82741233940b8471df06d8 (patch)
treead129d4af5e0cdbaf003c8849a858cfce138ab4e
parentcc9c506efd50786649658baa0d243654c59c512e (diff)
downloadsonarqube-0fd2f2204c44b6b5ec82741233940b8471df06d8.tar.gz
sonarqube-0fd2f2204c44b6b5ec82741233940b8471df06d8.zip
SONAR-7368 Deprecate quality_profiles measure and add data in scanner report
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolder.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderImpl.java18
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolder.java7
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeQProfileMeasureStep.java51
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadReportAnalysisMetadataHolderStep.java15
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportComputationSteps.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/util/InitializedProperty.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderRule.java15
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolderRule.java13
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeQProfileMeasureStepTest.java101
-rw-r--r--sonar-core/src/main/java/org/sonar/core/metric/BatchMetrics.java5
-rw-r--r--sonar-core/src/test/java/org/sonar/core/metric/BatchMetricsTest.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java4
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/batch/report/MetadataPublisher.java13
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/batch/rule/QProfileSensor.java72
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/batch/rule/UsedQProfiles.java112
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java2
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java2
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java3
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java22
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java135
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java72
-rw-r--r--sonar-scanner-protocol/src/main/protobuf/scanner_report.proto9
23 files changed, 207 insertions, 476 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolder.java
index 26008c8233e..93565cabbd3 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolder.java
@@ -19,7 +19,9 @@
*/
package org.sonar.server.computation.analysis;
+import java.util.Map;
import javax.annotation.CheckForNull;
+import org.sonar.server.computation.qualityprofile.QualityProfile;
import org.sonar.server.computation.snapshot.Snapshot;
public interface AnalysisMetadataHolder {
@@ -60,4 +62,6 @@ public interface AnalysisMetadataHolder {
*/
int getRootComponentRef();
+ Map<String, QualityProfile> getQProfilesByLanguage();
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderImpl.java
index 6184b12ba16..003d0c7509f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderImpl.java
@@ -19,8 +19,11 @@
*/
package org.sonar.server.computation.analysis;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import org.sonar.server.computation.qualityprofile.QualityProfile;
import org.sonar.server.computation.snapshot.Snapshot;
import org.sonar.server.computation.util.InitializedProperty;
@@ -38,6 +41,8 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder
private InitializedProperty<Integer> rootComponentRef = new InitializedProperty<>();
+ private InitializedProperty<Map<String, QualityProfile>> qProfilesPerLanguage = new InitializedProperty<>();
+
@Override
public MutableAnalysisMetadataHolder setAnalysisDate(long date) {
checkState(!analysisDate.isInitialized(), "Analysis date has already been set");
@@ -109,4 +114,17 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder
return rootComponentRef.getProperty();
}
+ @Override
+ public MutableAnalysisMetadataHolder setQProfilesByLanguage(Map<String, QualityProfile> qprofilesByLanguage) {
+ checkState(!this.qProfilesPerLanguage.isInitialized(), "QProfiles by language has already been set");
+ this.qProfilesPerLanguage.setProperty(ImmutableMap.copyOf(qprofilesByLanguage));
+ return this;
+ }
+
+ @Override
+ public Map<String, QualityProfile> getQProfilesByLanguage() {
+ checkState(qProfilesPerLanguage.isInitialized(), "QProfiles by language has not been set");
+ return qProfilesPerLanguage.getProperty();
+ }
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolder.java
index ad2eae0ae98..20326ee5ec1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolder.java
@@ -19,7 +19,9 @@
*/
package org.sonar.server.computation.analysis;
+import java.util.Map;
import javax.annotation.Nullable;
+import org.sonar.server.computation.qualityprofile.QualityProfile;
import org.sonar.server.computation.snapshot.Snapshot;
public interface MutableAnalysisMetadataHolder extends AnalysisMetadataHolder {
@@ -49,4 +51,9 @@ public interface MutableAnalysisMetadataHolder extends AnalysisMetadataHolder {
*/
MutableAnalysisMetadataHolder setRootComponentRef(int rootComponentRef);
+ /**
+ * @throws IllegalStateException if QProfile by language has already been set
+ */
+ MutableAnalysisMetadataHolder setQProfilesByLanguage(Map<String, QualityProfile> qprofilesByLanguage);
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeQProfileMeasureStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeQProfileMeasureStep.java
index c3c2df10de5..336fc050f32 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeQProfileMeasureStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeQProfileMeasureStep.java
@@ -19,10 +19,10 @@
*/
package org.sonar.server.computation.step;
-import com.google.common.base.Optional;
import java.util.HashMap;
import java.util.Map;
import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.analysis.AnalysisMetadataHolder;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.CrawlerDepthLimit;
import org.sonar.server.computation.component.PathAwareCrawler;
@@ -38,33 +38,36 @@ import org.sonar.server.computation.qualityprofile.QualityProfile;
import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER;
/**
- * Aggregates quality profile on lower-level module nodes on their parent modules and project
+ * Compute quality profile measure per module based on present languages
*/
public class ComputeQProfileMeasureStep implements ComputationStep {
private final TreeRootHolder treeRootHolder;
private final MeasureRepository measureRepository;
private final MetricRepository metricRepository;
+ private final AnalysisMetadataHolder analysisMetadataHolder;
- public ComputeQProfileMeasureStep(TreeRootHolder treeRootHolder, MeasureRepository measureRepository, MetricRepository metricRepository) {
+ public ComputeQProfileMeasureStep(TreeRootHolder treeRootHolder, MeasureRepository measureRepository, MetricRepository metricRepository,
+ AnalysisMetadataHolder analysisMetadataHolder) {
this.treeRootHolder = treeRootHolder;
this.measureRepository = measureRepository;
this.metricRepository = metricRepository;
+ this.analysisMetadataHolder = analysisMetadataHolder;
}
@Override
public void execute() {
Metric qProfilesMetric = metricRepository.getByKey(CoreMetrics.QUALITY_PROFILES_KEY);
- new PathAwareCrawler<>(new NewCoverageAggregationComponentVisitor(qProfilesMetric))
+ new PathAwareCrawler<>(new QProfileAggregationComponentVisitor(qProfilesMetric))
.visit(treeRootHolder.getRoot());
}
- private class NewCoverageAggregationComponentVisitor extends PathAwareVisitorAdapter<QProfiles> {
+ private class QProfileAggregationComponentVisitor extends PathAwareVisitorAdapter<QProfiles> {
private final Metric qProfilesMetric;
- public NewCoverageAggregationComponentVisitor(Metric qProfilesMetric) {
- super(CrawlerDepthLimit.MODULE, POST_ORDER, new SimpleStackElementFactory<QProfiles>() {
+ public QProfileAggregationComponentVisitor(Metric qProfilesMetric) {
+ super(CrawlerDepthLimit.FILE, POST_ORDER, new SimpleStackElementFactory<QProfiles>() {
@Override
public QProfiles createForAny(Component component) {
return new QProfiles();
@@ -74,19 +77,33 @@ public class ComputeQProfileMeasureStep implements ComputationStep {
}
@Override
+ public void visitFile(Component file, Path<QProfiles> path) {
+ String languageKey = file.getFileAttributes().getLanguageKey();
+ if (languageKey == null) {
+ // No qprofile for unknown languages
+ return;
+ }
+ if (!analysisMetadataHolder.getQProfilesByLanguage().containsKey(languageKey)) {
+ throw new IllegalStateException("Report contains a file with language '" + languageKey + "' but no matching quality profile");
+ }
+ path.parent().add(analysisMetadataHolder.getQProfilesByLanguage().get(languageKey));
+ }
+
+ @Override
+ public void visitDirectory(Component directory, Path<QProfiles> path) {
+ QProfiles qProfiles = path.current();
+ path.parent().add(qProfiles);
+ }
+
+ @Override
public void visitProject(Component project, Path<QProfiles> path) {
addMeasure(project, path.current());
}
@Override
public void visitModule(Component module, Path<QProfiles> path) {
- Optional<Measure> measure = measureRepository.getRawMeasure(module, qProfilesMetric);
QProfiles qProfiles = path.current();
- if (measure.isPresent()) {
- qProfiles.add(measure.get());
- } else {
- addMeasure(module, path.current());
- }
+ addMeasure(module, path.current());
path.parent().add(qProfiles);
}
@@ -100,14 +117,14 @@ public class ComputeQProfileMeasureStep implements ComputationStep {
private static class QProfiles {
private final Map<String, QualityProfile> profilesByKey = new HashMap<>();
- public void add(Measure measure) {
- profilesByKey.putAll(QPMeasureData.fromJson(measure.getStringValue()).getProfilesByKey());
- }
-
public void add(QProfiles qProfiles) {
profilesByKey.putAll(qProfiles.profilesByKey);
}
+ public void add(QualityProfile qProfile) {
+ profilesByKey.put(qProfile.getQpKey(), qProfile);
+ }
+
public Measure createMeasure() {
return Measure.newMeasureBuilder().create(QPMeasureData.toJson(new QPMeasureData(profilesByKey.values())));
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadReportAnalysisMetadataHolderStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadReportAnalysisMetadataHolderStep.java
index da7885d9086..0bffb7e5f2d 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadReportAnalysisMetadataHolderStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadReportAnalysisMetadataHolderStep.java
@@ -19,12 +19,17 @@
*/
package org.sonar.server.computation.step;
+import com.google.common.base.Function;
+import java.util.Date;
import org.sonar.api.utils.MessageException;
import org.sonar.ce.queue.CeTask;
import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile;
import org.sonar.server.computation.analysis.MutableAnalysisMetadataHolder;
import org.sonar.server.computation.batch.BatchReportReader;
+import org.sonar.server.computation.qualityprofile.QualityProfile;
+import static com.google.common.collect.Maps.transformValues;
import static java.lang.String.format;
import static org.elasticsearch.common.lang3.StringUtils.isNotEmpty;
@@ -33,6 +38,15 @@ import static org.elasticsearch.common.lang3.StringUtils.isNotEmpty;
*/
public class LoadReportAnalysisMetadataHolderStep implements ComputationStep {
+ private static final ToComputeQProfile TO_COMPUTE_QPROFILE = new ToComputeQProfile();
+
+ private static final class ToComputeQProfile implements Function<QProfile, QualityProfile> {
+ @Override
+ public QualityProfile apply(QProfile input) {
+ return new QualityProfile(input.getKey(), input.getName(), input.getLanguage(), new Date(input.getRulesUpdatedAt()));
+ }
+ }
+
private final CeTask ceTask;
private final BatchReportReader reportReader;
private final MutableAnalysisMetadataHolder mutableAnalysisMetadataHolder;
@@ -53,6 +67,7 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep {
mutableAnalysisMetadataHolder.setBranch(isNotEmpty(reportMetadata.getBranch()) ? reportMetadata.getBranch() : null);
mutableAnalysisMetadataHolder.setAnalysisDate(reportMetadata.getAnalysisDate());
mutableAnalysisMetadataHolder.setCrossProjectDuplicationEnabled(reportMetadata.getCrossProjectDuplicationActivated());
+ mutableAnalysisMetadataHolder.setQProfilesByLanguage(transformValues(reportMetadata.getQprofilesPerLanguage(), TO_COMPUTE_QPROFILE));
}
private void checkProjectKeyConsistency(ScannerReport.Metadata reportMetadata) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportComputationSteps.java
index 5baf2e59f27..8343cc318ca 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportComputationSteps.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportComputationSteps.java
@@ -72,6 +72,7 @@ public class ReportComputationSteps extends AbstractComputationSteps {
// Must be executed after computation of differential measures
QualityGateMeasuresStep.class,
+ // Must be executed after computation of language distribution
ComputeQProfileMeasureStep.class,
// Must be executed after computation of quality profile measure
QualityProfileEventsStep.class,
@@ -104,8 +105,7 @@ public class ReportComputationSteps extends AbstractComputationSteps {
// notifications are sent at the end, so that webapp displays up-to-date information
SendIssueNotificationsStep.class,
- PublishTaskResultStep.class
- );
+ PublishTaskResultStep.class);
private final ComputeEngineContainer computeEngineContainer;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/util/InitializedProperty.java b/server/sonar-server/src/main/java/org/sonar/server/computation/util/InitializedProperty.java
index d7d3ab36dad..55a16083744 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/util/InitializedProperty.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/util/InitializedProperty.java
@@ -26,7 +26,7 @@ public class InitializedProperty<E> {
private E property;
private boolean initialized = false;
- public InitializedProperty setProperty(@Nullable E property) {
+ public InitializedProperty<E> setProperty(@Nullable E property) {
this.property = property;
this.initialized = true;
return this;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderRule.java
index 59511d90853..ae79537f57e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderRule.java
@@ -20,9 +20,11 @@
package org.sonar.server.computation.analysis;
import java.util.Date;
+import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.junit.rules.ExternalResource;
+import org.sonar.server.computation.qualityprofile.QualityProfile;
import org.sonar.server.computation.snapshot.Snapshot;
import org.sonar.server.computation.util.InitializedProperty;
@@ -41,6 +43,8 @@ public class AnalysisMetadataHolderRule extends ExternalResource implements Anal
private InitializedProperty<Integer> rootComponentRef = new InitializedProperty<>();
+ private InitializedProperty<Map<String, QualityProfile>> qProfilesPerLanguage = new InitializedProperty<>();
+
public AnalysisMetadataHolderRule setAnalysisDate(Date date) {
checkNotNull(date, "Date must not be null");
this.analysisDate.setProperty(date.getTime());
@@ -108,4 +112,15 @@ public class AnalysisMetadataHolderRule extends ExternalResource implements Anal
checkState(rootComponentRef.isInitialized(), "Root component ref has not been set");
return rootComponentRef.getProperty();
}
+
+ public AnalysisMetadataHolderRule setQProfilesByLanguage(Map<String, QualityProfile> qProfilesPerLanguage) {
+ this.qProfilesPerLanguage.setProperty(qProfilesPerLanguage);
+ return this;
+ }
+
+ @Override
+ public Map<String, QualityProfile> getQProfilesByLanguage() {
+ checkState(qProfilesPerLanguage.isInitialized(), "QProfile per language has not been set");
+ return qProfilesPerLanguage.getProperty();
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolderRule.java
index aa11e0ff1ba..bc187d8469c 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolderRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolderRule.java
@@ -19,9 +19,11 @@
*/
package org.sonar.server.computation.analysis;
+import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.junit.rules.ExternalResource;
+import org.sonar.server.computation.qualityprofile.QualityProfile;
import org.sonar.server.computation.snapshot.Snapshot;
public class MutableAnalysisMetadataHolderRule extends ExternalResource implements MutableAnalysisMetadataHolder {
@@ -92,4 +94,15 @@ public class MutableAnalysisMetadataHolderRule extends ExternalResource implemen
public int getRootComponentRef() {
return delegate.getRootComponentRef();
}
+
+ @Override
+ public MutableAnalysisMetadataHolder setQProfilesByLanguage(Map<String, QualityProfile> qprofilesByLanguage) {
+ delegate.setQProfilesByLanguage(qprofilesByLanguage);
+ return this;
+ }
+
+ @Override
+ public Map<String, QualityProfile> getQProfilesByLanguage() {
+ return delegate.getQProfilesByLanguage();
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeQProfileMeasureStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeQProfileMeasureStepTest.java
index 764b52dec77..355de0ff52c 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeQProfileMeasureStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeQProfileMeasureStepTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.computation.step;
+import com.google.common.collect.ImmutableMap;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -26,8 +27,10 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.sonar.server.computation.analysis.AnalysisMetadataHolderRule;
import org.sonar.server.computation.batch.TreeRootHolderRule;
import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.FileAttributes;
import org.sonar.server.computation.component.ReportComponent;
import org.sonar.server.computation.measure.Measure;
import org.sonar.server.computation.measure.MeasureRepositoryRule;
@@ -36,9 +39,12 @@ import org.sonar.server.computation.qualityprofile.QPMeasureData;
import org.sonar.server.computation.qualityprofile.QualityProfile;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
import static org.assertj.guava.api.Assertions.assertThat;
import static org.sonar.api.measures.CoreMetrics.QUALITY_PROFILES;
import static org.sonar.api.measures.CoreMetrics.QUALITY_PROFILES_KEY;
+import static org.sonar.server.computation.component.Component.Type.DIRECTORY;
+import static org.sonar.server.computation.component.Component.Type.FILE;
import static org.sonar.server.computation.component.Component.Type.MODULE;
import static org.sonar.server.computation.component.Component.Type.PROJECT;
import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
@@ -47,22 +53,38 @@ public class ComputeQProfileMeasureStepTest {
private static final String QP_NAME_1 = "qp1";
private static final String QP_NAME_2 = "qp1";
- private static final String LANGUAGE_KEY_1 = "language_key1";
- private static final String LANGUAGE_KEY_2 = "language_key2";
+ private static final String LANGUAGE_KEY_1 = "java";
+ private static final String LANGUAGE_KEY_2 = "php";
private static final String PROJECT_KEY = "PROJECT KEY";
private static final int PROJECT_REF = 1;
private static final int MODULE_REF = 11;
private static final int SUB_MODULE_REF = 111;
+ private static final int FOLDER_1_REF = 1111;
+ private static final int FOLDER_2_REF = 1112;
+ private static final int FILE_1_1_REF = 11111;
+ private static final int FILE_1_2_REF = 11112;
+ private static final int FILE_2_1_REF = 11121;
+ private static final int FILE_2_2_REF = 11122;
private static final Component MULTI_MODULE_PROJECT = ReportComponent.builder(PROJECT, PROJECT_REF).setKey(PROJECT_KEY)
.addChildren(
ReportComponent.builder(MODULE, MODULE_REF)
.addChildren(
- ReportComponent.builder(MODULE, SUB_MODULE_REF).build()
- )
- .build()
- ).build();
+ ReportComponent.builder(MODULE, SUB_MODULE_REF)
+ .addChildren(ReportComponent.builder(DIRECTORY, FOLDER_1_REF)
+ .addChildren(
+ ReportComponent.builder(FILE, FILE_1_1_REF).setFileAttributes(new FileAttributes(false, "java")).build(),
+ ReportComponent.builder(FILE, FILE_1_2_REF).setFileAttributes(new FileAttributes(false, "java")).build())
+ .build(),
+ ReportComponent.builder(DIRECTORY, FOLDER_2_REF)
+ .addChildren(
+ ReportComponent.builder(FILE, FILE_2_1_REF).setFileAttributes(new FileAttributes(false, null)).build(),
+ ReportComponent.builder(FILE, FILE_2_2_REF).setFileAttributes(new FileAttributes(false, "php")).build())
+ .build())
+ .build())
+ .build())
+ .build();
@Rule
public ExpectedException thrown = ExpectedException.none();
@@ -76,80 +98,52 @@ public class ComputeQProfileMeasureStepTest {
@Rule
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+ @Rule
+ public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
+
ComputeQProfileMeasureStep underTest;
@Before
public void setUp() throws Exception {
- underTest = new ComputeQProfileMeasureStep(treeRootHolder, measureRepository, metricRepository);
+ underTest = new ComputeQProfileMeasureStep(treeRootHolder, measureRepository, metricRepository, analysisMetadataHolder);
}
@Test
public void add_quality_profile_measure_on_project() throws Exception {
treeRootHolder.setRoot(MULTI_MODULE_PROJECT);
-
- QualityProfile qp = createQProfile(QP_NAME_1, LANGUAGE_KEY_1);
- addMeasure(SUB_MODULE_REF, qp);
+ QualityProfile qpJava = createQProfile(QP_NAME_1, LANGUAGE_KEY_1);
+ QualityProfile qpPhp = createQProfile(QP_NAME_2, LANGUAGE_KEY_2);
+ analysisMetadataHolder.setQProfilesByLanguage(ImmutableMap.of(LANGUAGE_KEY_1, qpJava, LANGUAGE_KEY_2, qpPhp));
underTest.execute();
- assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF).get(QUALITY_PROFILES_KEY)).extracting("data").containsOnly(toJson(qp));
+ assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF).get(QUALITY_PROFILES_KEY))
+ .extracting("data").containsOnly(toJson(qpJava, qpPhp));
}
@Test
- public void add_quality_profile_measure_from_multiple_modules() throws Exception {
- ReportComponent project = ReportComponent.builder(PROJECT, PROJECT_REF)
- .addChildren(
- ReportComponent.builder(MODULE, MODULE_REF)
- .addChildren(
- ReportComponent.builder(MODULE, SUB_MODULE_REF).build()
- )
- .build(),
- ReportComponent.builder(MODULE, 12).build()
- ).build();
-
- treeRootHolder.setRoot(project);
-
- QualityProfile qp1 = createQProfile(QP_NAME_1, LANGUAGE_KEY_1);
- addMeasure(SUB_MODULE_REF, qp1);
- QualityProfile qp2 = createQProfile(QP_NAME_2, LANGUAGE_KEY_2);
- addMeasure(12, qp2);
-
- underTest.execute();
-
- assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF).get(QUALITY_PROFILES_KEY)).extracting("data").containsOnly(toJson(qp1, qp2));
- }
-
- @Test
- public void nothing_to_add_when_measure_already_exists_on_project() throws Exception {
+ public void nothing_to_add_when_no_files() throws Exception {
ReportComponent project = ReportComponent.builder(PROJECT, PROJECT_REF).build();
-
treeRootHolder.setRoot(project);
- QualityProfile qp = createQProfile(QP_NAME_1, LANGUAGE_KEY_1);
- addMeasure(PROJECT_REF, qp);
-
- underTest.execute();
-
- assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF)).isEmpty();
- }
-
- @Test
- public void nothing_to_add_when_no_qprofile_computed_on_project() throws Exception {
- treeRootHolder.setRoot(MULTI_MODULE_PROJECT);
-
underTest.execute();
assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF)).isEmpty();
}
@Test
- public void nothing_to_add_when_qprofiles_computed_on_project_are_empty() throws Exception {
+ public void fail_if_report_inconsistant() throws Exception {
treeRootHolder.setRoot(MULTI_MODULE_PROJECT);
- measureRepository.addRawMeasure(PROJECT_REF, QUALITY_PROFILES_KEY, newMeasureBuilder().create(toJson()));
+ QualityProfile qpJava = createQProfile(QP_NAME_1, LANGUAGE_KEY_1);
+ analysisMetadataHolder.setQProfilesByLanguage(ImmutableMap.of(LANGUAGE_KEY_1, qpJava));
- underTest.execute();
+ try {
+ underTest.execute();
+ fail("Expected exception");
+ } catch (Exception e) {
+ assertThat(e).hasCause(new IllegalStateException("Report contains a file with language 'php' but no matching quality profile"));
+ }
- assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF)).isEmpty();
}
private static QualityProfile createQProfile(String qpName, String languageKey) {
@@ -165,4 +159,5 @@ public class ComputeQProfileMeasureStepTest {
List<QualityProfile> qualityProfiles = Arrays.asList(qps);
return QPMeasureData.toJson(new QPMeasureData(qualityProfiles));
}
+
}
diff --git a/sonar-core/src/main/java/org/sonar/core/metric/BatchMetrics.java b/sonar-core/src/main/java/org/sonar/core/metric/BatchMetrics.java
index f93d5faa92d..3f626f79151 100644
--- a/sonar-core/src/main/java/org/sonar/core/metric/BatchMetrics.java
+++ b/sonar-core/src/main/java/org/sonar/core/metric/BatchMetrics.java
@@ -72,7 +72,6 @@ import static org.sonar.api.measures.CoreMetrics.OVERALL_UNCOVERED_CONDITIONS;
import static org.sonar.api.measures.CoreMetrics.OVERALL_UNCOVERED_LINES;
import static org.sonar.api.measures.CoreMetrics.PUBLIC_API;
import static org.sonar.api.measures.CoreMetrics.PUBLIC_UNDOCUMENTED_API;
-import static org.sonar.api.measures.CoreMetrics.QUALITY_PROFILES;
import static org.sonar.api.measures.CoreMetrics.SKIPPED_TESTS;
import static org.sonar.api.measures.CoreMetrics.STATEMENTS;
import static org.sonar.api.measures.CoreMetrics.TESTS;
@@ -146,9 +145,7 @@ public class BatchMetrics {
OVERALL_CONDITIONS_TO_COVER,
OVERALL_UNCOVERED_CONDITIONS,
OVERALL_COVERED_CONDITIONS_BY_LINE,
- OVERALL_CONDITIONS_BY_LINE,
-
- QUALITY_PROFILES);
+ OVERALL_CONDITIONS_BY_LINE);
private final Set<Metric> metrics;
diff --git a/sonar-core/src/test/java/org/sonar/core/metric/BatchMetricsTest.java b/sonar-core/src/test/java/org/sonar/core/metric/BatchMetricsTest.java
index c1e2e5e2fb3..d1f0081c922 100644
--- a/sonar-core/src/test/java/org/sonar/core/metric/BatchMetricsTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/metric/BatchMetricsTest.java
@@ -36,7 +36,7 @@ public class BatchMetricsTest {
@Test
public void check_number_of_allowed_core_metrics() throws Exception {
- assertThat(SENSOR_METRICS_WITHOUT_METRIC_PLUGIN.getMetrics()).hasSize(49);
+ assertThat(SENSOR_METRICS_WITHOUT_METRIC_PLUGIN.getMetrics()).hasSize(48);
}
@Test
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
index 1b0d755ce9f..d7df60a352f 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
@@ -2554,12 +2554,16 @@ public final class CoreMetrics {
/**
* @since 4.4
+ * @deprecated since 5.5
*/
+ @Deprecated
public static final String QUALITY_PROFILES_KEY = "quality_profiles";
/**
* @since 4.4
+ * @deprecated since 5.5
*/
+ @Deprecated
public static final Metric<String> QUALITY_PROFILES = new Metric.Builder(QUALITY_PROFILES_KEY, "Profiles", Metric.ValueType.DATA)
.setDescription("Details of quality profiles used during analysis")
.setQualitative(false)
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/report/MetadataPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/report/MetadataPublisher.java
index 67dc6c0be04..489d71fd7d5 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/batch/report/MetadataPublisher.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/report/MetadataPublisher.java
@@ -26,6 +26,8 @@ import org.sonar.api.resources.Project;
import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
import org.sonar.batch.index.BatchComponent;
import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.rule.ModuleQProfiles;
+import org.sonar.batch.rule.QProfile;
import org.sonar.batch.scan.ImmutableProjectReactor;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReportWriter;
@@ -35,11 +37,13 @@ public class MetadataPublisher implements ReportPublisherStep {
private final BatchComponentCache componentCache;
private final ImmutableProjectReactor reactor;
private final Settings settings;
+ private final ModuleQProfiles qProfiles;
- public MetadataPublisher(BatchComponentCache componentCache, ImmutableProjectReactor reactor, Settings settings) {
+ public MetadataPublisher(BatchComponentCache componentCache, ImmutableProjectReactor reactor, Settings settings, ModuleQProfiles qProfiles) {
this.componentCache = componentCache;
this.reactor = reactor;
this.settings = settings;
+ this.qProfiles = qProfiles;
}
@Override
@@ -56,6 +60,13 @@ public class MetadataPublisher implements ReportPublisherStep {
if (branch != null) {
builder.setBranch(branch);
}
+ for (QProfile qp : qProfiles.findAll()) {
+ builder.getMutableQprofilesPerLanguage().put(qp.getLanguage(), org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile.newBuilder()
+ .setKey(qp.getKey())
+ .setLanguage(qp.getLanguage())
+ .setName(qp.getName())
+ .setRulesUpdatedAt(qp.getRulesUpdatedAt().getTime()).build());
+ }
writer.writeMetadata(builder.build());
}
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/rule/QProfileSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/rule/QProfileSensor.java
deleted file mode 100644
index bdffd6f1f5d..00000000000
--- a/sonar-scanner-engine/src/main/java/org/sonar/batch/rule/QProfileSensor.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.batch.rule;
-
-import org.sonar.api.batch.AnalysisMode;
-import org.sonar.api.batch.Sensor;
-import org.sonar.api.batch.SensorContext;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.resources.Project;
-
-/**
- * Stores which Quality profiles have been used on the current module.
- *
- * TODO This information should not be stored as a measure but should be send as metadata in the {@link org.sonar.scanner.protocol.output.ScannerReport}
- */
-public class QProfileSensor implements Sensor {
-
- private final ModuleQProfiles moduleQProfiles;
- private final FileSystem fs;
- private final AnalysisMode analysisMode;
-
- public QProfileSensor(ModuleQProfiles moduleQProfiles, FileSystem fs, AnalysisMode analysisMode) {
- this.moduleQProfiles = moduleQProfiles;
- this.fs = fs;
- this.analysisMode = analysisMode;
- }
-
- @Override
- public boolean shouldExecuteOnProject(Project project) {
- // Should be only executed on leaf modules
- return project.getModules().isEmpty()
- // Useless in issues mode
- && !analysisMode.isIssues();
- }
-
- @Override
- public void analyse(Project project, SensorContext context) {
- UsedQProfiles used = new UsedQProfiles();
- for (String language : fs.languages()) {
- QProfile profile = moduleQProfiles.findByLanguage(language);
- if (profile != null) {
- used.add(profile);
- }
- }
- Measure<?> detailsMeasure = new Measure<>(CoreMetrics.QUALITY_PROFILES, used.toJson());
- context.saveMeasure(detailsMeasure);
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName();
- }
-}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/rule/UsedQProfiles.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/rule/UsedQProfiles.java
deleted file mode 100644
index 72186a5e959..00000000000
--- a/sonar-scanner-engine/src/main/java/org/sonar/batch/rule/UsedQProfiles.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.batch.rule;
-
-import com.google.common.collect.Sets;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.core.util.UtcDateUtils;
-
-import javax.annotation.concurrent.Immutable;
-
-import java.io.StringWriter;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.SortedSet;
-
-@Immutable
-public class UsedQProfiles {
-
- private final SortedSet<QProfile> profiles = Sets.newTreeSet(new Comparator<QProfile>() {
- @Override
- public int compare(QProfile o1, QProfile o2) {
- int c = o1.getLanguage().compareTo(o2.getLanguage());
- if (c == 0) {
- c = o1.getName().compareTo(o2.getName());
- }
- return c;
- }
- });
-
- public static UsedQProfiles fromJson(String json) {
- UsedQProfiles result = new UsedQProfiles();
- JsonArray jsonRoot = new JsonParser().parse(json).getAsJsonArray();
- for (JsonElement jsonElt : jsonRoot) {
- JsonObject jsonProfile = jsonElt.getAsJsonObject();
- QProfile profile = new QProfile();
- profile.setKey(jsonProfile.get("key").getAsString());
- profile.setName(jsonProfile.get("name").getAsString());
- profile.setLanguage(jsonProfile.get("language").getAsString());
- profile.setRulesUpdatedAt(UtcDateUtils.parseDateTime(jsonProfile.get("rulesUpdatedAt").getAsString()));
- result.add(profile);
- }
- return result;
- }
-
- public String toJson() {
- StringWriter json = new StringWriter();
- JsonWriter writer = JsonWriter.of(json);
- writer.beginArray();
- for (QProfile profile : profiles) {
- writer
- .beginObject()
- .prop("key", profile.getKey())
- .prop("language", profile.getLanguage())
- .prop("name", profile.getName())
- .prop("rulesUpdatedAt", UtcDateUtils.formatDateTime(profile.getRulesUpdatedAt()))
- .endObject();
- }
- writer.endArray();
- writer.close();
- return json.toString();
- }
-
- public UsedQProfiles add(UsedQProfiles other) {
- addAll(other.profiles);
- return this;
- }
-
- public UsedQProfiles add(QProfile profile) {
- profiles.add(profile);
- return this;
- }
-
- public UsedQProfiles addAll(Collection<QProfile> profiles) {
- this.profiles.addAll(profiles);
- return this;
- }
-
- public SortedSet<QProfile> profiles() {
- return profiles;
- }
-
- public Map<String, QProfile> profilesByKey() {
- Map<String, QProfile> map = new HashMap<>();
- for (QProfile profile : profiles) {
- map.put(profile.getKey(), profile);
- }
- return map;
- }
-}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
index 56da7fd624d..daba4b1c6c9 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
@@ -56,7 +56,6 @@ import org.sonar.batch.phases.PublishPhaseExecutor;
import org.sonar.batch.phases.SensorsExecutor;
import org.sonar.batch.postjob.DefaultPostJobContext;
import org.sonar.batch.postjob.PostJobOptimizer;
-import org.sonar.batch.rule.QProfileSensor;
import org.sonar.batch.rule.QProfileVerifier;
import org.sonar.batch.rule.RuleFinderCompatibility;
import org.sonar.batch.rule.RulesProfileProvider;
@@ -151,7 +150,6 @@ public class ModuleScanContainer extends ComponentContainer {
// rules
new RulesProfileProvider(),
- QProfileSensor.class,
CheckFactory.class,
// issues
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
index 7a349224239..19e2600ed25 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
@@ -324,7 +324,7 @@ public class CpdMediumTest {
Map<String, List<Measure>> allMeasures = result.allMeasures();
- assertThat(allMeasures.get("com.foo.project")).extracting("metricKey").containsOnly(CoreMetrics.QUALITY_PROFILES_KEY);
+ assertThat(allMeasures.get("com.foo.project")).extracting("metricKey").isEmpty();
assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue.value").containsOnly(
tuple(CoreMetrics.LINES_KEY, blockCount * 2 + 1));
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java
index b72458e3141..0affd4dad8e 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java
@@ -90,8 +90,7 @@ public class MeasuresMediumTest {
Map<String, List<Measure>> allMeasures = result.allMeasures();
- assertThat(allMeasures.get("com.foo.project")).extracting("metricKey", "stringValue.value").containsOnly(
- tuple(CoreMetrics.QUALITY_PROFILES_KEY, "[{\"key\":\"Sonar Way\",\"language\":\"xoo\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2009-02-13T23:31:31+0000\"}]"));
+ assertThat(allMeasures.get("com.foo.project")).extracting("metricKey", "stringValue.value").isEmpty();
assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue.value").containsOnly(
tuple(CoreMetrics.LINES_KEY, 2));
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java
index 7069845e9cf..dd2fe0b7b6d 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java
@@ -30,12 +30,18 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.rule.ModuleQProfiles;
+import org.sonar.batch.rule.QProfile;
import org.sonar.batch.scan.ImmutableProjectReactor;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReportReader;
import org.sonar.scanner.protocol.output.ScannerReportWriter;
+import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public class MetadataPublisherTest {
@@ -46,6 +52,7 @@ public class MetadataPublisherTest {
private Project project;
private MetadataPublisher underTest;
private Settings settings;
+ private ModuleQProfiles qProfiles;
@Before
public void prepare() {
@@ -56,12 +63,19 @@ public class MetadataPublisherTest {
componentCache.add(project, null);
componentCache.add(sampleFile, project);
settings = new Settings();
- underTest = new MetadataPublisher(componentCache, new ImmutableProjectReactor(projectDef), settings);
+ qProfiles = mock(ModuleQProfiles.class);
+ underTest = new MetadataPublisher(componentCache, new ImmutableProjectReactor(projectDef), settings, qProfiles);
}
@Test
public void write_metadata() throws Exception {
settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
+ Date date = new Date();
+ when(qProfiles.findAll()).thenReturn(asList(new QProfile()
+ .setKey("q1")
+ .setName("Q1")
+ .setLanguage("java")
+ .setRulesUpdatedAt(date)));
File outputDir = temp.newFolder();
ScannerReportWriter writer = new ScannerReportWriter(outputDir);
@@ -73,6 +87,12 @@ public class MetadataPublisherTest {
assertThat(metadata.getProjectKey()).isEqualTo("foo");
assertThat(metadata.getProjectKey()).isEqualTo("foo");
assertThat(metadata.getCrossProjectDuplicationActivated()).isTrue();
+ assertThat(metadata.getQprofilesPerLanguage()).containsOnly(entry("java", org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile.newBuilder()
+ .setKey("q1")
+ .setName("Q1")
+ .setLanguage("java")
+ .setRulesUpdatedAt(date.getTime())
+ .build()));
}
@Test
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java
deleted file mode 100644
index 4801f246c55..00000000000
--- a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.batch.rule;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.AnalysisMode;
-import org.sonar.api.batch.SensorContext;
-import org.sonar.api.batch.fs.internal.DefaultFileSystem;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.resources.Project;
-import org.sonar.api.test.IsMeasure;
-import org.sonar.core.util.UtcDateUtils;
-
-import java.util.Collections;
-import java.util.Date;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class QProfileSensorTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- static final Date DATE = UtcDateUtils.parseDateTime("2014-01-15T12:00:00+0000");
- static final QProfile JAVA_PROFILE = new QProfile().setKey("java-two").setName("Java Two").setLanguage("java")
- .setRulesUpdatedAt(DATE);
- static final QProfile PHP_PROFILE = new QProfile().setKey("php-one").setName("Php One").setLanguage("php")
- .setRulesUpdatedAt(DATE);
-
- ModuleQProfiles moduleQProfiles = mock(ModuleQProfiles.class);
- Project project = mock(Project.class);
- SensorContext sensorContext = mock(SensorContext.class);
- DefaultFileSystem fs;
-
- @Before
- public void prepare() throws Exception {
- fs = new DefaultFileSystem(temp.newFolder().toPath());
- }
-
- @Test
- public void to_string() {
- QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class));
- assertThat(sensor.toString()).isEqualTo("QProfileSensor");
- }
-
- @Test
- public void no_execution_in_issues_mode() {
- AnalysisMode analysisMode = mock(AnalysisMode.class);
- when(analysisMode.isIssues()).thenReturn(true);
- QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, analysisMode);
- assertThat(sensor.shouldExecuteOnProject(project)).isFalse();
-
- }
-
- @Test
- public void no_qprofiles() {
- when(moduleQProfiles.findAll()).thenReturn(Collections.<QProfile>emptyList());
-
- QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class));
- assertThat(sensor.shouldExecuteOnProject(project)).isTrue();
- sensor.analyse(project, sensorContext);
-
- // measures are not saved
- verify(sensorContext).saveMeasure(argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES, "[]")));
- }
-
- @Test
- public void mark_profiles_as_used() {
- when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE);
- when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE);
- when(moduleQProfiles.findByLanguage("abap")).thenReturn(null);
- fs.addLanguages("java", "php", "abap");
-
- QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class));
- assertThat(sensor.shouldExecuteOnProject(project)).isTrue();
- sensor.analyse(project, sensorContext);
- }
-
- @Test
- public void store_measures_on_single_lang_module() {
- when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE);
- when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE);
- when(moduleQProfiles.findByLanguage("abap")).thenReturn(null);
- fs.addLanguages("java");
-
- QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class));
- assertThat(sensor.shouldExecuteOnProject(project)).isTrue();
- sensor.analyse(project, sensorContext);
-
- verify(sensorContext).saveMeasure(
- argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES,
- "[{\"key\":\"java-two\",\"language\":\"java\",\"name\":\"Java Two\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}]")));
- }
-
- @Test
- public void store_measures_on_multi_lang_module() {
- when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE);
- when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE);
- when(moduleQProfiles.findByLanguage("abap")).thenReturn(null);
- fs.addLanguages("java", "php");
-
- QProfileSensor sensor = new QProfileSensor(moduleQProfiles, fs, mock(AnalysisMode.class));
- assertThat(sensor.shouldExecuteOnProject(project)).isTrue();
- sensor.analyse(project, sensorContext);
-
- verify(sensorContext).saveMeasure(
- argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES,
- "[{\"key\":\"java-two\",\"language\":\"java\",\"name\":\"Java Two\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}," +
- "{\"key\":\"php-one\",\"language\":\"php\",\"name\":\"Php One\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}]")));
- }
-}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java
deleted file mode 100644
index dd46501e68e..00000000000
--- a/sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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.batch.rule;
-
-import org.junit.Test;
-import org.sonar.core.util.UtcDateUtils;
-
-import java.util.Arrays;
-import java.util.Map;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class UsedQProfilesTest {
-
- static final String JAVA_JSON = "{\"key\":\"p1\",\"language\":\"java\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2014-01-15T00:00:00+0000\"}";
- static final String PHP_JSON = "{\"key\":\"p2\",\"language\":\"php\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2014-02-20T00:00:00+0000\"}";
-
- @Test
- public void from_and_to_json() {
- QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java")
- .setRulesUpdatedAt(UtcDateUtils.parseDateTime("2014-01-15T00:00:00+0000"));
- QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php")
- .setRulesUpdatedAt(UtcDateUtils.parseDateTime("2014-02-20T00:00:00+0000"));
-
- UsedQProfiles used = new UsedQProfiles().add(java).add(php);
- String json = "[" + JAVA_JSON + "," + PHP_JSON + "]";
- assertThat(used.toJson()).isEqualTo(json);
-
- used = UsedQProfiles.fromJson(json);
- assertThat(used.profiles()).hasSize(2);
- assertThat(used.profiles().first().getKey()).isEqualTo("p1");
- assertThat(used.profiles().last().getKey()).isEqualTo("p2");
- }
-
- @Test
- public void do_not_duplicate_profiles() {
- QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java");
- QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php");
-
- UsedQProfiles used = new UsedQProfiles().addAll(Arrays.asList(java, java, php));
- assertThat(used.profiles()).hasSize(2);
- }
-
- @Test
- public void group_profiles_by_key() {
- QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java");
- QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php");
-
- UsedQProfiles used = new UsedQProfiles().addAll(Arrays.asList(java, java, php));
- Map<String, QProfile> map = used.profilesByKey();
- assertThat(map).hasSize(2);
- assertThat(map.get("p1")).isSameAs(java);
- assertThat(map.get("p2")).isSameAs(php);
- }
-}
diff --git a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
index c7b4964f540..0d958a4eef7 100644
--- a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
+++ b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
@@ -41,6 +41,14 @@ message Metadata {
string branch = 3;
int32 root_component_ref = 4;
bool cross_project_duplication_activated = 5;
+ map<string, QProfile> qprofiles_per_language = 6;
+
+ message QProfile {
+ string key = 1;
+ string name = 2;
+ string language = 3;
+ int64 rulesUpdatedAt = 4;
+ }
}
message ActiveRule {
@@ -90,6 +98,7 @@ message Component {
DIRECTORY = 3;
FILE = 4;
}
+
}
message Measure {