aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2016-08-12 17:53:09 +0200
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2016-08-13 12:08:39 +0200
commit062c27d9de4d892209dc05087ae3dc68e0b93cb4 (patch)
tree2308a470793f52c326d275bbbb2a955c068702db
parented92f9d0d05ea12d91176dcbe8c63a8f7eb685e6 (diff)
downloadsonarqube-062c27d9de4d892209dc05087ae3dc68e0b93cb4.tar.gz
sonarqube-062c27d9de4d892209dc05087ae3dc68e0b93cb4.zip
SONAR-7957 New metric « New Lines of Code »
-rw-r--r--it/it-tests/src/test/java/it/dbCleaner/PurgeTest.java24
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewLinesMeasureStep.java185
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java1
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportNewLinesMeasureStepTest.java192
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties4
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java29
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/resources/CoreMetricsTest.java3
7 files changed, 423 insertions, 15 deletions
diff --git a/it/it-tests/src/test/java/it/dbCleaner/PurgeTest.java b/it/it-tests/src/test/java/it/dbCleaner/PurgeTest.java
index 73cade69b6f..3891af3855f 100644
--- a/it/it-tests/src/test/java/it/dbCleaner/PurgeTest.java
+++ b/it/it-tests/src/test/java/it/dbCleaner/PurgeTest.java
@@ -115,10 +115,10 @@ public class PurgeTest {
// must be a different date, else a single snapshot is kept per day
scan(PROJECT_SAMPLE_PATH, DateFormatUtils.ISO_DATE_FORMAT.format(today));
- int newMeasuresOnTrk = 55;
- int newMeasuresOnBrc = 286;
- int newMeasuresOnDir = 44;
- int newMeasuresOnFil = 0;
+ int newMeasuresOnTrk = 56;
+ int newMeasuresOnBrc = 292;
+ int newMeasuresOnDir = 48;
+ int newMeasuresOnFil = 4;
assertMeasuresCountForQualifier("TRK", measuresOnTrk + newMeasuresOnTrk);
assertMeasuresCountForQualifier("BRC", measuresOnBrc + newMeasuresOnBrc);
@@ -129,7 +129,7 @@ public class PurgeTest {
collector.checkThat(
"Wrong number of measure of new_ metrics",
count("project_measures, metrics where metrics.id = project_measures.metric_id and metrics.name like 'new_%'"),
- equalTo(121));
+ equalTo(136));
// added measures relate to project and new_* metrics
expectedMeasures += newMeasuresOnTrk + newMeasuresOnBrc + newMeasuresOnDir + newMeasuresOnFil;
@@ -256,8 +256,9 @@ public class PurgeTest {
scan(PROJECT_SAMPLE_PATH, "2012-02-02");
- assertThat(count(COUNT_FILE_MEASURES)).isEqualTo(fileMeasures);
- assertThat(count(COUNT_DIR_MEASURES)).isLessThan(2 * dirMeasures); // second analysis as NEW_* metrics
+ // second analysis with new_* metrics
+ assertThat(count(COUNT_FILE_MEASURES)).isLessThan(2 * fileMeasures);
+ assertThat(count(COUNT_DIR_MEASURES)).isLessThan(2 * dirMeasures);
}
/**
@@ -274,8 +275,9 @@ public class PurgeTest {
scan(PROJECT_SAMPLE_PATH, "2012-02-02");
- assertThat(count(COUNT_FILE_MEASURES)).isEqualTo(fileMeasures);
- assertThat(count(COUNT_DIR_MEASURES)).isGreaterThan(2 * dirMeasures); // second analysis as NEW_* metrics
+ // second analysis as NEW_* metrics
+ assertThat(count(COUNT_FILE_MEASURES)).isLessThan( 2 * fileMeasures);
+ assertThat(count(COUNT_DIR_MEASURES)).isGreaterThan(2 * dirMeasures);
}
/**
@@ -359,8 +361,8 @@ public class PurgeTest {
private void logMeasures(String title, String qualifier) {
String sql = "SELECT m.name as metricName, pm.value as value, pm.text_value as textValue, pm.variation_value_1, pm.variation_value_2, pm.variation_value_3 "
+
- "FROM project_measures pm, snapshots s, metrics m " +
- "WHERE pm.snapshot_id=s.id and pm.metric_id=m.id and s.qualifier='"
+ "FROM project_measures pm, projects p, metrics m " +
+ "WHERE pm.component_uuid=p.uuid and pm.metric_id=m.id and p.qualifier='"
+ qualifier + "'";
List<Map<String, String>> rows = orchestrator.getDatabase().executeSql(sql);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewLinesMeasureStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewLinesMeasureStep.java
new file mode 100644
index 00000000000..f05bad753f9
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewLinesMeasureStep.java
@@ -0,0 +1,185 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.task.projectanalysis.step;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.Map;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.core.util.stream.Collectors;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.server.computation.task.projectanalysis.component.PathAwareCrawler;
+import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
+import org.sonar.server.computation.task.projectanalysis.formula.Counter;
+import org.sonar.server.computation.task.projectanalysis.formula.CounterInitializationContext;
+import org.sonar.server.computation.task.projectanalysis.formula.CreateMeasureContext;
+import org.sonar.server.computation.task.projectanalysis.formula.Formula;
+import org.sonar.server.computation.task.projectanalysis.formula.FormulaExecutorComponentVisitor;
+import org.sonar.server.computation.task.projectanalysis.formula.counter.IntVariationValue;
+import org.sonar.server.computation.task.projectanalysis.measure.Measure;
+import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository;
+import org.sonar.server.computation.task.projectanalysis.measure.MeasureVariations;
+import org.sonar.server.computation.task.projectanalysis.metric.Metric;
+import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository;
+import org.sonar.server.computation.task.projectanalysis.period.Period;
+import org.sonar.server.computation.task.projectanalysis.period.PeriodsHolder;
+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 org.sonar.server.computation.task.step.ComputationStep;
+
+import static org.sonar.api.measures.CoreMetrics.NCLOC_DATA_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_NCLOC_KEY;
+import static org.sonar.api.utils.KeyValueFormat.newIntegerConverter;
+
+/**
+ * Computes new lines of code measure on files and then aggregates them on higher components.
+ */
+public class NewLinesMeasureStep implements ComputationStep {
+
+ private final TreeRootHolder treeRootHolder;
+ private final PeriodsHolder periodsHolder;
+ private final MetricRepository metricRepository;
+ private final MeasureRepository measureRepository;
+ private final ScmInfoRepository scmInfoRepository;
+
+ public NewLinesMeasureStep(TreeRootHolder treeRootHolder, PeriodsHolder periodsHolder, MetricRepository metricRepository, MeasureRepository measureRepository,
+ ScmInfoRepository scmInfoRepository) {
+ this.treeRootHolder = treeRootHolder;
+ this.periodsHolder = periodsHolder;
+ this.metricRepository = metricRepository;
+ this.measureRepository = measureRepository;
+ this.scmInfoRepository = scmInfoRepository;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Compute new lines of code";
+ }
+
+ @Override
+ public void execute() {
+ new PathAwareCrawler<>(
+ FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
+ .withVariationSupport(periodsHolder)
+ .buildFor(ImmutableList.of(new NewLinesFormula(measureRepository, scmInfoRepository, metricRepository.getByKey(NCLOC_DATA_KEY)))))
+ .visit(treeRootHolder.getRoot());
+ }
+
+ private static class NewLinesCounter implements Counter<NewLinesCounter> {
+ private final MeasureRepository measureRepository;
+ private final ScmInfoRepository scmInfoRepository;
+ private final Metric nclocDataMetric;
+
+ private final IntVariationValue.Array newLines = IntVariationValue.newArray();
+
+ private NewLinesCounter(MeasureRepository measureRepository, ScmInfoRepository scmInfoRepository, Metric nclocDataMetric) {
+ this.measureRepository = measureRepository;
+ this.scmInfoRepository = scmInfoRepository;
+ this.nclocDataMetric = nclocDataMetric;
+ }
+
+ @Override
+ public void aggregate(NewLinesCounter counter) {
+ this.newLines.incrementAll(counter.newLines);
+ }
+
+ @Override
+ public void initialize(CounterInitializationContext context) {
+ context.getPeriods().forEach(period -> newLines.increment(period, 0));
+
+ Component leak = context.getLeaf();
+ if (leak.getType() != Component.Type.FILE) {
+ return;
+ }
+
+ Optional<ScmInfo> optionalScmInfo = scmInfoRepository.getScmInfo(leak);
+ Optional<Measure> nclocData = measureRepository.getRawMeasure(leak, nclocDataMetric);
+
+ if (!nclocData.isPresent() || !optionalScmInfo.isPresent()) {
+ return;
+ }
+
+ ScmInfo scmInfo = optionalScmInfo.get();
+
+ nclocLineNumbers(nclocData.get()).stream()
+ .map(scmInfo::getChangesetForLine)
+ .forEach(changeset -> context.getPeriods().stream()
+ .filter(period -> isLineInPeriod(changeset, period))
+ .forEach(period -> newLines.increment(period, 1)));
+ }
+
+ /**
+ * NCLOC_DATA contains Key-value pairs, where key - is a line number, and value - is an indicator of whether line
+ * contains code (1) or not (0).
+ *
+ * This method parses the value of the NCLOC_DATA measure and return the line numbers which contain code.
+ */
+ private static List<Integer> nclocLineNumbers(Measure nclocDataMeasure) {
+ Map<Integer, Integer> parsedNclocData = KeyValueFormat.parse(nclocDataMeasure.getData(), newIntegerConverter(), newIntegerConverter());
+ return parsedNclocData.entrySet()
+ .stream()
+ .filter(entry -> entry.getValue() == 1)
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toList());
+ }
+
+ private static boolean isLineInPeriod(Changeset changeset, Period period) {
+ return changeset.getDate() > period.getSnapshotDate();
+ }
+ }
+
+ private static final class NewLinesFormula implements Formula<NewLinesCounter> {
+ private final MeasureRepository measureRepository;
+ private final ScmInfoRepository scmInfoRepository;
+ private final Metric nclocDataMetric;
+
+ private NewLinesFormula(MeasureRepository measureRepository, ScmInfoRepository scmInfoRepository, Metric nclocDataMetric) {
+ this.measureRepository = measureRepository;
+ this.scmInfoRepository = scmInfoRepository;
+ this.nclocDataMetric = nclocDataMetric;
+ }
+
+ @Override
+ public NewLinesCounter createNewCounter() {
+ return new NewLinesCounter(measureRepository, scmInfoRepository, nclocDataMetric);
+ }
+
+ @Override
+ public Optional<Measure> createMeasure(NewLinesCounter counter, CreateMeasureContext context) {
+ String metricKey = context.getMetric().getKey();
+ if (NEW_NCLOC_KEY.equals(metricKey)) {
+ Optional<MeasureVariations> newLines = counter.newLines.toMeasureVariations();
+ return newLines.isPresent()
+ ? Optional.of(Measure.newMeasureBuilder().setVariations(newLines.get()).createNoValue())
+ : Optional.absent();
+ }
+
+ throw new IllegalArgumentException("Unsupported metric " + context.getMetric());
+ }
+
+ @Override
+ public String[] getOutputMetricKeys() {
+ return new String[] {NEW_NCLOC_KEY};
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java
index 7785c691bd2..e9038735573 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java
@@ -59,6 +59,7 @@ public class ReportComputationSteps extends AbstractComputationSteps {
// data computation
SizeMeasuresStep.class,
+ NewLinesMeasureStep.class,
NewCoverageMeasuresStep.class,
CoverageMeasuresStep.class,
CommentMeasuresStep.class,
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportNewLinesMeasureStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportNewLinesMeasureStepTest.java
new file mode 100644
index 00000000000..a9adc37aa05
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportNewLinesMeasureStepTest.java
@@ -0,0 +1,192 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.task.projectanalysis.step;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.server.computation.task.projectanalysis.measure.Measure;
+import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.task.projectanalysis.measure.MeasureVariations;
+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.scm.Changeset;
+import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoRepositoryRule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+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_NCLOC;
+import static org.sonar.api.measures.CoreMetrics.NEW_NCLOC_KEY;
+import static org.sonar.api.utils.DateUtils.parseDate;
+import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.DIRECTORY;
+import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.FILE;
+import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.MODULE;
+import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT;
+import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder;
+
+public class ReportNewLinesMeasureStepTest {
+ private static final int ROOT_REF = 1;
+ private static final int MODULE_REF = 12;
+ private static final int SUB_MODULE_1_REF = 123;
+ private static final int SUB_MODULE_2_REF = 126;
+ private static final int DIRECTORY_REF = 1234;
+ private static final int DIRECTORY_2_REF = 1235;
+ private static final int FILE_1_REF = 12341;
+ private static final int FILE_2_REF = 12342;
+ private static final int FILE_3_REF = 1261;
+ private static final int FILE_4_REF = 1262;
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule()
+ .setRoot(
+ builder(PROJECT, ROOT_REF)
+ .addChildren(
+ builder(MODULE, MODULE_REF)
+ .addChildren(
+ builder(MODULE, SUB_MODULE_1_REF)
+ .addChildren(
+ builder(DIRECTORY, DIRECTORY_REF)
+ .addChildren(
+ builder(FILE, FILE_1_REF).build(),
+ builder(FILE, FILE_2_REF).build())
+ .build(),
+ builder(DIRECTORY, DIRECTORY_2_REF).build())
+ .build(),
+ builder(MODULE, SUB_MODULE_2_REF)
+ .addChildren(
+ builder(FILE, FILE_3_REF).build(),
+ builder(FILE, FILE_4_REF).build())
+ .build())
+ .build())
+ .build());
+ @Rule
+ public PeriodsHolderRule periodsHolder = new PeriodsHolderRule().setPeriods(
+ new Period(2, "mode_p_1", null, parseDate("2009-12-25").getTime(), "u1"),
+ new Period(5, "mode_p_5", null, parseDate("2011-02-18").getTime(), "u2"));
+ @Rule
+ public ScmInfoRepositoryRule scmInfoRepository = new ScmInfoRepositoryRule();
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(NEW_NCLOC)
+ .add(NCLOC_DATA);
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+ NewLinesMeasureStep underTest = new NewLinesMeasureStep(treeRootHolder, periodsHolder, metricRepository, measureRepository, scmInfoRepository);
+
+ @Test
+ public void compute_new_ncloc() {
+ setChangesets(FILE_1_REF, FILE_2_REF, FILE_3_REF, FILE_4_REF);
+ setNclocsExcept(FILE_1_REF, 2, 4, 6);
+ setNclocsExcept(FILE_2_REF);
+ setNclocsExcept(FILE_3_REF, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
+ setNclocsExcept(FILE_4_REF, 1, 2);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, NEW_NCLOC_KEY, 12 - 1 - 3);
+ assertRawMeasureValue(FILE_2_REF, NEW_NCLOC_KEY, 12 - 1);
+ assertRawMeasureValue(FILE_3_REF, NEW_NCLOC_KEY, 0);
+ assertRawMeasureValue(FILE_4_REF, NEW_NCLOC_KEY, 12 - 1 - 2);
+ assertRawMeasureValue(DIRECTORY_REF, NEW_NCLOC_KEY, 19);
+ assertRawMeasureValue(DIRECTORY_2_REF, NEW_NCLOC_KEY, 0);
+ assertRawMeasureValue(SUB_MODULE_1_REF, NEW_NCLOC_KEY, 19);
+ assertRawMeasureValue(SUB_MODULE_2_REF, NEW_NCLOC_KEY, 9);
+ assertRawMeasureValue(MODULE_REF, NEW_NCLOC_KEY, 28);
+ assertRawMeasureValue(ROOT_REF, NEW_NCLOC_KEY, 28);
+ }
+
+ @Test
+ public void compute_with_no_changeset() {
+ setNclocsExcept(FILE_1_REF);
+
+ underTest.execute();
+
+ assertNoRawMeasureValue();
+ }
+
+ @Test
+ public void compute_with_no_ncloc_data() {
+ underTest.execute();
+
+ assertNoRawMeasureValue();
+ }
+
+ private void assertNoRawMeasureValue() {
+ assertRawMeasureValue(FILE_1_REF, NEW_NCLOC_KEY, 0);
+ assertRawMeasureValue(FILE_2_REF, NEW_NCLOC_KEY, 0);
+ assertRawMeasureValue(FILE_3_REF, NEW_NCLOC_KEY, 0);
+ assertRawMeasureValue(FILE_4_REF, NEW_NCLOC_KEY, 0);
+ assertRawMeasureValue(DIRECTORY_REF, NEW_NCLOC_KEY, 0);
+ assertRawMeasureValue(DIRECTORY_2_REF, NEW_NCLOC_KEY, 0);
+ assertRawMeasureValue(SUB_MODULE_1_REF, NEW_NCLOC_KEY, 0);
+ assertRawMeasureValue(SUB_MODULE_2_REF, NEW_NCLOC_KEY, 0);
+ assertRawMeasureValue(MODULE_REF, NEW_NCLOC_KEY, 0);
+ assertRawMeasureValue(ROOT_REF, NEW_NCLOC_KEY, 0);
+ }
+
+ private void assertRawMeasureValue(int componentRef, String metricKey, double period2Value) {
+ assertRawMeasureValue(componentRef, metricKey, period2Value, 0d);
+ }
+
+ private void assertRawMeasureValue(int componentRef, String metricKey, double period2Value, double period5Value) {
+ MeasureVariations variations = measureRepository.getAddedRawMeasure(componentRef, metricKey).get().getVariations();
+ assertThat(variations.getVariation2()).isEqualTo(period2Value);
+ assertThat(variations.getVariation5()).isEqualTo(period5Value);
+ }
+
+ private void setNclocsExcept(int componentRef, Integer... lineNumbersNotLineOfCode) {
+ List<Integer> notLocNumbers = Arrays.asList(lineNumbersNotLineOfCode);
+ Map<Integer, Integer> nclocData = IntStream.rangeClosed(1, 12)
+ .filter(lineNumber -> !notLocNumbers.contains(lineNumber))
+ .boxed()
+ .collect(Collectors.toMap(Function.identity(), lineNumber -> 1));
+ measureRepository.addRawMeasure(componentRef, NCLOC_DATA_KEY, Measure.newMeasureBuilder().create(KeyValueFormat.format(nclocData)));
+ }
+
+ private void setChangesets(int... componentRefs) {
+ Arrays.stream(componentRefs)
+ .forEach(componentRef -> scmInfoRepository.setScmInfo(componentRef,
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-1").build(),
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-1").build(),
+ // line 3 is older, part of no period
+ Changeset.newChangesetBuilder().setDate(parseDate("2007-01-15").getTime()).setRevision("rev-2").build(),
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-1").build(),
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-1").build(),
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-1").build(),
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-1").build(),
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-1").build(),
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-1").build(),
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-1").build(),
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-1").build(),
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-1").build()));
+ }
+
+}
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 2fe6894a9fb..dd6158fd3e2 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -2455,7 +2455,7 @@ metric.major_violations.name=Major Issues
metric.minor_violations.description=Minor issues
metric.minor_violations.name=Minor Issues
metric.ncloc.abbreviation=LOC
-metric.ncloc.description=Non Commenting Lines of Code
+metric.ncloc.description=Non commenting lines of code
metric.ncloc.name=Lines of Code
metric.ncloc_language_distribution.description=Non Commenting Lines of Code Distributed By Language
metric.ncloc_language_distribution.name=Lines of Code Per Language
@@ -2501,6 +2501,8 @@ metric.new_major_violations.description=New Major issues
metric.new_major_violations.name=New Major Issues
metric.new_minor_violations.description=New Minor issues
metric.new_minor_violations.name=New Minor Issues
+metric.new_ncloc.name=New Lines of Code
+metric.new_ncloc.description=New non commenting lines of code
metric.new_overall_branch_coverage.description=Condition coverage of new/changed code by all tests
metric.new_overall_branch_coverage.name=Overall Condition Coverage on New Code
metric.new_overall_conditions_to_cover.description=New conditions to cover by all tests
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 a576efb7530..58bce43ac4b 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
@@ -123,13 +123,28 @@ public final class CoreMetrics {
public static final String NCLOC_KEY = "ncloc";
public static final Metric<Integer> NCLOC = new Metric.Builder(NCLOC_KEY, "Lines of Code", Metric.ValueType.INT)
- .setDescription("Non Commenting Lines of Code")
+ .setDescription("Non commenting lines of code")
.setDirection(Metric.DIRECTION_WORST)
.setQualitative(false)
.setDomain(DOMAIN_SIZE)
.create();
/**
+ * @since 6.1
+ */
+ public static final String NEW_NCLOC_KEY = "new_ncloc";
+ /**
+ * @since 6.1
+ */
+ public static final Metric<Integer> NEW_NCLOC = new Metric.Builder(NEW_NCLOC_KEY, "New Lines of Code", Metric.ValueType.INT)
+ .setDescription("New non commenting lines of code")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(false)
+ .setDomain(DOMAIN_SIZE)
+ .setDeleteHistoricalData(true)
+ .create();
+
+ /**
* @since 4.4
*/
public static final String NCLOC_LANGUAGE_DISTRIBUTION_KEY = "ncloc_language_distribution";
@@ -1314,7 +1329,13 @@ public final class CoreMetrics {
.setOptimizedBestValue(true)
.create();
+ /**
+ * @since 6.1
+ */
public static final String NEW_LINES_DUPLICATED_KEY = "new_duplicated_lines";
+ /**
+ * @since 6.1
+ */
public static final Metric<Integer> NEW_LINES_DUPLICATED = new Metric.Builder(NEW_LINES_DUPLICATED_KEY, "New Duplicated Lines", Metric.ValueType.INT)
.setDescription("New duplicated lines")
.setDirection(Metric.DIRECTION_WORST)
@@ -1334,7 +1355,13 @@ public final class CoreMetrics {
.setOptimizedBestValue(true)
.create();
+ /**
+ * @since 6.1
+ */
public static final String NEW_BLOCKS_DUPLICATED_KEY = "new_duplicated_blocks";
+ /**
+ * @since 6.1
+ */
public static final Metric<Integer> NEW_BLOCKS_DUPLICATED = new Metric.Builder(NEW_BLOCKS_DUPLICATED_KEY, "Duplicated Blocks on New Code", Metric.ValueType.INT)
.setDescription("Duplicated blocks on new code")
.setDirection(Metric.DIRECTION_WORST)
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/resources/CoreMetricsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/resources/CoreMetricsTest.java
index e82d793431a..854cef24431 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/resources/CoreMetricsTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/resources/CoreMetricsTest.java
@@ -19,12 +19,11 @@
*/
package org.sonar.api.resources;
+import java.util.List;
import org.junit.Test;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Metric;
-import java.util.List;
-
import static org.assertj.core.api.Assertions.assertThat;
public class CoreMetricsTest {