diff options
author | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2016-08-10 18:09:58 +0200 |
---|---|---|
committer | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2016-08-12 15:56:36 +0200 |
commit | de2eaf079f1933eb9dcdb511eeae7207ac4d10c8 (patch) | |
tree | 3040ef5e1697bc31b4c79738b404c9964867a45d /server | |
parent | 531135e491d9cdd11d5b687881945cf88f3ddaae (diff) | |
download | sonarqube-de2eaf079f1933eb9dcdb511eeae7207ac4d10c8.tar.gz sonarqube-de2eaf079f1933eb9dcdb511eeae7207ac4d10c8.zip |
SONAR-7951 New metric « Duplicated lines on new code »
Diffstat (limited to 'server')
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); + } + +} |