From 0fd2f2204c44b6b5ec82741233940b8471df06d8 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Mon, 21 Mar 2016 18:52:14 +0100 Subject: [PATCH] SONAR-7368 Deprecate quality_profiles measure and add data in scanner report --- .../analysis/AnalysisMetadataHolder.java | 4 + .../analysis/AnalysisMetadataHolderImpl.java | 18 +++ .../MutableAnalysisMetadataHolder.java | 7 + .../step/ComputeQProfileMeasureStep.java | 51 ++++--- .../LoadReportAnalysisMetadataHolderStep.java | 15 ++ .../step/ReportComputationSteps.java | 4 +- .../computation/util/InitializedProperty.java | 2 +- .../analysis/AnalysisMetadataHolderRule.java | 15 ++ .../MutableAnalysisMetadataHolderRule.java | 13 ++ .../step/ComputeQProfileMeasureStepTest.java | 101 +++++++------ .../org/sonar/core/metric/BatchMetrics.java | 5 +- .../sonar/core/metric/BatchMetricsTest.java | 2 +- .../org/sonar/api/measures/CoreMetrics.java | 4 + .../sonar/batch/report/MetadataPublisher.java | 13 +- .../org/sonar/batch/rule/QProfileSensor.java | 72 ---------- .../org/sonar/batch/rule/UsedQProfiles.java | 112 --------------- .../sonar/batch/scan/ModuleScanContainer.java | 2 - .../batch/mediumtest/cpd/CpdMediumTest.java | 2 +- .../measures/MeasuresMediumTest.java | 3 +- .../batch/report/MetadataPublisherTest.java | 22 ++- .../sonar/batch/rule/QProfileSensorTest.java | 135 ------------------ .../sonar/batch/rule/UsedQProfilesTest.java | 72 ---------- .../src/main/protobuf/scanner_report.proto | 9 ++ 23 files changed, 207 insertions(+), 476 deletions(-) delete mode 100644 sonar-scanner-engine/src/main/java/org/sonar/batch/rule/QProfileSensor.java delete mode 100644 sonar-scanner-engine/src/main/java/org/sonar/batch/rule/UsedQProfiles.java delete mode 100644 sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java delete mode 100644 sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java 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 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 rootComponentRef = new InitializedProperty<>(); + private InitializedProperty> 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 qprofilesByLanguage) { + checkState(!this.qProfilesPerLanguage.isInitialized(), "QProfiles by language has already been set"); + this.qProfilesPerLanguage.setProperty(ImmutableMap.copyOf(qprofilesByLanguage)); + return this; + } + + @Override + public Map 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 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 { + private class QProfileAggregationComponentVisitor extends PathAwareVisitorAdapter { private final Metric qProfilesMetric; - public NewCoverageAggregationComponentVisitor(Metric qProfilesMetric) { - super(CrawlerDepthLimit.MODULE, POST_ORDER, new SimpleStackElementFactory() { + public QProfileAggregationComponentVisitor(Metric qProfilesMetric) { + super(CrawlerDepthLimit.FILE, POST_ORDER, new SimpleStackElementFactory() { @Override public QProfiles createForAny(Component component) { return new QProfiles(); @@ -73,6 +76,25 @@ public class ComputeQProfileMeasureStep implements ComputationStep { this.qProfilesMetric = qProfilesMetric; } + @Override + public void visitFile(Component file, Path 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 path) { + QProfiles qProfiles = path.current(); + path.parent().add(qProfiles); + } + @Override public void visitProject(Component project, Path path) { addMeasure(project, path.current()); @@ -80,13 +102,8 @@ public class ComputeQProfileMeasureStep implements ComputationStep { @Override public void visitModule(Component module, Path path) { - Optional 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 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 { + @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 { private E property; private boolean initialized = false; - public InitializedProperty setProperty(@Nullable E property) { + public InitializedProperty 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 rootComponentRef = new InitializedProperty<>(); + private InitializedProperty> 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 qProfilesPerLanguage) { + this.qProfilesPerLanguage.setProperty(qProfilesPerLanguage); + return this; + } + + @Override + public Map 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 qprofilesByLanguage) { + delegate.setQProfilesByLanguage(qprofilesByLanguage); + return this; + } + + @Override + public Map 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 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 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 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 profiles = Sets.newTreeSet(new Comparator() { - @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 profiles) { - this.profiles.addAll(profiles); - return this; - } - - public SortedSet profiles() { - return profiles; - } - - public Map profilesByKey() { - Map 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> 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> 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.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 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 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 { -- 2.39.5