aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2015-07-03 16:46:05 +0200
committerJulien Lancelot <julien.lancelot@sonarsource.com>2015-07-07 10:40:25 +0200
commita6f8589f7015886ba1a325ce6955d27d65da07cf (patch)
tree0ee53145eb1eed83a7b7d2f50d774906cb91a583
parent7787674e7c193b0e7f6fbaac7212c30f1a0797ee (diff)
downloadsonarqube-a6f8589f7015886ba1a325ce6955d27d65da07cf.tar.gz
sonarqube-a6f8589f7015886ba1a325ce6955d27d65da07cf.zip
SONAR-6680 Create step to compute quality profile measure in compute engine
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeQProfileMeasureStep.java119
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeQProfileMeasureStepTest.java159
4 files changed, 285 insertions, 2 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
index 1d7f0e161e5..46c8c4b9241 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
@@ -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
index 00000000000..5f5dbcce21b
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeQProfileMeasureStep.java
@@ -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";
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java
index 20982691441..e8541194f51 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java
@@ -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
index 00000000000..e2b96d9fdd8
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeQProfileMeasureStepTest.java
@@ -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));
+ }
+}