aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2016-08-10 18:09:58 +0200
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2016-08-12 15:56:36 +0200
commitde2eaf079f1933eb9dcdb511eeae7207ac4d10c8 (patch)
tree3040ef5e1697bc31b4c79738b404c9964867a45d /server
parent531135e491d9cdd11d5b687881945cf88f3ddaae (diff)
downloadsonarqube-de2eaf079f1933eb9dcdb511eeae7207ac4d10c8.tar.gz
sonarqube-de2eaf079f1933eb9dcdb511eeae7207ac4d10c8.zip
SONAR-7951 New metric « Duplicated lines on new code »
Diffstat (limited to 'server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/formula/VariationSumFormula.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewDuplicationMeasuresStep.java215
-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/ReportNewDuplicationMeasuresStepTest.java280
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsDuplicationMeasuresStepTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsNewDuplicationMeasuresStepTest.java153
6 files changed, 650 insertions, 5 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/formula/VariationSumFormula.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/formula/VariationSumFormula.java
index 4270f7e41e4..249a03095d2 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/formula/VariationSumFormula.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/formula/VariationSumFormula.java
@@ -49,7 +49,7 @@ public class VariationSumFormula implements Formula<VariationSumFormula.Variatio
public VariationSumFormula(String metricKey, Predicate<Period> supportedPeriods, @Nullable Double defaultInputValue) {
this.metricKey = requireNonNull(metricKey, "Metric key cannot be null");
- this.supportedPeriods = requireNonNull(supportedPeriods, "Period predicate can not be null");
+ this.supportedPeriods = requireNonNull(supportedPeriods, "Period predicate cannot be null");
this.defaultInputValue = defaultInputValue;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewDuplicationMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewDuplicationMeasuresStep.java
new file mode 100644
index 00000000000..c0de8c85b5f
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewDuplicationMeasuresStep.java
@@ -0,0 +1,215 @@
+/*
+ * 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 com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.SetMultimap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+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.duplication.Duplication;
+import org.sonar.server.computation.task.projectanalysis.duplication.DuplicationRepository;
+import org.sonar.server.computation.task.projectanalysis.duplication.InnerDuplicate;
+import org.sonar.server.computation.task.projectanalysis.duplication.TextBlock;
+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.VariationSumFormula;
+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.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.ScmInfo;
+import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoRepository;
+import org.sonar.server.computation.task.step.ComputationStep;
+
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.toMap;
+import static org.sonar.api.measures.CoreMetrics.NEW_LINES_DUPLICATED_KEY;
+import static org.sonar.server.computation.task.projectanalysis.period.PeriodPredicates.viewsRestrictedPeriods;
+
+/**
+ * Computes new duplication measures on files and then aggregates them on higher components.
+ *
+ */
+public class NewDuplicationMeasuresStep implements ComputationStep {
+
+ private final ImmutableList<Formula> formulas;
+
+ private final TreeRootHolder treeRootHolder;
+ private final PeriodsHolder periodsHolder;
+ private final MetricRepository metricRepository;
+ private final MeasureRepository measureRepository;
+
+ public NewDuplicationMeasuresStep(TreeRootHolder treeRootHolder, PeriodsHolder periodsHolder, MetricRepository metricRepository, MeasureRepository measureRepository,
+ ScmInfoRepository scmInfoRepository, @Nullable DuplicationRepository duplicationRepository) {
+ this.treeRootHolder = treeRootHolder;
+ this.periodsHolder = periodsHolder;
+ this.metricRepository = metricRepository;
+ this.measureRepository = measureRepository;
+ this.formulas = ImmutableList.of(NewDuplicationFormula.from(scmInfoRepository, duplicationRepository));
+ }
+
+ /**
+ * Constructor used by Pico in Governance where no DuplicationRepository is available.
+ */
+ public NewDuplicationMeasuresStep(TreeRootHolder treeRootHolder, PeriodsHolder periodsHolder, MetricRepository metricRepository, MeasureRepository measureRepository) {
+ this(treeRootHolder, periodsHolder, metricRepository, measureRepository, null, null);
+ }
+
+ @Override
+ public String getDescription() {
+ return "Compute new duplication measures";
+ }
+
+ @Override
+ public void execute() {
+ new PathAwareCrawler<>(
+ FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
+ .withVariationSupport(periodsHolder)
+ .buildFor(formulas))
+ .visit(treeRootHolder.getRoot());
+ }
+
+ private static class NewDuplicationCounter implements Counter<NewDuplicationCounter> {
+ @CheckForNull
+ private final DuplicationRepository duplicationRepository;
+ private final ScmInfoRepository scmInfoRepository;
+ private final IntVariationValue.Array newLines = IntVariationValue.newArray();
+
+ private NewDuplicationCounter(@Nullable DuplicationRepository duplicationRepository, ScmInfoRepository scmInfoRepository) {
+ this.duplicationRepository = duplicationRepository;
+ this.scmInfoRepository = scmInfoRepository;
+ }
+
+ @Override
+ public void aggregate(NewDuplicationCounter counter) {
+ this.newLines.incrementAll(counter.newLines);
+ }
+
+ @Override
+ public void initialize(CounterInitializationContext context) {
+ Component leaf = context.getLeaf();
+ Iterable<Duplication> duplications = requireNonNull(this.duplicationRepository, "DuplicationRepository missing").getDuplications(leaf);
+ Optional<ScmInfo> scmInfo = scmInfoRepository.getScmInfo(leaf);
+
+ if (!scmInfo.isPresent()) {
+ return;
+ }
+
+ NewLinesDuplicatedAccumulator newLinesDuplicatedAccumulator = new NewLinesDuplicatedAccumulator(scmInfo.get(), context.getPeriods());
+
+ for (Duplication duplication : duplications) {
+ newLinesDuplicatedAccumulator.addBlock(duplication.getOriginal());
+ duplication.getDuplicates().stream()
+ .filter(InnerDuplicate.class::isInstance)
+ .map(duplicate -> (InnerDuplicate) duplicate)
+ .forEach(duplicate -> newLinesDuplicatedAccumulator.addBlock(duplicate.getTextBlock()));
+ }
+
+ Map<Period, Integer> newLinesDuplicatedByPeriod = newLinesDuplicatedAccumulator.getNewLinesDuplicated();
+ context.getPeriods().forEach(period -> newLines.increment(period, newLinesDuplicatedByPeriod.getOrDefault(period, 0)));
+ }
+ }
+
+ private static class NewLinesDuplicatedAccumulator {
+ private final ScmInfo scmInfo;
+ private final List<Period> periods;
+ private final SetMultimap<Period, Integer> counts;
+
+ private NewLinesDuplicatedAccumulator(ScmInfo scmInfo, List<Period> periods) {
+ this.scmInfo = scmInfo;
+ this.periods = periods;
+ this.counts = LinkedHashMultimap.create(periods.size(), Iterables.size(scmInfo.getAllChangesets()));
+ }
+
+ void addBlock(TextBlock textBlock) {
+ IntStream.rangeClosed(textBlock.getStart(), textBlock.getEnd())
+ .forEach(line -> periods.stream()
+ .filter(period -> isLineInPeriod(line, period))
+ .forEach(period -> counts.put(period, line)));
+ }
+
+ Map<Period, Integer> getNewLinesDuplicated() {
+ return ImmutableMap.copyOf(counts.keySet().stream().collect(toMap(Function.identity(), period -> counts.get(period).size())));
+ }
+
+ private boolean isLineInPeriod(int lineNumber, Period period) {
+ return scmInfo.getChangesetForLine(lineNumber).getDate() > period.getSnapshotDate();
+ }
+ }
+
+ private static final class NewDuplicationFormula implements Formula<NewDuplicationCounter> {
+ private static final Formula VIEW_FORMULA = new VariationSumFormula(NEW_LINES_DUPLICATED_KEY, viewsRestrictedPeriods(), 0.0d);
+
+ private final DuplicationRepository duplicationRepository;
+ private final ScmInfoRepository scmInfoRepository;
+
+ private NewDuplicationFormula(ScmInfoRepository scmInfoRepository, @Nullable DuplicationRepository duplicationRepository) {
+ this.duplicationRepository = duplicationRepository;
+ this.scmInfoRepository = scmInfoRepository;
+ }
+
+ public static Formula<?> from(@Nullable ScmInfoRepository scmInfoRepository, @Nullable DuplicationRepository duplicationRepository) {
+ return scmInfoRepository == null
+ ? VIEW_FORMULA
+ : new NewDuplicationFormula(scmInfoRepository, duplicationRepository);
+ }
+
+ @Override
+ public NewDuplicationCounter createNewCounter() {
+ return new NewDuplicationCounter(duplicationRepository, scmInfoRepository);
+ }
+
+ @Override
+ public Optional<Measure> createMeasure(NewDuplicationCounter counter, CreateMeasureContext context) {
+ String metricKey = context.getMetric().getKey();
+ if (NEW_LINES_DUPLICATED_KEY.equals(metricKey)) {
+ Optional<MeasureVariations> variations = counter.newLines.toMeasureVariations();
+ return variations.isPresent()
+ ? Optional.of(Measure.newMeasureBuilder().setVariations(variations.get()).createNoValue())
+ : Optional.absent();
+ }
+
+ throw new IllegalArgumentException("Unsupported metric " + context.getMetric());
+ }
+
+ @Override
+ public String[] getOutputMetricKeys() {
+ return new String[] {NEW_LINES_DUPLICATED_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 eed8275241d..7785c691bd2 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
@@ -65,6 +65,7 @@ public class ReportComputationSteps extends AbstractComputationSteps {
CustomMeasuresCopyStep.class,
DuplicationMeasuresStep.class,
DuplicationDataMeasuresStep.class,
+ NewDuplicationMeasuresStep.class,
LanguageDistributionMeasuresStep.class,
UnitTestMeasuresStep.class,
ComplexityMeasuresStep.class,
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportNewDuplicationMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportNewDuplicationMeasuresStepTest.java
new file mode 100644
index 00000000000..9fe70a560cf
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportNewDuplicationMeasuresStepTest.java
@@ -0,0 +1,280 @@
+/*
+ * 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 javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.server.computation.task.projectanalysis.duplication.DuplicationRepositoryRule;
+import org.sonar.server.computation.task.projectanalysis.duplication.TextBlock;
+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 com.google.common.base.Preconditions.checkArgument;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.measures.CoreMetrics.NEW_LINES_DUPLICATED;
+import static org.sonar.api.measures.CoreMetrics.NEW_LINES_DUPLICATED_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;
+import static org.sonar.server.computation.task.projectanalysis.measure.Measure.newMeasureBuilder;
+import static org.sonar.server.computation.task.projectanalysis.measure.MeasureRepoEntry.entryOf;
+import static org.sonar.server.computation.task.projectanalysis.measure.MeasureRepoEntry.toEntries;
+import static org.sonar.server.computation.task.projectanalysis.measure.MeasureVariations.newMeasureVariationsBuilder;
+
+public class ReportNewDuplicationMeasuresStepTest {
+ 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 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;
+ private static final String SOME_FILE_KEY = "some file key";
+
+ @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())
+ .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();
+ @Rule
+ public ScmInfoRepositoryRule scmInfoRepository = new ScmInfoRepositoryRule();
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(NEW_LINES_DUPLICATED);
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+ @Rule
+ public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);
+
+ @Before
+ public void setUp() {
+ periodsHolder.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"));
+ }
+
+ NewDuplicationMeasuresStep underTest = new NewDuplicationMeasuresStep(treeRootHolder, periodsHolder, metricRepository, measureRepository, scmInfoRepository,
+ duplicationRepository);
+
+ @Test
+ public void compute_duplicated_lines_counts_lines_from_original_and_InnerDuplicate_of_a_single_line() {
+ TextBlock original = new TextBlock(1, 1);
+ duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(2, 2));
+ setChangesets(FILE_1_REF);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, 2d);
+ }
+
+ @Test
+ public void compute_duplicated_lines_counts_lines_from_original_and_ignores_InProjectDuplicate() {
+ TextBlock original = new TextBlock(1, 1);
+ duplicationRepository.addDuplication(FILE_1_REF, original, FILE_2_REF, new TextBlock(2, 2));
+ setChangesets(FILE_1_REF);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, 1d);
+ }
+
+ @Test
+ public void compute_duplicated_lines_counts_lines_from_original_and_ignores_CrossProjectDuplicate() {
+ TextBlock original = new TextBlock(1, 1);
+ duplicationRepository.addDuplication(FILE_1_REF, original, SOME_FILE_KEY, new TextBlock(2, 2));
+ setChangesets(FILE_1_REF);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, 1d);
+ }
+
+ @Test
+ public void compute_duplicated_lines_counts_lines_from_original_and_InnerDuplicate() {
+ TextBlock original = new TextBlock(1, 5);
+ duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(10, 11));
+ setChangesets(FILE_1_REF);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, 6d);
+ }
+
+ @Test
+ public void compute_duplicated_lines_counts_lines_from_original_and_InnerDuplicate_only_once() {
+ TextBlock original = new TextBlock(1, 10);
+ duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(10, 11), new TextBlock(11, 12));
+ duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(2, 2), new TextBlock(4, 4));
+ setChangesets(FILE_1_REF);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, 11d);
+ }
+
+ @Test
+ public void compute_new_duplicated_lines_on_different_periods() {
+ TextBlock original = new TextBlock(1, 1);
+ duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(2, 2));
+ scmInfoRepository.setScmInfo(FILE_1_REF,
+ Changeset.newChangesetBuilder().setDate(parseDate("2012-01-01").getTime()).setRevision("rev-1").build(),
+ Changeset.newChangesetBuilder().setDate(parseDate("2011-01-01").getTime()).setRevision("rev-2").build());
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, 2d, 1d);
+ }
+
+ @Test
+ public void compute_and_aggregate_duplicated_lines() {
+ addDuplicatedBlock(FILE_1_REF, 2);
+ addDuplicatedBlock(FILE_3_REF, 10);
+ addDuplicatedBlock(FILE_4_REF, 12);
+ setChangesets(FILE_1_REF);
+ setChangesets(FILE_2_REF);
+ setChangesets(FILE_3_REF);
+ setChangesets(FILE_4_REF);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, 2d);
+ assertRawMeasureValue(FILE_2_REF, 0d);
+ assertRawMeasureValue(FILE_3_REF, 9d);
+ assertRawMeasureValue(FILE_4_REF, 11d);
+ assertRawMeasureValue(DIRECTORY_REF, 2d);
+ assertRawMeasureValue(SUB_MODULE_1_REF, 2d);
+ assertRawMeasureValue(SUB_MODULE_2_REF, 20d);
+ assertRawMeasureValue(MODULE_REF, 22d);
+ assertRawMeasureValue(ROOT_REF, 22d);
+ }
+
+ @Test
+ public void compute_and_aggregate_zero_duplicated_line_when_no_duplication() {
+ setChangesets(FILE_1_REF);
+ setChangesets(FILE_2_REF);
+ setChangesets(FILE_3_REF);
+ setChangesets(FILE_4_REF);
+
+ underTest.execute();
+
+ assertComputedAndAggregatedToZeroInt();
+ }
+
+ /**
+ * Adds duplication blocks of a single line (each line is specific to its block).
+ *
+ * This is a very simple use case, convenient for unit tests but more realistic and complex use cases must be tested separately.
+ */
+ private void addDuplicatedBlock(int fileRef, int blockCount) {
+ checkArgument(blockCount > 1, "BlockCount can not be less than 2");
+ TextBlock original = new TextBlock(1, 1);
+ TextBlock[] duplicates = new TextBlock[blockCount - 1];
+ for (int i = 2; i < blockCount + 1; i++) {
+ duplicates[i - 2] = new TextBlock(i, i);
+ }
+ duplicationRepository.addDuplication(fileRef, original, duplicates);
+ }
+
+ private void setChangesets(int 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());
+ }
+
+ private void assertRawMeasureValue(int componentRef, double period2Value) {
+ assertRawMeasureValue(componentRef, period2Value, 0d);
+ }
+
+ private void assertRawMeasureValue(int componentRef, double period2Value, double period5Value) {
+ assertThat(toEntries(measureRepository.getAddedRawMeasures(componentRef))).containsOnlyOnce(
+ entryOf(NEW_LINES_DUPLICATED_KEY, createMeasure(period2Value, period5Value)));
+ }
+
+ private void assertComputedAndAggregatedToZeroInt() {
+ assertRawMeasureValue(FILE_1_REF, 0);
+ assertRawMeasureValue(FILE_2_REF, 0);
+ assertRawMeasureValue(FILE_3_REF, 0);
+ assertRawMeasureValue(FILE_4_REF, 0);
+ assertRawMeasureValue(DIRECTORY_REF, 0);
+ assertRawMeasureValue(SUB_MODULE_1_REF, 0);
+ assertRawMeasureValue(SUB_MODULE_2_REF, 0);
+ assertRawMeasureValue(MODULE_REF, 0);
+ assertRawMeasureValue(ROOT_REF, 0);
+ }
+
+ private static Measure createMeasure(@Nullable Double variationPeriod2, @Nullable Double variationPeriod5) {
+ MeasureVariations.Builder variationBuilder = newMeasureVariationsBuilder();
+ if (variationPeriod2 != null) {
+ variationBuilder.setVariation(new Period(2, "", null, 1L, "u2"), variationPeriod2);
+ }
+ if (variationPeriod5 != null) {
+ variationBuilder.setVariation(new Period(5, "", null, 1L, "u2"), variationPeriod5);
+ }
+ return newMeasureBuilder()
+ .setVariations(variationBuilder.build())
+ .createNoValue();
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsDuplicationMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsDuplicationMeasuresStepTest.java
index 88356d549a0..ea9acec66c1 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsDuplicationMeasuresStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsDuplicationMeasuresStepTest.java
@@ -365,10 +365,6 @@ public class ViewsDuplicationMeasuresStepTest {
assertThat(measureRepository.getAddedRawMeasures(PROJECT_VIEW_3_REF)).isEmpty();
}
- private void assertNoRawMeasure(int componentRef, String metricKey) {
- assertThat(measureRepository.getAddedRawMeasure(componentRef, metricKey)).isAbsent();
- }
-
private void assertRawMeasureValue(int componentRef, String metricKey, int value) {
assertThat(measureRepository.getAddedRawMeasure(componentRef, metricKey).get().getIntValue()).isEqualTo(value);
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsNewDuplicationMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsNewDuplicationMeasuresStepTest.java
new file mode 100644
index 00000000000..d14f427ff25
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ViewsNewDuplicationMeasuresStepTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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 javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+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.step.ComputationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
+import static org.sonar.api.measures.CoreMetrics.NEW_LINES_DUPLICATED;
+import static org.sonar.api.measures.CoreMetrics.NEW_LINES_DUPLICATED_KEY;
+import static org.sonar.api.utils.DateUtils.parseDate;
+import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT_VIEW;
+import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.SUBVIEW;
+import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.VIEW;
+import static org.sonar.server.computation.task.projectanalysis.component.ViewsComponent.builder;
+import static org.sonar.server.computation.task.projectanalysis.measure.Measure.newMeasureBuilder;
+import static org.sonar.server.computation.task.projectanalysis.measure.MeasureVariations.newMeasureVariationsBuilder;
+
+public class ViewsNewDuplicationMeasuresStepTest {
+
+ private static final int ROOT_REF = 1;
+ private static final int SUBVIEW_REF = 12;
+ private static final int SUB_SUBVIEW_REF = 123;
+ private static final int PROJECT_VIEW_1_REF = 1231;
+ private static final int PROJECT_VIEW_2_REF = 1232;
+ private static final int PROJECT_VIEW_3_REF = 13;
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule()
+ .setRoot(builder(VIEW, ROOT_REF)
+ .addChildren(
+ builder(SUBVIEW, SUBVIEW_REF)
+ .addChildren(
+ builder(SUBVIEW, SUB_SUBVIEW_REF)
+ .addChildren(
+ builder(PROJECT_VIEW, PROJECT_VIEW_1_REF).build(),
+ builder(PROJECT_VIEW, PROJECT_VIEW_2_REF).build())
+ .build())
+ .build(),
+ builder(PROJECT_VIEW, PROJECT_VIEW_3_REF).build())
+ .build());
+ @Rule
+ public PeriodsHolderRule periodsHolder = new PeriodsHolderRule();
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(NEW_LINES_DUPLICATED);
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+ ComputationStep underTest = new NewDuplicationMeasuresStep(treeRootHolder, periodsHolder, metricRepository, measureRepository);
+
+ @Before
+ public void setUp() {
+ periodsHolder.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"));
+ }
+
+ @Test
+ public void aggregate_duplicated_lines() {
+ addRawMeasure(PROJECT_VIEW_1_REF, NEW_LINES_DUPLICATED_KEY, 10);
+ addRawMeasure(PROJECT_VIEW_2_REF, NEW_LINES_DUPLICATED_KEY, 40);
+ addRawMeasure(PROJECT_VIEW_3_REF, NEW_LINES_DUPLICATED_KEY, 50);
+
+ underTest.execute();
+
+ assertNoNewRawMeasuresOnProjectViews();
+ assertRawMeasureValue(SUB_SUBVIEW_REF, NEW_LINES_DUPLICATED_KEY, 50);
+ assertRawMeasureValue(SUBVIEW_REF, NEW_LINES_DUPLICATED_KEY, 50);
+ assertRawMeasureValue(ROOT_REF, NEW_LINES_DUPLICATED_KEY, 100);
+ }
+
+ @Test
+ public void aggregate_zero_duplicated_line() {
+ addRawMeasure(PROJECT_VIEW_1_REF, NEW_LINES_DUPLICATED_KEY, 0);
+ addRawMeasure(PROJECT_VIEW_2_REF, NEW_LINES_DUPLICATED_KEY, 0);
+ // no raw measure for PROJECT_VIEW_3_REF
+
+ underTest.execute();
+
+ assertNoNewRawMeasuresOnProjectViews();
+ assertRawMeasureValue(SUB_SUBVIEW_REF, NEW_LINES_DUPLICATED_KEY, 0);
+ assertRawMeasureValue(SUBVIEW_REF, NEW_LINES_DUPLICATED_KEY, 0);
+ assertRawMeasureValue(ROOT_REF, NEW_LINES_DUPLICATED_KEY, 0);
+ }
+
+ @Test
+ public void aggregate_zero_duplicated_line_when_no_data() {
+ underTest.execute();
+
+ assertNoNewRawMeasuresOnProjectViews();
+ assertRawMeasureValue(SUB_SUBVIEW_REF, NEW_LINES_DUPLICATED_KEY, 0);
+ assertRawMeasureValue(SUBVIEW_REF, NEW_LINES_DUPLICATED_KEY, 0);
+ assertRawMeasureValue(ROOT_REF, NEW_LINES_DUPLICATED_KEY, 0);
+ }
+
+ private void addRawMeasure(int componentRef, String metricKey, double value) {
+ measureRepository.addRawMeasure(componentRef, metricKey, createMeasure(value, value));
+ }
+
+ private static Measure createMeasure(@Nullable Double variationPeriod2, @Nullable Double variationPeriod5) {
+ MeasureVariations.Builder variationBuilder = newMeasureVariationsBuilder();
+ if (variationPeriod2 != null) {
+ variationBuilder.setVariation(new Period(2, "", null, 1L, "U2"), variationPeriod2);
+ }
+ if (variationPeriod5 != null) {
+ variationBuilder.setVariation(new Period(5, "", null, 1L, "U2"), variationPeriod5);
+ }
+ return newMeasureBuilder()
+ .setVariations(variationBuilder.build())
+ .createNoValue();
+ }
+
+ private void assertNoNewRawMeasuresOnProjectViews() {
+ assertThat(measureRepository.getAddedRawMeasures(PROJECT_VIEW_1_REF)).isEmpty();
+ assertThat(measureRepository.getAddedRawMeasures(PROJECT_VIEW_2_REF)).isEmpty();
+ assertThat(measureRepository.getAddedRawMeasures(PROJECT_VIEW_3_REF)).isEmpty();
+ }
+
+ private void assertRawMeasureValue(int componentRef, String metricKey, int value) {
+ assertThat(measureRepository.getAddedRawMeasure(componentRef, metricKey).get().getVariations().getVariation2()).isEqualTo(value);
+ }
+
+}