@@ -21,17 +21,20 @@ | |||
package org.sonar.server.computation.task.projectanalysis.step; | |||
import com.google.common.base.Optional; | |||
import com.google.common.collect.HashMultiset; | |||
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.Multiset; | |||
import com.google.common.collect.SetMultimap; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.function.Function; | |||
import java.util.stream.Collectors; | |||
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; | |||
@@ -44,7 +47,6 @@ import org.sonar.server.computation.task.projectanalysis.formula.CounterInitiali | |||
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; | |||
@@ -56,38 +58,28 @@ 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_BLOCKS_DUPLICATED_KEY; | |||
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; | |||
private final NewDuplicationFormula duplicationFormula; | |||
public NewDuplicationMeasuresStep(TreeRootHolder treeRootHolder, PeriodsHolder periodsHolder, MetricRepository metricRepository, MeasureRepository measureRepository, | |||
ScmInfoRepository scmInfoRepository, @Nullable DuplicationRepository duplicationRepository) { | |||
ScmInfoRepository scmInfoRepository, 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); | |||
this.duplicationFormula = new NewDuplicationFormula(scmInfoRepository, duplicationRepository); | |||
} | |||
@Override | |||
@@ -100,17 +92,17 @@ public class NewDuplicationMeasuresStep implements ComputationStep { | |||
new PathAwareCrawler<>( | |||
FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository) | |||
.withVariationSupport(periodsHolder) | |||
.buildFor(formulas)) | |||
.visit(treeRootHolder.getRoot()); | |||
.buildFor(ImmutableList.of(duplicationFormula))) | |||
.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 final IntVariationValue.Array newBlocks = IntVariationValue.newArray(); | |||
private NewDuplicationCounter(@Nullable DuplicationRepository duplicationRepository, ScmInfoRepository scmInfoRepository) { | |||
private NewDuplicationCounter(DuplicationRepository duplicationRepository, ScmInfoRepository scmInfoRepository) { | |||
this.duplicationRepository = duplicationRepository; | |||
this.scmInfoRepository = scmInfoRepository; | |||
} | |||
@@ -118,53 +110,75 @@ public class NewDuplicationMeasuresStep implements ComputationStep { | |||
@Override | |||
public void aggregate(NewDuplicationCounter counter) { | |||
this.newLines.incrementAll(counter.newLines); | |||
this.newBlocks.incrementAll(counter.newBlocks); | |||
} | |||
@Override | |||
public void initialize(CounterInitializationContext context) { | |||
Component leaf = context.getLeaf(); | |||
Iterable<Duplication> duplications = requireNonNull(this.duplicationRepository, "DuplicationRepository missing").getDuplications(leaf); | |||
if (leaf.getType() != Component.Type.FILE) { | |||
context.getPeriods().forEach(period -> { | |||
newLines.increment(period, 0); | |||
newBlocks.increment(period, 0); | |||
}); | |||
return; | |||
} | |||
Iterable<Duplication> duplications = duplicationRepository.getDuplications(leaf); | |||
Optional<ScmInfo> scmInfo = scmInfoRepository.getScmInfo(leaf); | |||
if (!scmInfo.isPresent()) { | |||
return; | |||
} | |||
NewLinesDuplicatedAccumulator newLinesDuplicatedAccumulator = new NewLinesDuplicatedAccumulator(scmInfo.get(), context.getPeriods()); | |||
DuplicationCounters duplicationCounters = new DuplicationCounters(scmInfo.get(), context.getPeriods()); | |||
for (Duplication duplication : duplications) { | |||
newLinesDuplicatedAccumulator.addBlock(duplication.getOriginal()); | |||
duplicationCounters.addBlock(duplication.getOriginal()); | |||
duplication.getDuplicates().stream() | |||
.filter(InnerDuplicate.class::isInstance) | |||
.map(duplicate -> (InnerDuplicate) duplicate) | |||
.forEach(duplicate -> newLinesDuplicatedAccumulator.addBlock(duplicate.getTextBlock())); | |||
.forEach(duplicate -> duplicationCounters.addBlock(duplicate.getTextBlock())); | |||
} | |||
Map<Period, Integer> newLinesDuplicatedByPeriod = newLinesDuplicatedAccumulator.getNewLinesDuplicated(); | |||
context.getPeriods().forEach(period -> newLines.increment(period, newLinesDuplicatedByPeriod.getOrDefault(period, 0))); | |||
Map<Period, Integer> newLinesDuplicatedByPeriod = duplicationCounters.getNewLinesDuplicated(); | |||
context.getPeriods().forEach(period -> { | |||
newLines.increment(period, newLinesDuplicatedByPeriod.getOrDefault(period, 0)); | |||
newBlocks.increment(period, duplicationCounters.getNewBlocksDuplicated().getOrDefault(period, 0)); | |||
}); | |||
} | |||
} | |||
private static class NewLinesDuplicatedAccumulator { | |||
private static class DuplicationCounters { | |||
private final ScmInfo scmInfo; | |||
private final List<Period> periods; | |||
private final SetMultimap<Period, Integer> counts; | |||
private final SetMultimap<Period, Integer> lineCounts; | |||
private final Multiset<Period> blockCounts; | |||
private NewLinesDuplicatedAccumulator(ScmInfo scmInfo, List<Period> periods) { | |||
private DuplicationCounters(ScmInfo scmInfo, List<Period> periods) { | |||
this.scmInfo = scmInfo; | |||
this.periods = periods; | |||
this.counts = LinkedHashMultimap.create(periods.size(), Iterables.size(scmInfo.getAllChangesets())); | |||
this.lineCounts = LinkedHashMultimap.create(periods.size(), Iterables.size(scmInfo.getAllChangesets())); | |||
this.blockCounts = HashMultiset.create(); | |||
} | |||
void addBlock(TextBlock textBlock) { | |||
Set<Period> periodWithNewCode = new HashSet<>(); | |||
IntStream.rangeClosed(textBlock.getStart(), textBlock.getEnd()) | |||
.forEach(line -> periods.stream() | |||
.filter(period -> isLineInPeriod(line, period)) | |||
.forEach(period -> counts.put(period, line))); | |||
.forEach(period -> { | |||
lineCounts.put(period, line); | |||
periodWithNewCode.add(period); | |||
})); | |||
blockCounts.addAll(periodWithNewCode); | |||
} | |||
Map<Period, Integer> getNewLinesDuplicated() { | |||
return ImmutableMap.copyOf(counts.keySet().stream().collect(toMap(Function.identity(), period -> counts.get(period).size()))); | |||
return ImmutableMap.copyOf(lineCounts.keySet().stream().collect(toMap(Function.identity(), period -> lineCounts.get(period).size()))); | |||
} | |||
Map<Period, Integer> getNewBlocksDuplicated() { | |||
return blockCounts.entrySet().stream().collect(Collectors.toMap(Multiset.Entry::getElement, Multiset.Entry::getCount)); | |||
} | |||
private boolean isLineInPeriod(int lineNumber, Period period) { | |||
@@ -173,22 +187,14 @@ public class NewDuplicationMeasuresStep implements ComputationStep { | |||
} | |||
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) { | |||
private NewDuplicationFormula(ScmInfoRepository scmInfoRepository, 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); | |||
@@ -197,19 +203,25 @@ public class NewDuplicationMeasuresStep implements ComputationStep { | |||
@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(); | |||
switch (metricKey) { | |||
case NEW_LINES_DUPLICATED_KEY: | |||
Optional<MeasureVariations> newLinesDuplicated = counter.newLines.toMeasureVariations(); | |||
return newLinesDuplicated.isPresent() | |||
? Optional.of(Measure.newMeasureBuilder().setVariations(newLinesDuplicated.get()).createNoValue()) | |||
: Optional.absent(); | |||
case NEW_BLOCKS_DUPLICATED_KEY: | |||
Optional<MeasureVariations> newBlocksDuplicated = counter.newBlocks.toMeasureVariations(); | |||
return newBlocksDuplicated.isPresent() | |||
? Optional.of(Measure.newMeasureBuilder().setVariations(newBlocksDuplicated.get()).createNoValue()) | |||
: Optional.absent(); | |||
default: | |||
throw new IllegalArgumentException("Unsupported metric " + context.getMetric()); | |||
} | |||
throw new IllegalArgumentException("Unsupported metric " + context.getMetric()); | |||
} | |||
@Override | |||
public String[] getOutputMetricKeys() { | |||
return new String[] {NEW_LINES_DUPLICATED_KEY}; | |||
return new String[]{NEW_LINES_DUPLICATED_KEY, NEW_BLOCKS_DUPLICATED_KEY}; | |||
} | |||
} | |||
} |
@@ -20,14 +20,12 @@ | |||
package org.sonar.server.computation.task.projectanalysis.step; | |||
import javax.annotation.Nullable; | |||
import org.junit.Before; | |||
import java.util.Arrays; | |||
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; | |||
@@ -38,6 +36,8 @@ import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoRepositoryRu | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKS_DUPLICATED; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKS_DUPLICATED_KEY; | |||
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; | |||
@@ -46,10 +46,6 @@ import static org.sonar.server.computation.task.projectanalysis.component.Compon | |||
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; | |||
@@ -57,6 +53,7 @@ public class ReportNewDuplicationMeasuresStepTest { | |||
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; | |||
@@ -76,7 +73,8 @@ public class ReportNewDuplicationMeasuresStepTest { | |||
.addChildren( | |||
builder(FILE, FILE_1_REF).build(), | |||
builder(FILE, FILE_2_REF).build()) | |||
.build()) | |||
.build(), | |||
builder(DIRECTORY, DIRECTORY_2_REF).build()) | |||
.build(), | |||
builder(MODULE, SUB_MODULE_2_REF) | |||
.addChildren( | |||
@@ -86,36 +84,31 @@ public class ReportNewDuplicationMeasuresStepTest { | |||
.build()) | |||
.build()); | |||
@Rule | |||
public PeriodsHolderRule periodsHolder = new PeriodsHolderRule(); | |||
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_LINES_DUPLICATED); | |||
.add(NEW_LINES_DUPLICATED) | |||
.add(NEW_BLOCKS_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)); | |||
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 1), new TextBlock(2, 2)); | |||
setChangesets(FILE_1_REF); | |||
underTest.execute(); | |||
assertRawMeasureValue(FILE_1_REF, 2d); | |||
assertRawMeasureValue(FILE_1_REF, NEW_LINES_DUPLICATED_KEY, 2d); | |||
} | |||
@Test | |||
@@ -126,7 +119,7 @@ public class ReportNewDuplicationMeasuresStepTest { | |||
underTest.execute(); | |||
assertRawMeasureValue(FILE_1_REF, 1d); | |||
assertRawMeasureValue(FILE_1_REF, NEW_LINES_DUPLICATED_KEY, 1d); | |||
} | |||
@Test | |||
@@ -137,7 +130,7 @@ public class ReportNewDuplicationMeasuresStepTest { | |||
underTest.execute(); | |||
assertRawMeasureValue(FILE_1_REF, 1d); | |||
assertRawMeasureValue(FILE_1_REF, NEW_LINES_DUPLICATED_KEY, 1d); | |||
} | |||
@Test | |||
@@ -148,7 +141,7 @@ public class ReportNewDuplicationMeasuresStepTest { | |||
underTest.execute(); | |||
assertRawMeasureValue(FILE_1_REF, 6d); | |||
assertRawMeasureValue(FILE_1_REF, NEW_LINES_DUPLICATED_KEY, 6d); | |||
} | |||
@Test | |||
@@ -160,7 +153,7 @@ public class ReportNewDuplicationMeasuresStepTest { | |||
underTest.execute(); | |||
assertRawMeasureValue(FILE_1_REF, 11d); | |||
assertRawMeasureValue(FILE_1_REF, NEW_LINES_DUPLICATED_KEY, 11d); | |||
} | |||
@Test | |||
@@ -173,7 +166,7 @@ public class ReportNewDuplicationMeasuresStepTest { | |||
underTest.execute(); | |||
assertRawMeasureValue(FILE_1_REF, 2d, 1d); | |||
assertRawMeasureValue(FILE_1_REF, NEW_LINES_DUPLICATED_KEY, 2d, 1d); | |||
} | |||
@Test | |||
@@ -188,15 +181,16 @@ public class ReportNewDuplicationMeasuresStepTest { | |||
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); | |||
assertRawMeasureValue(FILE_1_REF, NEW_LINES_DUPLICATED_KEY, 2d); | |||
assertRawMeasureValue(FILE_2_REF, NEW_LINES_DUPLICATED_KEY, 0d); | |||
assertRawMeasureValue(FILE_3_REF, NEW_LINES_DUPLICATED_KEY, 9d); | |||
assertRawMeasureValue(FILE_4_REF, NEW_LINES_DUPLICATED_KEY, 11d); | |||
assertRawMeasureValue(DIRECTORY_REF, NEW_LINES_DUPLICATED_KEY, 2d); | |||
assertRawMeasureValue(DIRECTORY_2_REF, NEW_LINES_DUPLICATED_KEY, 0d); | |||
assertRawMeasureValue(SUB_MODULE_1_REF, NEW_LINES_DUPLICATED_KEY, 2d); | |||
assertRawMeasureValue(SUB_MODULE_2_REF, NEW_LINES_DUPLICATED_KEY, 20d); | |||
assertRawMeasureValue(MODULE_REF, NEW_LINES_DUPLICATED_KEY, 22d); | |||
assertRawMeasureValue(ROOT_REF, NEW_LINES_DUPLICATED_KEY, 22d); | |||
} | |||
@Test | |||
@@ -208,7 +202,78 @@ public class ReportNewDuplicationMeasuresStepTest { | |||
underTest.execute(); | |||
assertComputedAndAggregatedToZeroInt(); | |||
assertComputedAndAggregatedToZeroInt(NEW_LINES_DUPLICATED_KEY); | |||
} | |||
@Test | |||
public void compute_duplicated_blocks_one_for_original_one_for_each_InnerDuplicate() { | |||
TextBlock original = new TextBlock(1, 1); | |||
duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(2, 2), new TextBlock(4, 4), new TextBlock(3, 4)); | |||
setChangesets(FILE_1_REF); | |||
underTest.execute(); | |||
assertRawMeasureValue(FILE_1_REF, NEW_BLOCKS_DUPLICATED_KEY, 4); | |||
} | |||
@Test | |||
public void compute_duplicated_blocks_does_not_count_blocks_only_once_it_assumes_consistency_from_duplication_data() { | |||
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 1), new TextBlock(4, 4)); | |||
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(2, 2), new TextBlock(4, 4)); | |||
setChangesets(FILE_1_REF); | |||
underTest.execute(); | |||
assertRawMeasureValue(FILE_1_REF, NEW_BLOCKS_DUPLICATED_KEY, 4); | |||
} | |||
@Test | |||
public void compute_duplicated_blocks_one_for_original_and_ignores_InProjectDuplicate() { | |||
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 1), FILE_2_REF, new TextBlock(2, 2)); | |||
setChangesets(FILE_1_REF); | |||
underTest.execute(); | |||
assertRawMeasureValue(FILE_1_REF, NEW_BLOCKS_DUPLICATED_KEY, 1); | |||
} | |||
@Test | |||
public void compute_duplicated_blocks_one_for_original_and_ignores_CrossProjectDuplicate() { | |||
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 1), SOME_FILE_KEY, new TextBlock(2, 2)); | |||
setChangesets(FILE_1_REF); | |||
underTest.execute(); | |||
assertRawMeasureValue(FILE_1_REF, NEW_BLOCKS_DUPLICATED_KEY, 1); | |||
} | |||
@Test | |||
public void compute_and_aggregate_duplicated_blocks_from_single_duplication() { | |||
addDuplicatedBlock(FILE_1_REF, 11); | |||
addDuplicatedBlock(FILE_2_REF, 2); | |||
addDuplicatedBlock(FILE_4_REF, 7); | |||
setChangesets(FILE_1_REF, FILE_2_REF, FILE_3_REF, FILE_4_REF); | |||
underTest.execute(); | |||
assertRawMeasureValue(FILE_1_REF, NEW_BLOCKS_DUPLICATED_KEY, 10); | |||
assertRawMeasureValue(FILE_2_REF, NEW_BLOCKS_DUPLICATED_KEY, 2); | |||
assertRawMeasureValue(FILE_3_REF, NEW_BLOCKS_DUPLICATED_KEY, 0); | |||
assertRawMeasureValue(FILE_4_REF, NEW_BLOCKS_DUPLICATED_KEY, 6); | |||
assertRawMeasureValue(DIRECTORY_REF, NEW_BLOCKS_DUPLICATED_KEY, 12); | |||
assertRawMeasureValue(SUB_MODULE_1_REF, NEW_BLOCKS_DUPLICATED_KEY, 12); | |||
assertRawMeasureValue(SUB_MODULE_2_REF, NEW_BLOCKS_DUPLICATED_KEY, 6); | |||
assertRawMeasureValue(MODULE_REF, NEW_BLOCKS_DUPLICATED_KEY, 18); | |||
assertRawMeasureValue(ROOT_REF, NEW_BLOCKS_DUPLICATED_KEY, 18); | |||
} | |||
@Test | |||
public void compute_and_aggregate_duplicated_blocks_to_zero_when_no_duplication() { | |||
setChangesets(FILE_1_REF, FILE_2_REF, FILE_3_REF, FILE_4_REF); | |||
underTest.execute(); | |||
assertComputedAndAggregatedToZeroInt(NEW_BLOCKS_DUPLICATED_KEY); | |||
} | |||
/** | |||
@@ -226,55 +291,43 @@ public class ReportNewDuplicationMeasuresStepTest { | |||
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 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())); | |||
} | |||
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 assertRawMeasureValue(int componentRef, String metricKey, double period2Value) { | |||
assertRawMeasureValue(componentRef, metricKey, period2Value, 0d); | |||
} | |||
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 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 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 assertComputedAndAggregatedToZeroInt(String metricKey) { | |||
assertRawMeasureValue(FILE_1_REF, metricKey, 0); | |||
assertRawMeasureValue(FILE_2_REF, metricKey, 0); | |||
assertRawMeasureValue(FILE_3_REF, metricKey, 0); | |||
assertRawMeasureValue(FILE_4_REF, metricKey, 0); | |||
assertRawMeasureValue(DIRECTORY_REF, metricKey, 0); | |||
assertRawMeasureValue(SUB_MODULE_1_REF, metricKey, 0); | |||
assertRawMeasureValue(SUB_MODULE_2_REF, metricKey, 0); | |||
assertRawMeasureValue(MODULE_REF, metricKey, 0); | |||
assertRawMeasureValue(ROOT_REF, metricKey, 0); | |||
} | |||
} |
@@ -1,153 +0,0 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -2472,6 +2472,8 @@ metric.new_coverage.description=Coverage of new/changed code | |||
metric.new_coverage.name=Coverage on New Code | |||
metric.new_critical_violations.description=New Critical issues | |||
metric.new_critical_violations.name=New Critical Issues | |||
metric.new_duplicated_blocks.name=Duplicated Blocks on New Code | |||
metric.new_duplicated_blocks.description=Duplicated blocks on new code | |||
metric.new_duplicated_lines.name=New Duplicated Lines | |||
metric.new_duplicated_lines.description=New duplicated lines | |||
metric.new_info_violations.description=New Info issues |
@@ -1334,6 +1334,16 @@ public final class CoreMetrics { | |||
.setOptimizedBestValue(true) | |||
.create(); | |||
public static final String NEW_BLOCKS_DUPLICATED_KEY = "new_duplicated_blocks"; | |||
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) | |||
.setQualitative(true) | |||
.setDomain(DOMAIN_DUPLICATIONS) | |||
.setBestValue(0.0) | |||
.setDeleteHistoricalData(true) | |||
.create(); | |||
public static final String DUPLICATED_FILES_KEY = "duplicated_files"; | |||
/** |