]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6680 Create step to compute quality profile measure in compute engine
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 3 Jul 2015 14:46:05 +0000 (16:46 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 7 Jul 2015 08:40:25 +0000 (10:40 +0200)
server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeQProfileMeasureStep.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeQProfileMeasureStepTest.java [new file with mode: 0644]

index 1d7f0e161e53dc70975a7e6a30c43e99c02daeb8..46c8c4b924194fd9c8cc5d4e7df3c58da86a3ec5 100644 (file)
@@ -65,7 +65,8 @@ public class ComputationSteps {
 
       // Must be executed after computation of differential measures
       QualityGateMeasuresStep.class,
-
+      ComputeQProfileMeasureStep.class,
+      // Must be executed after computation of quality profile measure
       QualityProfileEventsStep.class,
 
       // Must be executed after computation of quality gate measure
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
new file mode 100644 (file)
index 0000000..5f5dbcc
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.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.component.Component;
+import org.sonar.server.computation.component.PathAwareVisitor;
+import org.sonar.server.computation.component.TreeRootHolder;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.qualityprofile.QPMeasureData;
+import org.sonar.server.computation.qualityprofile.QualityProfile;
+
+import static org.sonar.server.computation.component.Component.Type.MODULE;
+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
+ */
+public class ComputeQProfileMeasureStep implements ComputationStep {
+
+  private final TreeRootHolder treeRootHolder;
+  private final MeasureRepository measureRepository;
+  private final MetricRepository metricRepository;
+
+  public ComputeQProfileMeasureStep(TreeRootHolder treeRootHolder, MeasureRepository measureRepository, MetricRepository metricRepository) {
+    this.treeRootHolder = treeRootHolder;
+    this.measureRepository = measureRepository;
+    this.metricRepository = metricRepository;
+  }
+
+  @Override
+  public void execute() {
+    Metric qProfilesMetric = metricRepository.getByKey(CoreMetrics.QUALITY_PROFILES_KEY);
+    new NewCoverageAggregationComponentVisitor(qProfilesMetric).visit(treeRootHolder.getRoot());
+  }
+
+  private class NewCoverageAggregationComponentVisitor extends PathAwareVisitor<QProfiles> {
+
+    private final Metric qProfilesMetric;
+
+    public NewCoverageAggregationComponentVisitor(Metric qProfilesMetric) {
+      super(MODULE, POST_ORDER, new SimpleStackElementFactory<QProfiles>() {
+        @Override
+        public QProfiles createForAny(Component component) {
+          return new QProfiles();
+        }
+      });
+      this.qProfilesMetric = qProfilesMetric;
+    }
+
+    @Override
+    protected void visitProject(Component project, Path<QProfiles> path) {
+      addMeasure(project, path.current());
+    }
+
+    @Override
+    protected 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());
+      }
+      path.parent().add(qProfiles);
+    }
+
+    private void addMeasure(Component component, QProfiles qProfiles) {
+      if (!qProfiles.profilesByKey.isEmpty()) {
+        measureRepository.add(component, qProfilesMetric, qProfiles.createMeasure());
+      }
+    }
+  }
+
+  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 Measure createMeasure() {
+      return Measure.newMeasureBuilder().create(QPMeasureData.toJson(new QPMeasureData(profilesByKey.values())));
+    }
+  }
+
+  @Override
+  public String getDescription() {
+    return "Computes Quality Profile measures";
+  }
+}
index 209826914410347bf7216c44c7da02b785798c72..e8541194f51b31748aabe0e3afeba17885c810af 100644 (file)
@@ -44,6 +44,11 @@ import org.sonar.server.computation.qualityprofile.QualityProfile;
 
 import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER;
 
+/**
+ * Computation of quality profile events
+ *
+ * As it depends upon {@link CoreMetrics#QUALITY_PROFILES_KEY}, it must be executed after {@link ComputeQProfileMeasureStep}
+ */
 public class QualityProfileEventsStep implements ComputationStep {
   private final TreeRootHolder treeRootHolder;
   private final MetricRepository metricRepository;
@@ -61,7 +66,6 @@ public class QualityProfileEventsStep implements ComputationStep {
     this.languageRepository = languageRepository;
   }
 
-
   @Override
   public void execute() {
     new DepthTraversalTypeAwareVisitor(Component.Type.PROJECT, POST_ORDER) {
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
new file mode 100644 (file)
index 0000000..e2b96d9
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.server.computation.step;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+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.guava.api.Assertions.assertThat;
+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;
+import static org.sonar.server.computation.measure.MeasureRepoEntry.entryOf;
+import static org.sonar.server.computation.measure.MeasureRepoEntry.toEntries;
+
+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";
+
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+
+  @Rule
+  public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(CoreMetrics.QUALITY_PROFILES);
+
+  @Rule
+  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+  ComputeQProfileMeasureStep sut;
+
+  @Before
+  public void setUp() throws Exception {
+    sut = new ComputeQProfileMeasureStep(treeRootHolder, measureRepository, metricRepository);
+  }
+
+  @Test
+  public void add_quality_profile_measure_on_project() throws Exception {
+    DumbComponent project = DumbComponent.builder(PROJECT, 1)
+      .addChildren(
+        DumbComponent.builder(MODULE, 11)
+          .addChildren(
+            DumbComponent.builder(MODULE, 111).build()
+          )
+          .build()
+      ).build();
+
+    treeRootHolder.setRoot(project);
+
+    QualityProfile qp = createQProfile(QP_NAME_1, LANGUAGE_KEY_1);
+    addMeasure(111, qp);
+
+    sut.execute();
+
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(CoreMetrics.QUALITY_PROFILES_KEY, newMeasureBuilder().create(toJson(qp))));
+  }
+
+  @Test
+  public void add_quality_profile_measure_from_multiple_modules() throws Exception {
+    DumbComponent project = DumbComponent.builder(PROJECT, 1)
+      .addChildren(
+        DumbComponent.builder(MODULE, 11)
+          .addChildren(
+            DumbComponent.builder(MODULE, 111).build()
+          )
+          .build(),
+        DumbComponent.builder(MODULE, 12).build()
+      ).build();
+
+    treeRootHolder.setRoot(project);
+
+    QualityProfile qp1 = createQProfile(QP_NAME_1, LANGUAGE_KEY_1);
+    addMeasure(111, qp1);
+    QualityProfile qp2 = createQProfile(QP_NAME_2, LANGUAGE_KEY_2);
+    addMeasure(12, qp2);
+
+    sut.execute();
+
+    assertThat(toEntries(measureRepository.getNewRawMeasures(1))).containsOnly(entryOf(CoreMetrics.QUALITY_PROFILES_KEY, newMeasureBuilder().create(toJson(qp1, qp2))));
+  }
+
+  @Test
+  public void nothing_to_add_when_no_measure() throws Exception {
+    DumbComponent project = DumbComponent.builder(PROJECT, 1)
+      .addChildren(
+        DumbComponent.builder(MODULE, 11)
+          .addChildren(
+            DumbComponent.builder(MODULE, 111).build()
+          )
+          .build()
+      ).build();
+
+    treeRootHolder.setRoot(project);
+
+    sut.execute();
+
+    assertThat(measureRepository.getNewRawMeasures(1)).isEmpty();
+  }
+
+  @Test
+  public void nothing_to_add_when_measure_already_exists_on_project() throws Exception {
+    DumbComponent project = DumbComponent.builder(PROJECT, 1).build();
+
+    treeRootHolder.setRoot(project);
+
+    QualityProfile qp = createQProfile(QP_NAME_1, LANGUAGE_KEY_1);
+    addMeasure(1, qp);
+
+    sut.execute();
+
+    assertThat(measureRepository.getNewRawMeasures(1)).isEmpty();
+  }
+
+  private static QualityProfile createQProfile(String qpName, String languageKey) {
+    return new QualityProfile(qpName + "-" + languageKey, qpName, languageKey, new Date());
+  }
+
+  private void addMeasure(int componentRef, QualityProfile... qps) {
+    Measure qualityProfileMeasure = newMeasureBuilder().create(toJson(qps));
+    measureRepository.addRawMeasure(componentRef, CoreMetrics.QUALITY_PROFILES_KEY, qualityProfileMeasure);
+  }
+
+  private static String toJson(QualityProfile... qps) {
+    List<QualityProfile> qualityProfiles = Arrays.asList(qps);
+    return QPMeasureData.toJson(new QPMeasureData(qualityProfiles));
+  }
+}