@@ -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 |
@@ -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"; | |||
} | |||
} |
@@ -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) { |
@@ -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)); | |||
} | |||
} |