aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2016-09-30 13:15:03 +0200
committerJulien Lancelot <julien.lancelot@sonarsource.com>2016-10-03 18:19:37 +0200
commitbc430dbb92b7b25b3c640625948a37a5a2fa5c16 (patch)
treedf24997fc3b9561111df63a47cb2ac76b4103be8
parent328d1094ff11072c9b4448949428d4cd6318bc16 (diff)
downloadsonarqube-bc430dbb92b7b25b3c640625948a37a5a2fa5c16.tar.gz
sonarqube-bc430dbb92b7b25b3c640625948a37a5a2fa5c16.zip
SONAR-7782 Compute Maintainability Rating on New Code
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java35
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitorTest.java87
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java19
4 files changed, 114 insertions, 29 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java
index 16328ee9f43..c33e94ebe84 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java
@@ -42,6 +42,10 @@ import org.sonar.server.computation.task.projectanalysis.scm.Changeset;
import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo;
import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoRepository;
+import static org.sonar.api.measures.CoreMetrics.NCLOC_DATA_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_TECHNICAL_DEBT_KEY;
import static org.sonar.api.utils.KeyValueFormat.newIntegerConverter;
import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
import static org.sonar.server.computation.task.projectanalysis.measure.Measure.newMeasureBuilder;
@@ -53,6 +57,7 @@ import static org.sonar.server.computation.task.projectanalysis.measure.MeasureV
*
* Compute following measure :
* {@link CoreMetrics#NEW_SQALE_DEBT_RATIO_KEY}
+ * {@link CoreMetrics#NEW_MAINTAINABILITY_RATING_KEY}
*/
public class NewMaintainabilityMeasuresVisitor extends PathAwareVisitorAdapter<NewMaintainabilityMeasuresVisitor.Counter> {
private static final Logger LOG = Loggers.get(NewMaintainabilityMeasuresVisitor.class);
@@ -61,25 +66,31 @@ public class NewMaintainabilityMeasuresVisitor extends PathAwareVisitorAdapter<N
private final MeasureRepository measureRepository;
private final PeriodsHolder periodsHolder;
private final RatingSettings ratingSettings;
+ private final RatingGrid ratingGrid;
private final Metric newDebtMetric;
private final Metric nclocDataMetric;
+
private final Metric newDebtRatioMetric;
+ private final Metric newMaintainabilityRatingMetric;
public NewMaintainabilityMeasuresVisitor(MetricRepository metricRepository, MeasureRepository measureRepository, ScmInfoRepository scmInfoRepository,
- PeriodsHolder periodsHolder, RatingSettings ratingSettings) {
+ PeriodsHolder periodsHolder, RatingSettings ratingSettings) {
super(CrawlerDepthLimit.FILE, POST_ORDER, CounterFactory.INSTANCE);
this.measureRepository = measureRepository;
this.scmInfoRepository = scmInfoRepository;
this.periodsHolder = periodsHolder;
this.ratingSettings = ratingSettings;
+ this.ratingGrid = ratingSettings.getRatingGrid();
// computed by NewDebtAggregator which is executed by IntegrateIssuesVisitor
- this.newDebtMetric = metricRepository.getByKey(CoreMetrics.NEW_TECHNICAL_DEBT_KEY);
+ this.newDebtMetric = metricRepository.getByKey(NEW_TECHNICAL_DEBT_KEY);
// which line is ncloc and which isn't
- this.nclocDataMetric = metricRepository.getByKey(CoreMetrics.NCLOC_DATA_KEY);
- // output metric
- this.newDebtRatioMetric = metricRepository.getByKey(CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY);
+ this.nclocDataMetric = metricRepository.getByKey(NCLOC_DATA_KEY);
+
+ // output metrics
+ this.newDebtRatioMetric = metricRepository.getByKey(NEW_SQALE_DEBT_RATIO_KEY);
+ this.newMaintainabilityRatingMetric = metricRepository.getByKey(NEW_MAINTAINABILITY_RATING_KEY);
}
@Override
@@ -107,14 +118,18 @@ public class NewMaintainabilityMeasuresVisitor extends PathAwareVisitorAdapter<N
}
private void computeAndSaveNewDebtRatioMeasure(Component component, Path<Counter> path) {
- MeasureVariations.Builder builder = newMeasureVariationsBuilder();
+ MeasureVariations.Builder newDebtRatio = newMeasureVariationsBuilder();
+ MeasureVariations.Builder newMaintainability = newMeasureVariationsBuilder();
for (Period period : periodsHolder.getPeriods()) {
double density = computeDensity(path.current(), period);
- builder.setVariation(period, 100.0 * density);
+ newDebtRatio.setVariation(period, 100.0 * density);
+ newMaintainability.setVariation(period, ratingGrid.getRatingForDensity(density).getIndex());
+ }
+ if (!newDebtRatio.isEmpty()) {
+ measureRepository.add(component, this.newDebtRatioMetric, newMeasureBuilder().setVariations(newDebtRatio.build()).createNoValue());
}
- if (!builder.isEmpty()) {
- Measure measure = newMeasureBuilder().setVariations(builder.build()).createNoValue();
- measureRepository.add(component, this.newDebtRatioMetric, measure);
+ if (!newMaintainability.isEmpty()) {
+ measureRepository.add(component, this.newMaintainabilityRatingMetric, newMeasureBuilder().setVariations(newMaintainability.build()).createNoValue());
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitorTest.java
index 015df327fa4..df9964c4c53 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitorTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitorTest.java
@@ -25,11 +25,11 @@ import com.google.common.collect.Ordering;
import java.util.Arrays;
import java.util.Set;
import org.assertj.core.data.Offset;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.KeyValueFormat;
import org.sonar.server.computation.task.projectanalysis.component.Component;
-import org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor;
import org.sonar.server.computation.task.projectanalysis.component.FileAttributes;
import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
@@ -40,6 +40,7 @@ import org.sonar.server.computation.task.projectanalysis.measure.MeasureVariatio
import org.sonar.server.computation.task.projectanalysis.metric.MetricRepositoryRule;
import org.sonar.server.computation.task.projectanalysis.period.Period;
import org.sonar.server.computation.task.projectanalysis.period.PeriodsHolderRule;
+import org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating;
import org.sonar.server.computation.task.projectanalysis.scm.Changeset;
import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoRepositoryRule;
@@ -48,6 +49,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.api.measures.CoreMetrics.NCLOC_DATA;
import static org.sonar.api.measures.CoreMetrics.NCLOC_DATA_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING;
+import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SQALE_DEBT_RATIO;
import static org.sonar.api.measures.CoreMetrics.NEW_SQALE_DEBT_RATIO_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_TECHNICAL_DEBT;
@@ -58,8 +61,13 @@ import static org.sonar.server.computation.task.projectanalysis.component.Compon
import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT;
import static org.sonar.server.computation.task.projectanalysis.measure.Measure.newMeasureBuilder;
import static org.sonar.server.computation.task.projectanalysis.measure.MeasureAssert.assertThat;
+import static org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating.A;
+import static org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating.D;
public class NewMaintainabilityMeasuresVisitorTest {
+
+ private static final double[] RATING_GRID = new double[] {0.1, 0.2, 0.5, 1};
+
private static final String LANGUAGE_1_KEY = "language 1 key";
private static final long LANGUAGE_1_DEV_COST = 30l;
private static final long PERIOD_2_SNAPSHOT_DATE = 12323l;
@@ -78,7 +86,8 @@ public class NewMaintainabilityMeasuresVisitorTest {
public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
.add(NEW_TECHNICAL_DEBT)
.add(NCLOC_DATA)
- .add(NEW_SQALE_DEBT_RATIO);
+ .add(NEW_SQALE_DEBT_RATIO)
+ .add(NEW_MAINTAINABILITY_RATING);
@Rule
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
@Rule
@@ -86,8 +95,14 @@ public class NewMaintainabilityMeasuresVisitorTest {
private RatingSettings ratingSettings = mock(RatingSettings.class);
- private VisitorsCrawler underTest = new VisitorsCrawler(Arrays.<ComponentVisitor>asList(new NewMaintainabilityMeasuresVisitor(metricRepository, measureRepository, scmInfoRepository,
- periodsHolder, ratingSettings)));
+ private VisitorsCrawler underTest;
+
+ @Before
+ public void setUp() throws Exception {
+ when(ratingSettings.getRatingGrid()).thenReturn(new RatingGrid(RATING_GRID));
+ underTest = new VisitorsCrawler(Arrays.asList(new NewMaintainabilityMeasuresVisitor(metricRepository, measureRepository, scmInfoRepository,
+ periodsHolder, ratingSettings)));
+ }
@Test
public void project_has_new_debt_ratio_variation_for_each_defined_period() {
@@ -128,10 +143,8 @@ public class NewMaintainabilityMeasuresVisitorTest {
treeRootHolder.setRoot(
builder(PROJECT, ROOT_REF)
.addChildren(
- builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build()
- )
- .build()
- );
+ builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build())
+ .build());
measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(50, 12));
measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NCLOC_DATA_KEY, createNclocDataMeasure(2, 3, 4));
scmInfoRepository.setScmInfo(LANGUAGE_1_FILE_REF, createChangesets(PERIOD_2_SNAPSHOT_DATE - 100, 4));
@@ -283,11 +296,10 @@ public class NewMaintainabilityMeasuresVisitorTest {
.addChildren(
builder(DIRECTORY, 111)
.addChildren(
- builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build()
- ).build()
- ).build()
- ).build()
- );
+ builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build())
+ .build())
+ .build())
+ .build());
Measure newDebtMeasure = createNewDebtMeasure(50, 12);
measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
@@ -327,8 +339,7 @@ public class NewMaintainabilityMeasuresVisitorTest {
treeRootHolder.setRoot(
builder(PROJECT, ROOT_REF)
.addChildren(builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build())
- .build()
- );
+ .build());
Measure newDebtMeasure = newMeasureBuilder().setVariations(new MeasureVariations(500d, 500d, 500d, 120d, 120d)).createNoValue();
measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
@@ -350,6 +361,40 @@ public class NewMaintainabilityMeasuresVisitorTest {
.hasVariation5(0d, VARIATION_COMPARISON_OFFSET);
}
+ @Test
+ public void compute_new_maintainability_rating() throws Exception {
+ setTwoPeriods();
+ when(ratingSettings.getDevCost(LANGUAGE_1_KEY)).thenReturn(LANGUAGE_1_DEV_COST);
+ treeRootHolder.setRoot(
+ builder(PROJECT, ROOT_REF)
+ .addChildren(
+ builder(MODULE, 11)
+ .addChildren(
+ builder(DIRECTORY, 111)
+ .addChildren(
+ builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY)).build())
+ .build())
+ .build())
+ .build());
+
+ Measure newDebtMeasure = createNewDebtMeasure(50, 12);
+ measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
+ measureRepository.addRawMeasure(111, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(150, 112));
+ measureRepository.addRawMeasure(11, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(200, 112));
+ measureRepository.addRawMeasure(ROOT_REF, NEW_TECHNICAL_DEBT_KEY, createNewDebtMeasure(250, 212));
+ // 4 lines file, only first one is not ncloc
+ measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NCLOC_DATA_KEY, createNclocDataMeasure(2, 3, 4));
+ // first 2 lines are before all snapshots, 2 last lines are after PERIOD 2's snapshot date
+ scmInfoRepository.setScmInfo(LANGUAGE_1_FILE_REF, createChangesets(PERIOD_2_SNAPSHOT_DATE - 100, 2, PERIOD_2_SNAPSHOT_DATE + 100, 2));
+
+ underTest.visit(treeRootHolder.getRoot());
+
+ assertNewMaintainability(LANGUAGE_1_FILE_REF, D, A);
+ assertNewMaintainability(111, D, A);
+ assertNewMaintainability(11, D, A);
+ assertNewMaintainability(ROOT_REF, D, A);
+ }
+
private void setupOneFileAloneInAProject(int newDebtPeriod2, int newDebtPeriod4, Flag isUnitTest, Flag withNclocLines, Flag withChangeSets) {
checkArgument(isUnitTest == Flag.UT_FILE || isUnitTest == Flag.SRC_FILE);
checkArgument(withNclocLines == Flag.WITH_NCLOC || withNclocLines == Flag.NO_NCLOC || withNclocLines == Flag.MISSING_MEASURE_NCLOC);
@@ -358,10 +403,8 @@ public class NewMaintainabilityMeasuresVisitorTest {
treeRootHolder.setRoot(
builder(PROJECT, ROOT_REF)
.addChildren(
- builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(isUnitTest == Flag.UT_FILE, LANGUAGE_1_KEY)).build()
- )
- .build()
- );
+ builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(isUnitTest == Flag.UT_FILE, LANGUAGE_1_KEY)).build())
+ .build());
Measure newDebtMeasure = createNewDebtMeasure(newDebtPeriod2, newDebtPeriod4);
measureRepository.addRawMeasure(LANGUAGE_1_FILE_REF, NEW_TECHNICAL_DEBT_KEY, newDebtMeasure);
@@ -448,6 +491,12 @@ public class NewMaintainabilityMeasuresVisitorTest {
.hasVariation4(expectedPeriod4Value, VARIATION_COMPARISON_OFFSET);
}
+ private void assertNewMaintainability(int componentRef, Rating expectedPeriod2Value, Rating expectedPeriod4Value) {
+ assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_MAINTAINABILITY_RATING_KEY))
+ .hasVariation2(expectedPeriod2Value.getIndex())
+ .hasVariation4(expectedPeriod4Value.getIndex());
+ }
+
private void setTwoPeriods() {
periodsHolder.setPeriods(
new Period(2, SOME_PERIOD_MODE, null, PERIOD_2_SNAPSHOT_DATE, SOME_ANALYSIS_UUID),
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 538d6ed97bf..0b173769ef3 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -392,6 +392,8 @@ metric.new_lines_to_cover.description=Lines to cover on new code
metric.new_lines_to_cover.name=Lines to Cover on New Code
metric.new_line_coverage.description=Line coverage of added/changed code
metric.new_line_coverage.name=Line Coverage on New Code
+metric.new_maintainability_rating.description=Maintainability rating on new code
+metric.new_maintainability_rating.name=Maintainability Rating on New Code
metric.new_major_violations.description=New Major issues
metric.new_major_violations.name=New Major Issues
metric.new_minor_violations.description=New Minor issues
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
index 3be47d98602..2ba6e188ee5 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
@@ -2169,6 +2169,25 @@ public final class CoreMetrics {
.create();
/**
+ * @since 6.2
+ */
+ public static final String NEW_MAINTAINABILITY_RATING_KEY = "new_maintainability_rating";
+
+ /**
+ * @since 6.2
+ */
+ public static final Metric<Integer> NEW_MAINTAINABILITY_RATING = new Metric.Builder(NEW_MAINTAINABILITY_RATING_KEY, "Maintainability Rating on New Code", Metric.ValueType.RATING)
+ .setDescription("Maintainability rating on new code")
+ .setDomain(DOMAIN_MAINTAINABILITY)
+ .setDirection(Metric.DIRECTION_WORST)
+ .setDeleteHistoricalData(true)
+ .setOptimizedBestValue(true)
+ .setQualitative(true)
+ .setBestValue(1.0)
+ .setWorstValue(5.0)
+ .create();
+
+ /**
* @since 4.5
*/
public static final String DEVELOPMENT_COST_KEY = "development_cost";