]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7368 Deprecate quality_profiles measure and add data in scanner report 864/head
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 21 Mar 2016 17:52:14 +0000 (18:52 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Thu, 24 Mar 2016 08:57:28 +0000 (09:57 +0100)
23 files changed:
server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolder.java
server/sonar-server/src/main/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolder.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeQProfileMeasureStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadReportAnalysisMetadataHolderStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportComputationSteps.java
server/sonar-server/src/main/java/org/sonar/server/computation/util/InitializedProperty.java
server/sonar-server/src/test/java/org/sonar/server/computation/analysis/AnalysisMetadataHolderRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/analysis/MutableAnalysisMetadataHolderRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeQProfileMeasureStepTest.java
sonar-core/src/main/java/org/sonar/core/metric/BatchMetrics.java
sonar-core/src/test/java/org/sonar/core/metric/BatchMetricsTest.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
sonar-scanner-engine/src/main/java/org/sonar/batch/report/MetadataPublisher.java
sonar-scanner-engine/src/main/java/org/sonar/batch/rule/QProfileSensor.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/batch/rule/UsedQProfiles.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java
sonar-scanner-engine/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java
sonar-scanner-engine/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java [deleted file]
sonar-scanner-engine/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java [deleted file]
sonar-scanner-protocol/src/main/protobuf/scanner_report.proto

index 26008c8233e819e20cfa562259da14c0f9c15f7a..93565cabbd30da15d57683e387d2f9a9515508a9 100644 (file)
@@ -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();
+
 }
index 6184b12ba16117c878e7bb1a0864b23ccd2c21c5..003d0c7509f39dc82c2b318f722c81bc1e66515b 100644 (file)
  */
 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();
+  }
+
 }
index ad2eae0ae9809f8c5eb8de0f86008706d7f0d9c2..20326ee5ec1d54dc2c45242f857d3b26bb2b46e5 100644 (file)
@@ -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);
+
 }
index c3c2df10de5594e0738b3e8bf0f67d3f1fea1687..336fc050f325d8dd67c1a090eccea1ded78fb66a 100644 (file)
  */
 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();
@@ -73,6 +76,25 @@ public class ComputeQProfileMeasureStep implements ComputationStep {
       this.qProfilesMetric = qProfilesMetric;
     }
 
+    @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());
@@ -80,13 +102,8 @@ public class ComputeQProfileMeasureStep implements ComputationStep {
 
     @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())));
     }
index da7885d9086acd7dc40e45b1ff1b506bc99147ee..0bffb7e5f2dd56fac794fbf3a3d4494d78452fdd 100644 (file)
  */
 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) {
index 5baf2e59f2724e802f70da1ed56ec99690850754..8343cc318ca6035734ff2061d442ebb5d205c8f6 100644 (file)
@@ -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;
 
index d7d3ab36dadb513f839044ee0ad197e983889b67..55a16083744d9b01ca44f113e79dfcb521532318 100644 (file)
@@ -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;
index 59511d908535ed519b98d8f748482e7ba44e0378..ae79537f57ea05e400fe8d997fc6dfd90879eb3d 100644 (file)
 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();
+  }
 }
index aa11e0ff1ba2401e7f4651cafd5dd16c474dc124..bc187d8469c40cf0363cfe10244c9f38f433a278 100644 (file)
  */
 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();
+  }
 }
index 764b52dec772ffeac4d489746a07f464ab740c9b..355de0ff52c2086d2c8bd9606b7715076f936bc8 100644 (file)
@@ -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));
   }
+
 }
index f93d5faa92de5ce26274965e78985a7f58af0627..3f626f791515337277966aab7ccc101e1bc6bbaa 100644 (file)
@@ -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;
 
index c1e2e5e2fb3c9c06e85d9884315458e814026484..d1f0081c92226f3c071c796634e19f8141a88842 100644 (file)
@@ -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
index 1b0d755ce9f83ca31edc8d29e0bd76586c5c1abb..d7df60a352f8fce865fa983ac8015fc681a93b47 100644 (file)
@@ -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)
index 67dc6c0be04215262d782c7f147fb9c579b7ec4e..489d71fd7d5ac67fc196b588fdf9f5541cf38d5d 100644 (file)
@@ -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 (file)
index bdffd6f..0000000
+++ /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 (file)
index 72186a5..0000000
+++ /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;
-  }
-}
index 56da7fd624d98c50470dc1a57a50e6098ed7fbe3..daba4b1c6c91dfe7a63f3f08a01eff5049118ae7 100644 (file)
@@ -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
index 7a349224239933611872fea9bb9ca5a9c8d4ac0c..19e2600ed255f34e81a2005e6a1b9565aa507769 100644 (file)
@@ -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));
index b72458e3141037130e099fffa19f89f71070abd8..0affd4dad8e0ea62d5fe80cf9dcbbcf913c1dfb2 100644 (file)
@@ -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));
index 7069845e9cf30107a1e12627718b065d642ba051..dd2fe0b7b6d58a3bdd98c6203f9a302fe4745212 100644 (file)
@@ -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 (file)
index 4801f24..0000000
+++ /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 (file)
index dd46501..0000000
+++ /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);
-  }
-}
index c7b4964f540d81c1e82e4f87df67c04a504c4d36..0d958a4eef794d53e930c688a933bae6a8c5f55b 100644 (file)
@@ -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 {