Browse Source

SONAR-6680 Create step to compute quality profile measure in compute engine

tags/5.2-RC1
Julien Lancelot 9 years ago
parent
commit
a6f8589f70

+ 2
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java View 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

+ 119
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeQProfileMeasureStep.java View File

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

+ 5
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java View 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) {

+ 159
- 0
server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeQProfileMeasureStepTest.java View File

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

Loading…
Cancel
Save