import org.sonar.ce.task.projectanalysis.formula.FormulaExecutorComponentVisitor;
import org.sonar.ce.task.projectanalysis.measure.Measure;
import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
-import org.sonar.ce.task.projectanalysis.metric.Metric;
import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Iterables.isEmpty;
import static java.util.Objects.requireNonNull;
-import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_KEY;
import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS_KEY;
import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES_KEY;
import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY_KEY;
import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_KEY;
import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
-import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
public class DuplicationMeasures {
protected final ImmutableList<Formula> formulas;
this.measureRepository = measureRepository;
// will be null for views
this.duplicationRepository = duplicationRepository;
- this.formulas = ImmutableList.of(new DuplicationFormula(metricRepository, measureRepository));
+ this.formulas = ImmutableList.of(new DuplicationFormula());
}
/**
public void execute() {
new PathAwareCrawler<>(
FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository).buildFor(formulas))
- .visit(treeRootHolder.getRoot());
+ .visit(treeRootHolder.getReportTreeRoot());
}
protected DuplicationCounter createCounter() {
private final DuplicationRepository duplicationRepository;
protected int fileCount = 0;
protected int blockCount = 0;
+ protected int dupLineCount = 0;
protected int lineCount = 0;
protected DuplicationCounter() {
public void aggregate(DuplicationCounter counter) {
this.fileCount += counter.fileCount;
this.blockCount += counter.blockCount;
+ this.dupLineCount += counter.dupLineCount;
this.lineCount += counter.lineCount;
}
}
protected void initializeForFile(Component file) {
+ // don't use measure since it won't be available for some files in the report tree in SLB
+ this.lineCount = file.getFileAttributes().getLines();
Iterable<Duplication> duplications = requireNonNull(this.duplicationRepository, "DuplicationRepository missing")
.getDuplications(file);
if (isEmpty(duplications)) {
this.fileCount += 1;
this.blockCount += blocks;
- this.lineCount += duplicatedLineNumbers.size();
+ this.dupLineCount += duplicatedLineNumbers.size();
+
}
private static void addLines(TextBlock textBlock, Set<Integer> duplicatedLineNumbers) {
private void initializeForProjectView(CounterInitializationContext context) {
fileCount += getMeasure(context, DUPLICATED_FILES_KEY);
blockCount += getMeasure(context, DUPLICATED_BLOCKS_KEY);
- lineCount += getMeasure(context, DUPLICATED_LINES_KEY);
+ dupLineCount += getMeasure(context, DUPLICATED_LINES_KEY);
+ lineCount += getMeasure(context, LINES_KEY);
}
private static int getMeasure(CounterInitializationContext context, String metricKey) {
}
private final class DuplicationFormula implements Formula<DuplicationCounter> {
- private final MeasureRepository measureRepository;
- private final Metric nclocMetric;
- private final Metric linesMetric;
- private final Metric commentLinesMetric;
-
- private DuplicationFormula(MetricRepository metricRepository, MeasureRepository measureRepository) {
- this.measureRepository = measureRepository;
- this.nclocMetric = metricRepository.getByKey(NCLOC_KEY);
- this.linesMetric = metricRepository.getByKey(LINES_KEY);
- this.commentLinesMetric = metricRepository.getByKey(COMMENT_LINES_KEY);
- }
-
@Override
public DuplicationCounter createNewCounter() {
return createCounter();
case DUPLICATED_FILES_KEY:
return Optional.of(Measure.newMeasureBuilder().create(counter.fileCount));
case DUPLICATED_LINES_KEY:
- return Optional.of(Measure.newMeasureBuilder().create(counter.lineCount));
+ return Optional.of(Measure.newMeasureBuilder().create(counter.dupLineCount));
case DUPLICATED_LINES_DENSITY_KEY:
return createDuplicatedLinesDensityMeasure(counter, context);
case DUPLICATED_BLOCKS_KEY:
}
private Optional<Measure> createDuplicatedLinesDensityMeasure(DuplicationCounter counter, CreateMeasureContext context) {
- int duplicatedLines = counter.lineCount;
- java.util.Optional<Integer> nbLines = getNbLinesFromLocOrNcloc(context);
- if (nbLines.isPresent() && nbLines.get() > 0) {
- double density = Math.min(100d, 100d * duplicatedLines / nbLines.get());
+ int duplicatedLines = counter.dupLineCount;
+ int nbLines = counter.lineCount;
+ if (nbLines > 0) {
+ double density = Math.min(100d, 100d * duplicatedLines / nbLines);
return Optional.of(Measure.newMeasureBuilder().create(density, context.getMetric().getDecimalScale()));
}
return Optional.absent();
}
- private java.util.Optional<Integer> getNbLinesFromLocOrNcloc(CreateMeasureContext context) {
- Optional<Measure> lines = measureRepository.getRawMeasure(context.getComponent(), linesMetric);
- if (lines.isPresent()) {
- return java.util.Optional.of(lines.get().getIntValue());
- }
- Optional<Measure> nclocs = measureRepository.getRawMeasure(context.getComponent(), nclocMetric);
- if (nclocs.isPresent()) {
- Optional<Measure> commentLines = measureRepository.getRawMeasure(context.getComponent(), commentLinesMetric);
- int nbLines = nclocs.get().getIntValue();
- return java.util.Optional.of(commentLines.isPresent() ? (nbLines + commentLines.get().getIntValue()) : nbLines);
- }
- return java.util.Optional.empty();
- }
-
@Override
public String[] getOutputMetricKeys() {
return new String[] {DUPLICATED_FILES_KEY, DUPLICATED_LINES_KEY, DUPLICATED_LINES_DENSITY_KEY, DUPLICATED_BLOCKS_KEY};
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info 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.ce.task.projectanalysis.duplication;
+
+import org.sonar.ce.task.projectanalysis.component.Component;
+
+public class InExtendedProjectDuplicate extends InProjectDuplicate {
+ public InExtendedProjectDuplicate(Component file, TextBlock textBlock) {
+ super(file, textBlock);
+ }
+
+ @Override
+ public String toString() {
+ return "InExtendedProjectDuplicate{" +
+ "file=" + file +
+ ", textBlock=" + getTextBlock() +
+ '}';
+ }
+}
@Immutable
public class InProjectDuplicate extends AbstractDuplicate {
- private final Component file;
+ protected final Component file;
public InProjectDuplicate(Component file, TextBlock textBlock) {
super(textBlock);
*/
package org.sonar.ce.task.projectanalysis.source.linereader;
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Ordering;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.sonar.ce.task.projectanalysis.duplication.Duplicate;
import org.sonar.ce.task.projectanalysis.duplication.Duplication;
import org.sonar.ce.task.projectanalysis.duplication.InnerDuplicate;
import org.sonar.ce.task.projectanalysis.duplication.TextBlock;
+import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.protobuf.DbFileSources;
import static com.google.common.collect.FluentIterable.from;
@Override
public Optional<ReadError> read(DbFileSources.Line.Builder lineBuilder) {
Predicate<Map.Entry<TextBlock, Integer>> containsLine = new TextBlockContainsLine(lineBuilder.getLine());
- for (Integer textBlockIndex : from(duplicatedTextBlockIndexByTextBlock.entrySet())
+ // list is sorted to cope with the non-guaranteed order of Map entries which would trigger false detection of changes
+ // in {@link DbFileSources.Line#getDuplicationList()}
+ duplicatedTextBlockIndexByTextBlock.entrySet().stream()
.filter(containsLine)
- .transform(MapEntryToBlockId.INSTANCE)
- // list is sorted to cope with the non-guaranteed order of Map entries which would trigger false detection of changes
- // in {@link DbFileSources.Line#getDuplicationList()}
- .toSortedList(Ordering.natural())) {
- lineBuilder.addDuplication(textBlockIndex);
- }
+ .map(MapEntryToBlockId.INSTANCE)
+ .sorted(Comparator.naturalOrder())
+ .forEach(lineBuilder::addDuplication);
+
return Optional.empty();
}
/**
- *
* <p>
* This method uses the natural order of TextBlocks to ensure that given the same set of TextBlocks, they get the same
* index. It avoids false detections of changes in {@link DbFileSources.Line#getDuplicationList()}.
* </p>
*/
private static Map<TextBlock, Integer> createIndexOfDuplicatedTextBlocks(Iterable<Duplication> duplications) {
- List<TextBlock> duplicatedTextBlocks = extractAllDuplicatedTextBlocks(duplications);
- Collections.sort(duplicatedTextBlocks);
- return from(duplicatedTextBlocks)
- .toMap(new TextBlockIndexGenerator());
+ return extractAllDuplicatedTextBlocks(duplications)
+ .stream().sorted()
+ .collect(Collectors.toMap(e -> e, new TextBlockIndexGenerator(), MoreCollectors.mergeNotSupportedMerger(), LinkedHashMap::new));
}
/**
}
@Override
- public boolean apply(@Nonnull Map.Entry<TextBlock, Integer> input) {
+ public boolean test(@Nonnull Map.Entry<TextBlock, Integer> input) {
return isLineInBlock(input.getKey(), line);
}
import org.sonar.ce.task.projectanalysis.duplication.Duplicate;
import org.sonar.ce.task.projectanalysis.duplication.Duplication;
import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepository;
+import org.sonar.ce.task.projectanalysis.duplication.InExtendedProjectDuplicate;
import org.sonar.ce.task.projectanalysis.duplication.InProjectDuplicate;
import org.sonar.ce.task.projectanalysis.duplication.InnerDuplicate;
import org.sonar.ce.task.projectanalysis.duplication.TextBlock;
Measure.newMeasureBuilder().create(duplicationXml));
}
- private String createXmlDuplications(String componentKey, Iterable<Duplication> duplications) {
+ private String createXmlDuplications(String componentDbKey, Iterable<Duplication> duplications) {
StringBuilder xml = new StringBuilder();
xml.append("<duplications>");
for (Duplication duplication : duplications) {
xml.append("<g>");
- appendDuplication(xml, componentKey, duplication.getOriginal());
+ appendDuplication(xml, componentDbKey, duplication.getOriginal(), false);
for (Duplicate duplicate : duplication.getDuplicates()) {
- processDuplicationBlock(xml, duplicate, componentKey);
+ processDuplicationBlock(xml, duplicate, componentDbKey);
}
xml.append("</g>");
}
return xml.toString();
}
- private void processDuplicationBlock(StringBuilder xml, Duplicate duplicate, String componentKey) {
+ private void processDuplicationBlock(StringBuilder xml, Duplicate duplicate, String componentDbKey) {
if (duplicate instanceof InnerDuplicate) {
- // Duplication is on a the same file
- appendDuplication(xml, componentKey, duplicate);
+ // Duplication is on the same file
+ appendDuplication(xml, componentDbKey, duplicate);
+ } else if (duplicate instanceof InExtendedProjectDuplicate) {
+ // Duplication is on a different file that is not saved in the DB
+ appendDuplication(xml, ((InExtendedProjectDuplicate) duplicate).getFile().getDbKey(), duplicate.getTextBlock(), true);
} else if (duplicate instanceof InProjectDuplicate) {
// Duplication is on a different file
appendDuplication(xml, ((InProjectDuplicate) duplicate).getFile().getDbKey(), duplicate);
} else if (duplicate instanceof CrossProjectDuplicate) {
- // componentKey is only set for cross project duplications
+ // Only componentKey is set for cross project duplications
String crossProjectComponentKey = ((CrossProjectDuplicate) duplicate).getFileKey();
appendDuplication(xml, crossProjectComponentKey, duplicate);
} else {
}
}
- private void appendDuplication(StringBuilder xml, String componentKey, Duplicate duplicate) {
- appendDuplication(xml, componentKey, duplicate.getTextBlock());
+ private void appendDuplication(StringBuilder xml, String componentDbKey, Duplicate duplicate) {
+ appendDuplication(xml, componentDbKey, duplicate.getTextBlock(), false);
}
- private void appendDuplication(StringBuilder xml, String componentKey, TextBlock textBlock) {
+ private void appendDuplication(StringBuilder xml, String componentDbKey, TextBlock textBlock, boolean onlyText) {
int length = textBlock.getEnd() - textBlock.getStart() + 1;
xml.append("<b s=\"").append(textBlock.getStart())
.append("\" l=\"").append(length)
- .append("\" r=\"").append(StringEscapeUtils.escapeXml(componentKey))
+ .append("\" t=\"").append(onlyText)
+ .append("\" r=\"").append(StringEscapeUtils.escapeXml(componentDbKey))
.append("\"/>");
}
}
import com.google.common.base.Function;
import javax.annotation.Nonnull;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit;
import org.sonar.ce.task.projectanalysis.duplication.Duplicate;
import org.sonar.ce.task.projectanalysis.duplication.Duplication;
import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepository;
+import org.sonar.ce.task.projectanalysis.duplication.InExtendedProjectDuplicate;
import org.sonar.ce.task.projectanalysis.duplication.InProjectDuplicate;
import org.sonar.ce.task.projectanalysis.duplication.InnerDuplicate;
import org.sonar.ce.task.projectanalysis.duplication.TextBlock;
*/
public class LoadDuplicationsFromReportStep implements ComputationStep {
private final TreeRootHolder treeRootHolder;
+ private final AnalysisMetadataHolder analysisMetadataHolder;
private final BatchReportReader batchReportReader;
private final DuplicationRepository duplicationRepository;
- public LoadDuplicationsFromReportStep(TreeRootHolder treeRootHolder, BatchReportReader batchReportReader, DuplicationRepository duplicationRepository) {
+ public LoadDuplicationsFromReportStep(TreeRootHolder treeRootHolder, AnalysisMetadataHolder analysisMetadataHolder, BatchReportReader batchReportReader,
+ DuplicationRepository duplicationRepository) {
this.treeRootHolder = treeRootHolder;
+ this.analysisMetadataHolder = analysisMetadataHolder;
this.batchReportReader = batchReportReader;
this.duplicationRepository = duplicationRepository;
}
if (input.getOtherFileRef() != 0) {
checkArgument(input.getOtherFileRef() != file.getReportAttributes().getRef(), "file and otherFile references can not be the same");
Component otherComponent = treeRootHolder.getReportTreeComponentByRef(input.getOtherFileRef());
- return new InProjectDuplicate(otherComponent, convert(input.getRange()));
+ if ((analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) && otherComponent.getStatus() == Component.Status.SAME) {
+ return new InExtendedProjectDuplicate(otherComponent, convert(input.getRange()));
+ } else {
+ return new InProjectDuplicate(otherComponent, convert(input.getRange()));
+ }
}
return new InnerDuplicate(convert(input.getRange()));
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info 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.ce.task.projectanalysis.duplication;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.ce.task.projectanalysis.component.FileAttributes;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
+import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS_KEY;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES_KEY;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY_KEY;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_KEY;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.MODULE;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
+import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
+
+public class DuplicationMeasuresTest {
+ 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 FileAttributes FILE_1_ATTRS = mock(FileAttributes.class);
+ private static final FileAttributes FILE_2_ATTRS = mock(FileAttributes.class);
+ private static final FileAttributes FILE_3_ATTRS = mock(FileAttributes.class);
+ private static final FileAttributes FILE_4_ATTRS = mock(FileAttributes.class);
+
+ 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).setFileAttributes(FILE_1_ATTRS).build(),
+ builder(FILE, FILE_2_REF).setFileAttributes(FILE_2_ATTRS).build())
+ .build())
+ .build(),
+ builder(MODULE, SUB_MODULE_2_REF)
+ .addChildren(
+ builder(FILE, FILE_3_REF).setFileAttributes(FILE_3_ATTRS).build(),
+ builder(FILE, FILE_4_REF).setFileAttributes(FILE_4_ATTRS).build())
+ .build())
+ .build())
+ .build());
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(DUPLICATED_BLOCKS)
+ .add(DUPLICATED_FILES)
+ .add(DUPLICATED_LINES)
+ .add(DUPLICATED_LINES_DENSITY);
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+ @Rule
+ public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);
+
+ private DuplicationMeasures underTest = new DuplicationMeasures(treeRootHolder, metricRepository, measureRepository, duplicationRepository);
+
+ @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(3, 3), new TextBlock(2, 3));
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_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(3, 3));
+ duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(2, 2), new TextBlock(3, 3));
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_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));
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_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));
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 1);
+ }
+
+ @Test
+ public void compute_and_aggregate_duplicated_blocks_from_single_duplication() {
+ addDuplicatedBlock(FILE_1_REF, 10);
+ addDuplicatedBlock(FILE_2_REF, 40);
+ addDuplicatedBlock(FILE_4_REF, 5);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 10);
+ assertRawMeasureValue(FILE_2_REF, DUPLICATED_BLOCKS_KEY, 40);
+ assertRawMeasureValue(FILE_3_REF, DUPLICATED_BLOCKS_KEY, 0);
+ assertRawMeasureValue(FILE_4_REF, DUPLICATED_BLOCKS_KEY, 5);
+ assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_BLOCKS_KEY, 50);
+ assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_BLOCKS_KEY, 50);
+ assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_BLOCKS_KEY, 5);
+ assertRawMeasureValue(MODULE_REF, DUPLICATED_BLOCKS_KEY, 55);
+ assertRawMeasureValue(ROOT_REF, DUPLICATED_BLOCKS_KEY, 55);
+ }
+
+ @Test
+ public void compute_and_aggregate_duplicated_blocks_to_zero_when_no_duplication() {
+ underTest.execute();
+
+ assertComputedAndAggregatedToZeroInt(DUPLICATED_BLOCKS_KEY);
+ }
+
+ @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));
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 2);
+ }
+
+ @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));
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 1);
+ }
+
+ @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));
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 1);
+ }
+
+ @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));
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 7);
+ }
+
+ @Test
+ public void compute_duplicated_lines_counts_lines_from_original_and_InnerDuplicate_only_once() {
+ TextBlock original = new TextBlock(1, 12);
+ duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(10, 11), new TextBlock(11, 15));
+ duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(2, 2), new TextBlock(96, 96));
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 16);
+ }
+
+ @Test
+ public void compute_and_aggregate_duplicated_files() {
+ addDuplicatedBlock(FILE_1_REF, 2);
+ addDuplicatedBlock(FILE_3_REF, 10);
+ addDuplicatedBlock(FILE_4_REF, 50);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_FILES_KEY, 1);
+ assertRawMeasureValue(FILE_2_REF, DUPLICATED_FILES_KEY, 0);
+ assertRawMeasureValue(FILE_3_REF, DUPLICATED_FILES_KEY, 1);
+ assertRawMeasureValue(FILE_4_REF, DUPLICATED_FILES_KEY, 1);
+ assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_FILES_KEY, 1);
+ assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_FILES_KEY, 1);
+ assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_FILES_KEY, 2);
+ assertRawMeasureValue(MODULE_REF, DUPLICATED_FILES_KEY, 3);
+ assertRawMeasureValue(ROOT_REF, DUPLICATED_FILES_KEY, 3);
+ }
+
+ @Test
+ public void compute_and_aggregate_zero_duplicated_files_when_no_duplication_data() {
+ underTest.execute();
+
+ assertComputedAndAggregatedToZeroInt(DUPLICATED_FILES_KEY);
+ }
+
+ @Test
+ public void compute_and_aggregate_duplicated_lines() {
+ addDuplicatedBlock(FILE_1_REF, 10);
+ addDuplicatedBlock(FILE_2_REF, 9);
+ addDuplicatedBlock(FILE_4_REF, 7);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 10);
+ assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_KEY, 9);
+ assertRawMeasureValue(FILE_3_REF, DUPLICATED_LINES_KEY, 0);
+ assertRawMeasureValue(FILE_4_REF, DUPLICATED_LINES_KEY, 7);
+ assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_KEY, 19);
+ assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_KEY, 19);
+ assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_LINES_KEY, 7);
+ assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_KEY, 26);
+ assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_KEY, 26);
+ }
+
+ @Test
+ public void compute_and_aggregate_zero_duplicated_line_when_no_duplication() {
+ underTest.execute();
+
+ assertComputedAndAggregatedToZeroInt(DUPLICATED_LINES_KEY);
+ }
+
+ @Test
+ public void compute_and_aggregate_duplicated_lines_density_using_lines() {
+ addDuplicatedBlock(FILE_1_REF, 2);
+ addDuplicatedBlock(FILE_2_REF, 3);
+
+ when(FILE_1_ATTRS.getLines()).thenReturn(10);
+ when(FILE_2_ATTRS.getLines()).thenReturn(40);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 20d);
+ assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 7.5d);
+ assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
+ assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
+ assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
+ assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
+ assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
+ assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
+ assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
+ }
+
+ @Test
+ public void compute_zero_percent_duplicated_lines_density_when_there_is_no_duplication() {
+ when(FILE_1_ATTRS.getLines()).thenReturn(10);
+ when(FILE_2_ATTRS.getLines()).thenReturn(40);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+ assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+ assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
+ assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
+ assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+ assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+ assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
+ assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+ assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+ }
+
+ @Test
+ public void not_compute_duplicated_lines_density_when_lines_is_zero() {
+ when(FILE_1_ATTRS.getLines()).thenReturn(0);
+ when(FILE_2_ATTRS.getLines()).thenReturn(0);
+ underTest.execute();
+ assertNoRawMeasures(DUPLICATED_LINES_DENSITY_KEY);
+ }
+
+ @Test
+ public void compute_100_percent_duplicated_lines_density() {
+ addDuplicatedBlock(FILE_1_REF, 2);
+ addDuplicatedBlock(FILE_2_REF, 3);
+
+ when(FILE_1_ATTRS.getLines()).thenReturn(2);
+ when(FILE_2_ATTRS.getLines()).thenReturn(3);
+
+ underTest.execute();
+
+ assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+ assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+ assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
+ assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
+ assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+ assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+ assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
+ assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+ assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+ }
+
+ /**
+ * 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 = 10; i < blockCount + 9; i++) {
+ duplicates[i - 10] = new TextBlock(i, i);
+ }
+ duplicationRepository.addDuplication(fileRef, original, duplicates);
+ }
+
+ private void addRawMeasure(int componentRef, String metricKey, int value) {
+ measureRepository.addRawMeasure(componentRef, metricKey, newMeasureBuilder().create(value));
+ }
+
+ private void assertNoRawMeasures(String metricKey) {
+ assertThat(measureRepository.getAddedRawMeasures(FILE_1_REF).get(metricKey)).isEmpty();
+ assertThat(measureRepository.getAddedRawMeasures(FILE_2_REF).get(metricKey)).isEmpty();
+ assertThat(measureRepository.getAddedRawMeasures(DIRECTORY_REF).get(metricKey)).isEmpty();
+ assertThat(measureRepository.getAddedRawMeasures(SUB_MODULE_1_REF).get(metricKey)).isEmpty();
+ assertThat(measureRepository.getAddedRawMeasures(MODULE_REF).get(metricKey)).isEmpty();
+ assertThat(measureRepository.getAddedRawMeasures(ROOT_REF).get(metricKey)).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);
+ }
+
+ private void assertRawMeasureValue(int componentRef, String metricKey, double value) {
+ assertThat(measureRepository.getAddedRawMeasure(componentRef, metricKey).get().getDoubleValue()).isEqualTo(value);
+ }
+
+ 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);
+ }
+
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info 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.ce.task.projectanalysis.duplication;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
-import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
-import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.guava.api.Assertions.assertThat;
-import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES;
-import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_KEY;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS_KEY;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES_KEY;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY_KEY;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_KEY;
-import static org.sonar.api.measures.CoreMetrics.LINES;
-import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
-import static org.sonar.api.measures.CoreMetrics.NCLOC;
-import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.MODULE;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
-import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
-import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
-
-public class ReportDuplicationMeasuresTest {
- 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 MetricRepositoryRule metricRepository = new MetricRepositoryRule()
- .add(LINES)
- .add(NCLOC)
- .add(COMMENT_LINES)
- .add(DUPLICATED_BLOCKS)
- .add(DUPLICATED_FILES)
- .add(DUPLICATED_LINES)
- .add(DUPLICATED_LINES_DENSITY);
- @Rule
- public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
- @Rule
- public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);
-
- private DuplicationMeasures underTest = new DuplicationMeasures(treeRootHolder, metricRepository, measureRepository, duplicationRepository);
-
- @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(3, 3), new TextBlock(2, 3));
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_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(3, 3));
- duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(2, 2), new TextBlock(3, 3));
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_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));
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_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));
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 1);
- }
-
- @Test
- public void compute_and_aggregate_duplicated_blocks_from_single_duplication() {
- addDuplicatedBlock(FILE_1_REF, 10);
- addDuplicatedBlock(FILE_2_REF, 40);
- addDuplicatedBlock(FILE_4_REF, 5);
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 10);
- assertRawMeasureValue(FILE_2_REF, DUPLICATED_BLOCKS_KEY, 40);
- assertRawMeasureValue(FILE_3_REF, DUPLICATED_BLOCKS_KEY, 0);
- assertRawMeasureValue(FILE_4_REF, DUPLICATED_BLOCKS_KEY, 5);
- assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_BLOCKS_KEY, 50);
- assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_BLOCKS_KEY, 50);
- assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_BLOCKS_KEY, 5);
- assertRawMeasureValue(MODULE_REF, DUPLICATED_BLOCKS_KEY, 55);
- assertRawMeasureValue(ROOT_REF, DUPLICATED_BLOCKS_KEY, 55);
- }
-
- @Test
- public void compute_and_aggregate_duplicated_blocks_to_zero_when_no_duplication() {
- underTest.execute();
-
- assertComputedAndAggregatedToZeroInt(DUPLICATED_BLOCKS_KEY);
- }
-
- @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));
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 2);
- }
-
- @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));
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 1);
- }
-
- @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));
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 1);
- }
-
- @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));
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 7);
- }
-
- @Test
- public void compute_duplicated_lines_counts_lines_from_original_and_InnerDuplicate_only_once() {
- TextBlock original = new TextBlock(1, 12);
- duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(10, 11), new TextBlock(11, 15));
- duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(2, 2), new TextBlock(96, 96));
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 16);
- }
-
- @Test
- public void compute_and_aggregate_duplicated_files() {
- addDuplicatedBlock(FILE_1_REF, 2);
- addDuplicatedBlock(FILE_3_REF, 10);
- addDuplicatedBlock(FILE_4_REF, 50);
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_FILES_KEY, 1);
- assertRawMeasureValue(FILE_2_REF, DUPLICATED_FILES_KEY, 0);
- assertRawMeasureValue(FILE_3_REF, DUPLICATED_FILES_KEY, 1);
- assertRawMeasureValue(FILE_4_REF, DUPLICATED_FILES_KEY, 1);
- assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_FILES_KEY, 1);
- assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_FILES_KEY, 1);
- assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_FILES_KEY, 2);
- assertRawMeasureValue(MODULE_REF, DUPLICATED_FILES_KEY, 3);
- assertRawMeasureValue(ROOT_REF, DUPLICATED_FILES_KEY, 3);
- }
-
- @Test
- public void compute_and_aggregate_zero_duplicated_files_when_no_duplication_data() {
- underTest.execute();
-
- assertComputedAndAggregatedToZeroInt(DUPLICATED_FILES_KEY);
- }
-
- @Test
- public void compute_and_aggregate_duplicated_lines() {
- addDuplicatedBlock(FILE_1_REF, 10);
- addDuplicatedBlock(FILE_2_REF, 9);
- addDuplicatedBlock(FILE_4_REF, 7);
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 10);
- assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_KEY, 9);
- assertRawMeasureValue(FILE_3_REF, DUPLICATED_LINES_KEY, 0);
- assertRawMeasureValue(FILE_4_REF, DUPLICATED_LINES_KEY, 7);
- assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_KEY, 19);
- assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_KEY, 19);
- assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_LINES_KEY, 7);
- assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_KEY, 26);
- assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_KEY, 26);
- }
-
- @Test
- public void compute_and_aggregate_zero_duplicated_line_when_no_duplication() {
- underTest.execute();
-
- assertComputedAndAggregatedToZeroInt(DUPLICATED_LINES_KEY);
- }
-
- @Test
- public void compute_and_aggregate_duplicated_lines_density_using_lines() {
- addDuplicatedBlock(FILE_1_REF, 2);
- addDuplicatedBlock(FILE_2_REF, 3);
-
- addRawMeasure(FILE_1_REF, LINES_KEY, 10);
- addRawMeasure(FILE_2_REF, LINES_KEY, 40);
- addRawMeasure(DIRECTORY_REF, LINES_KEY, 50);
- addRawMeasure(SUB_MODULE_1_REF, LINES_KEY, 50);
- addRawMeasure(MODULE_REF, LINES_KEY, 50);
- addRawMeasure(ROOT_REF, LINES_KEY, 50);
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 20d);
- assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 7.5d);
- assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- }
-
- @Test
- public void compute_and_aggregate_duplicated_lines_density_using_nclocs_and_comment_lines() {
- addDuplicatedBlock(FILE_1_REF, 2);
- addDuplicatedBlock(FILE_2_REF, 3);
-
- addRawMeasure(FILE_1_REF, COMMENT_LINES_KEY, 2);
- addRawMeasure(FILE_2_REF, COMMENT_LINES_KEY, 10);
- addRawMeasure(DIRECTORY_REF, COMMENT_LINES_KEY, 12);
- addRawMeasure(SUB_MODULE_1_REF, COMMENT_LINES_KEY, 12);
- addRawMeasure(MODULE_REF, COMMENT_LINES_KEY, 12);
- addRawMeasure(ROOT_REF, COMMENT_LINES_KEY, 12);
-
- addRawMeasure(FILE_1_REF, NCLOC_KEY, 8);
- addRawMeasure(FILE_2_REF, NCLOC_KEY, 30);
- addRawMeasure(DIRECTORY_REF, NCLOC_KEY, 38);
- addRawMeasure(SUB_MODULE_1_REF, NCLOC_KEY, 38);
- addRawMeasure(MODULE_REF, NCLOC_KEY, 38);
- addRawMeasure(ROOT_REF, NCLOC_KEY, 38);
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 20d);
- assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 7.5d);
- assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- }
-
- @Test
- public void compute_duplicated_lines_density_using_only_nclocs() {
- addDuplicatedBlock(FILE_1_REF, 2);
- addDuplicatedBlock(FILE_2_REF, 3);
-
- addRawMeasure(FILE_1_REF, NCLOC_KEY, 10);
- addRawMeasure(FILE_2_REF, NCLOC_KEY, 40);
- addRawMeasure(DIRECTORY_REF, NCLOC_KEY, 50);
- addRawMeasure(SUB_MODULE_1_REF, NCLOC_KEY, 50);
- addRawMeasure(MODULE_REF, NCLOC_KEY, 50);
- addRawMeasure(ROOT_REF, NCLOC_KEY, 50);
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 20d);
- assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 7.5d);
- assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- }
-
- @Test
- public void compute_zero_percent_duplicated_lines_density_when_there_is_no_duplication() {
- addRawMeasure(FILE_1_REF, COMMENT_LINES_KEY, 2);
- addRawMeasure(FILE_2_REF, COMMENT_LINES_KEY, 10);
- addRawMeasure(DIRECTORY_REF, COMMENT_LINES_KEY, 12);
- addRawMeasure(SUB_MODULE_1_REF, COMMENT_LINES_KEY, 12);
- addRawMeasure(MODULE_REF, COMMENT_LINES_KEY, 12);
- addRawMeasure(ROOT_REF, COMMENT_LINES_KEY, 12);
-
- addRawMeasure(FILE_1_REF, NCLOC_KEY, 8);
- addRawMeasure(FILE_2_REF, NCLOC_KEY, 30);
- addRawMeasure(DIRECTORY_REF, NCLOC_KEY, 38);
- addRawMeasure(SUB_MODULE_1_REF, NCLOC_KEY, 38);
- addRawMeasure(MODULE_REF, NCLOC_KEY, 38);
- addRawMeasure(ROOT_REF, NCLOC_KEY, 38);
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
- assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
- assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
- assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
- assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
- assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
- }
-
- @Test
- public void not_compute_duplicated_lines_density_when_lines_is_zero() {
- addRawMeasure(FILE_1_REF, LINES_KEY, 0);
- addRawMeasure(FILE_2_REF, LINES_KEY, 0);
- addRawMeasure(DIRECTORY_REF, LINES_KEY, 0);
- addRawMeasure(SUB_MODULE_1_REF, LINES_KEY, 0);
- addRawMeasure(MODULE_REF, LINES_KEY, 0);
- addRawMeasure(ROOT_REF, LINES_KEY, 0);
-
- underTest.execute();
-
- assertNoRawMeasures(DUPLICATED_LINES_DENSITY_KEY);
- }
-
- @Test
- public void not_compute_duplicated_lines_density_when_ncloc_and_comment_are_zero() {
- addRawMeasure(FILE_1_REF, COMMENT_LINES_KEY, 0);
- addRawMeasure(FILE_2_REF, COMMENT_LINES_KEY, 0);
- addRawMeasure(DIRECTORY_REF, COMMENT_LINES_KEY, 0);
- addRawMeasure(SUB_MODULE_1_REF, COMMENT_LINES_KEY, 0);
- addRawMeasure(MODULE_REF, COMMENT_LINES_KEY, 0);
- addRawMeasure(ROOT_REF, COMMENT_LINES_KEY, 0);
-
- addRawMeasure(FILE_1_REF, NCLOC_KEY, 0);
- addRawMeasure(FILE_2_REF, NCLOC_KEY, 0);
- addRawMeasure(DIRECTORY_REF, NCLOC_KEY, 0);
- addRawMeasure(SUB_MODULE_1_REF, NCLOC_KEY, 0);
- addRawMeasure(MODULE_REF, NCLOC_KEY, 0);
- addRawMeasure(ROOT_REF, NCLOC_KEY, 0);
-
- underTest.execute();
-
- assertNoRawMeasures(DUPLICATED_LINES_DENSITY_KEY);
- }
-
- @Test
- public void compute_100_percent_duplicated_lines_density() {
- addDuplicatedBlock(FILE_1_REF, 2);
- addDuplicatedBlock(FILE_2_REF, 3);
-
- addRawMeasure(FILE_1_REF, LINES_KEY, 2);
- addRawMeasure(FILE_2_REF, LINES_KEY, 3);
- addRawMeasure(DIRECTORY_REF, LINES_KEY, 5);
- addRawMeasure(SUB_MODULE_1_REF, LINES_KEY, 5);
- addRawMeasure(MODULE_REF, LINES_KEY, 5);
- addRawMeasure(ROOT_REF, LINES_KEY, 5);
-
- underTest.execute();
-
- assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
- assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
- assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
- assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
- assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
- assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
- assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
- }
-
- /**
- * 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 = 10; i < blockCount + 9; i++) {
- duplicates[i - 10] = new TextBlock(i, i);
- }
- duplicationRepository.addDuplication(fileRef, original, duplicates);
- }
-
- private void addRawMeasure(int componentRef, String metricKey, int value) {
- measureRepository.addRawMeasure(componentRef, metricKey, newMeasureBuilder().create(value));
- }
-
- private void assertNoRawMeasures(String metricKey) {
- assertThat(measureRepository.getAddedRawMeasures(FILE_1_REF).get(metricKey)).isEmpty();
- assertThat(measureRepository.getAddedRawMeasures(FILE_2_REF).get(metricKey)).isEmpty();
- assertThat(measureRepository.getAddedRawMeasures(DIRECTORY_REF).get(metricKey)).isEmpty();
- assertThat(measureRepository.getAddedRawMeasures(SUB_MODULE_1_REF).get(metricKey)).isEmpty();
- assertThat(measureRepository.getAddedRawMeasures(MODULE_REF).get(metricKey)).isEmpty();
- assertThat(measureRepository.getAddedRawMeasures(ROOT_REF).get(metricKey)).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);
- }
-
- private void assertRawMeasureValue(int componentRef, String metricKey, double value) {
- assertThat(measureRepository.getAddedRawMeasure(componentRef, metricKey).get().getDoubleValue()).isEqualTo(value);
- }
-
- 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);
- }
-
-}
addRawMeasure(PROJECT_VIEW_1_REF, LINES_KEY, 10);
addRawMeasure(PROJECT_VIEW_2_REF, LINES_KEY, 40);
addRawMeasure(PROJECT_VIEW_3_REF, LINES_KEY, 70);
- addRawMeasure(SUB_SUBVIEW_REF, LINES_KEY, 50);
- addRawMeasure(SUBVIEW_REF, LINES_KEY, 50);
- addRawMeasure(ROOT_REF, LINES_KEY, 50);
underTest.execute();
assertNoNewRawMeasuresOnProjectViews();
assertRawMeasureValue(SUB_SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
assertRawMeasureValue(SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 18d);
- }
-
- @Test
- public void compute_and_aggregate_duplicated_lines_density_using_nclocs_and_comment_lines() {
- addRawMeasure(PROJECT_VIEW_1_REF, DUPLICATED_LINES_KEY, 2);
- addRawMeasure(PROJECT_VIEW_2_REF, DUPLICATED_LINES_KEY, 3);
- addRawMeasure(PROJECT_VIEW_3_REF, DUPLICATED_LINES_KEY, 4);
-
- addRawMeasure(PROJECT_VIEW_1_REF, COMMENT_LINES_KEY, 2);
- addRawMeasure(PROJECT_VIEW_2_REF, COMMENT_LINES_KEY, 10);
- addRawMeasure(PROJECT_VIEW_3_REF, COMMENT_LINES_KEY, 20);
- addRawMeasure(SUB_SUBVIEW_REF, COMMENT_LINES_KEY, 12);
- addRawMeasure(SUBVIEW_REF, COMMENT_LINES_KEY, 12);
- addRawMeasure(ROOT_REF, COMMENT_LINES_KEY, 12);
-
- addRawMeasure(PROJECT_VIEW_1_REF, NCLOC_KEY, 8);
- addRawMeasure(PROJECT_VIEW_2_REF, NCLOC_KEY, 30);
- addRawMeasure(PROJECT_VIEW_3_REF, NCLOC_KEY, 50);
- addRawMeasure(SUB_SUBVIEW_REF, NCLOC_KEY, 38);
- addRawMeasure(SUBVIEW_REF, NCLOC_KEY, 38);
- addRawMeasure(ROOT_REF, NCLOC_KEY, 38);
-
- underTest.execute();
-
- assertNoNewRawMeasuresOnProjectViews();
- assertRawMeasureValue(SUB_SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertRawMeasureValue(SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 18d);
- }
-
- @Test
- public void compute_duplicated_lines_density_using_only_nclocs() {
- addRawMeasure(PROJECT_VIEW_1_REF, DUPLICATED_LINES_KEY, 2);
- addRawMeasure(PROJECT_VIEW_2_REF, DUPLICATED_LINES_KEY, 3);
-
- addRawMeasure(PROJECT_VIEW_1_REF, NCLOC_KEY, 10);
- addRawMeasure(PROJECT_VIEW_2_REF, NCLOC_KEY, 40);
- addRawMeasure(SUB_SUBVIEW_REF, NCLOC_KEY, 50);
- addRawMeasure(SUBVIEW_REF, NCLOC_KEY, 50);
- addRawMeasure(ROOT_REF, NCLOC_KEY, 50);
-
- underTest.execute();
-
- assertNoNewRawMeasuresOnProjectViews();
- assertRawMeasureValue(SUB_SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertRawMeasureValue(SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
- assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
+ assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 7.5d);
}
@Test
addRawMeasure(PROJECT_VIEW_2_REF, DUPLICATED_LINES_KEY, 0);
// no raw measure for PROJECT_VIEW_3_REF
- addRawMeasure(PROJECT_VIEW_1_REF, COMMENT_LINES_KEY, 2);
- addRawMeasure(PROJECT_VIEW_2_REF, COMMENT_LINES_KEY, 10);
- addRawMeasure(SUB_SUBVIEW_REF, COMMENT_LINES_KEY, 12);
- addRawMeasure(SUBVIEW_REF, COMMENT_LINES_KEY, 12);
- addRawMeasure(ROOT_REF, COMMENT_LINES_KEY, 12);
-
- addRawMeasure(PROJECT_VIEW_1_REF, NCLOC_KEY, 8);
- addRawMeasure(PROJECT_VIEW_2_REF, NCLOC_KEY, 30);
- addRawMeasure(SUB_SUBVIEW_REF, NCLOC_KEY, 38);
- addRawMeasure(SUBVIEW_REF, NCLOC_KEY, 38);
- addRawMeasure(ROOT_REF, NCLOC_KEY, 38);
+ addRawMeasure(PROJECT_VIEW_1_REF, LINES_KEY, 10);
+ addRawMeasure(PROJECT_VIEW_2_REF, LINES_KEY, 40);
underTest.execute();
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY)).isPresent();
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY).get().getData()).isEqualTo(
- "<duplications><g><b s=\"1\" l=\"5\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" r=\"" + FILE_1_KEY + "\"/></g></duplications>");
+ "<duplications><g><b s=\"1\" l=\"5\" t=\"false\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" t=\"false\" r=\""
+ + FILE_1_KEY + "\"/></g></duplications>");
assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, DUPLICATIONS_DATA_KEY)).isAbsent();
}
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY)).isPresent();
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY).get().getData()).isEqualTo(
- "<duplications><g><b s=\"1\" l=\"5\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" r=\"" + FILE_2_KEY + "\"/></g></duplications>");
+ "<duplications><g><b s=\"1\" l=\"5\" t=\"false\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" t=\"false\" r=\""
+ + FILE_2_KEY + "\"/></g></duplications>");
assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, DUPLICATIONS_DATA_KEY)).isAbsent();
}
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY)).isPresent();
assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY).get().getData()).isEqualTo(
- "<duplications><g><b s=\"1\" l=\"5\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" r=\"" + fileKeyFromOtherProject + "\"/></g></duplications>");
+ "<duplications><g><b s=\"1\" l=\"5\" t=\"false\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" t=\"false\" r=\""
+ + fileKeyFromOtherProject + "\"/></g></duplications>");
assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, DUPLICATIONS_DATA_KEY)).isAbsent();
}
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);
@Rule
public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
- private LoadDuplicationsFromReportStep underTest = new LoadDuplicationsFromReportStep(treeRootHolder, reportReader, duplicationRepository);
+ private LoadDuplicationsFromReportStep underTest = new LoadDuplicationsFromReportStep(treeRootHolder, analysisMetadataHolder,
+ reportReader, duplicationRepository);
@Test
public void verify_description() {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info 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.duplication.ws;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.db.component.ComponentDto;
+
+public class Duplication {
+ private final ComponentDto componentDto;
+ private final String componentDbKey;
+ private final Integer from;
+ private final Integer size;
+ private final boolean removed;
+
+ static Duplication newRemovedComponent(String componentDbKey, Integer from, Integer size) {
+ return new Duplication(null, componentDbKey, from, size, true);
+ }
+
+ static Duplication newTextComponent(String componentDbKey, Integer from, Integer size) {
+ return new Duplication(null, componentDbKey, from, size, false);
+ }
+
+ static Duplication newComponent(ComponentDto componentDto, Integer from, Integer size) {
+ return new Duplication(componentDto, componentDto.getDbKey(), from, size, false);
+ }
+
+ private Duplication(@Nullable ComponentDto componentDto, String componentDbKey, Integer from, Integer size, boolean removed) {
+ this.componentDto = componentDto;
+ this.componentDbKey = componentDbKey;
+ this.from = from;
+ this.size = size;
+ this.removed = removed;
+ }
+
+ String componentDbKey() {
+ return componentDbKey;
+ }
+
+ Integer from() {
+ return from;
+ }
+
+ Integer size() {
+ return size;
+ }
+
+ public boolean removed() {
+ return removed;
+ }
+
+ /**
+ * can be null if the file wasn't found in DB. This can happen if the target was removed (cross-project duplications) or
+ * if the target refers to an unchanged file in SLBs/PRs.
+ */
+ @CheckForNull
+ public ComponentDto componentDto() {
+ return componentDto;
+ }
+}
import com.google.common.annotations.VisibleForTesting;
import java.io.Serializable;
import java.io.StringReader;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.xml.stream.XMLInputFactory;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentDto;
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Maps.newHashMap;
-
@ServerSide
public class DuplicationsParser {
-
+ private static final BlockComparator BLOCK_COMPARATOR = new BlockComparator();
private final ComponentDao componentDao;
public DuplicationsParser(ComponentDao componentDao) {
}
public List<Block> parse(DbSession session, ComponentDto component, @Nullable String branch, @Nullable String pullRequest, @Nullable String duplicationsData) {
- Map<String, ComponentDto> componentsByKey = newHashMap();
- List<Block> blocks = newArrayList();
- if (duplicationsData != null) {
- try {
- SMInputFactory inputFactory = initStax();
- SMHierarchicCursor root = inputFactory.rootElementCursor(new StringReader(duplicationsData));
- root.advance(); // <duplications>
- SMInputCursor cursor = root.childElementCursor("g");
- while (cursor.getNext() != null) {
- List<Duplication> duplications = newArrayList();
- SMInputCursor bCursor = cursor.childElementCursor("b");
- while (bCursor.getNext() != null) {
- String from = bCursor.getAttrValue("s");
- String size = bCursor.getAttrValue("l");
- String componentKey = bCursor.getAttrValue("r");
- if (from != null && size != null && componentKey != null) {
- duplications.add(createDuplication(componentsByKey, branch, pullRequest, from, size, componentKey, session));
+ Map<String, ComponentDto> componentsByKey = new HashMap<>();
+ List<Block> blocks = new ArrayList<>();
+ if (duplicationsData == null) {
+ return blocks;
+ }
+
+ DuplicationComparator duplicationComparator = new DuplicationComparator(component.uuid(), component.projectUuid());
+
+ try {
+ SMInputFactory inputFactory = initStax();
+ SMHierarchicCursor root = inputFactory.rootElementCursor(new StringReader(duplicationsData));
+ root.advance(); // <duplications>
+ SMInputCursor cursor = root.childElementCursor("g");
+ while (cursor.getNext() != null) {
+ List<Duplication> duplications = new ArrayList<>();
+ SMInputCursor bCursor = cursor.childElementCursor("b");
+ while (bCursor.getNext() != null) {
+ String from = bCursor.getAttrValue("s");
+ String size = bCursor.getAttrValue("l");
+ boolean onlyText = Boolean.parseBoolean(bCursor.getAttrValue("t"));
+ String componentDbKey = bCursor.getAttrValue("r");
+ if (from != null && size != null && componentDbKey != null) {
+ if (onlyText) {
+ // flag means that the target refers to an unchanged file in SLBs/PRs that doesn't exist in DB.
+ // Display as text without a link or other details.
+ duplications.add(Duplication.newTextComponent(componentDbKey, Integer.valueOf(from), Integer.valueOf(size)));
+ } else {
+ duplications.add(createDuplication(componentsByKey, branch, pullRequest, from, size, componentDbKey, session));
}
}
- Collections.sort(duplications, new DuplicationComparator(component.uuid(), component.projectUuid()));
- blocks.add(new Block(duplications));
}
- Collections.sort(blocks, new BlockComparator());
- } catch (XMLStreamException e) {
- throw new IllegalStateException("XML is not valid", e);
+ duplications.sort(duplicationComparator);
+ blocks.add(new Block(duplications));
}
+ blocks.sort(BLOCK_COMPARATOR);
+ return blocks;
+ } catch (XMLStreamException e) {
+ throw new IllegalStateException("XML is not valid", e);
}
- return blocks;
}
- private Duplication createDuplication(Map<String, ComponentDto> componentsByKey, @Nullable String branch, @Nullable String pullRequest, String from, String size,
- String componentDbKey, DbSession session) {
+ private Duplication createDuplication(Map<String, ComponentDto> componentsByKey, @Nullable String branch, @Nullable String pullRequest, String from,
+ String size, String componentDbKey, DbSession session) {
String componentKey = convertToKey(componentDbKey);
- ComponentDto component = componentsByKey.get(componentKey);
- if (component == null) {
- Optional<ComponentDto> componentDtoOptional;
- if (branch != null) {
- componentDtoOptional = componentDao.selectByKeyAndBranch(session, componentKey, branch);
- } else if (pullRequest != null) {
- componentDtoOptional = componentDao.selectByKeyAndPullRequest(session, componentKey, pullRequest);
- } else {
- componentDtoOptional = componentDao.selectByKey(session, componentKey);
- }
- component = componentDtoOptional.isPresent() ? componentDtoOptional.get() : null;
+
+ ComponentDto component;
+ if (componentsByKey.containsKey(componentKey)) {
+ component = componentsByKey.get(componentKey);
+ } else {
+ component = loadComponent(session, componentKey, branch, pullRequest);
componentsByKey.put(componentKey, component);
}
- return new Duplication(component, Integer.valueOf(from), Integer.valueOf(size));
+
+ if (component != null) {
+ return Duplication.newComponent(component, Integer.valueOf(from), Integer.valueOf(size));
+ } else {
+ //This can happen if the target was removed (cross-project duplications)
+ return Duplication.newRemovedComponent(componentKey, Integer.valueOf(from), Integer.valueOf(size));
+ }
+ }
+
+ @CheckForNull
+ private ComponentDto loadComponent(DbSession session, String componentKey, @Nullable String branch, @Nullable String pullRequest) {
+ if (branch != null) {
+ return componentDao.selectByKeyAndBranch(session, componentKey, branch).orElse(null);
+ } else if (pullRequest != null) {
+ return componentDao.selectByKeyAndPullRequest(session, componentKey, pullRequest).orElse(null);
+ } else {
+ return componentDao.selectByKey(session, componentKey).orElse(null);
+ }
}
private static String convertToKey(String dbKey) {
return new SMInputFactory(xmlFactory);
}
+ /**
+ * Sorts the duplications with the following criteria:
+ * - Duplications in the same file by starting line
+ * - Duplications in the same project
+ * - Cross project duplications
+ */
@VisibleForTesting
static class DuplicationComparator implements Comparator<Duplication>, Serializable {
private static final long serialVersionUID = 1;
- private final String uuid;
+ private final String fileUuid;
private final String projectUuid;
- DuplicationComparator(String uuid, String projectUuid) {
- this.uuid = uuid;
+ DuplicationComparator(String fileUuid, String projectUuid) {
+ this.fileUuid = fileUuid;
this.projectUuid = projectUuid;
}
if (d1 == null || d2 == null) {
return -1;
}
- ComponentDto file1 = d1.file();
- ComponentDto file2 = d2.file();
+ ComponentDto file1 = d1.componentDto();
+ ComponentDto file2 = d2.componentDto();
- if (file1 == null || file2 == null) {
- return -1;
- }
- if (file1.equals(d2.file())) {
+ if (file1 != null && file1.equals(file2)) {
// if duplication on same file => order by starting line
return d1.from().compareTo(d2.from());
}
- if (file1.uuid().equals(uuid)) {
+ if (sameFile(file1) && !sameFile(file2)) {
// the current resource must be displayed first
return -1;
}
- if (file2.uuid().equals(uuid)) {
+ if (sameFile(file2) && !sameFile(file1)) {
// the current resource must be displayed first
return 1;
}
- if (StringUtils.equals(file1.projectUuid(), projectUuid) && !StringUtils.equals(file2.projectUuid(), projectUuid)) {
+ if (sameProject(file1) && !sameProject(file2)) {
// if resource is in the same project, this it must be displayed first
return -1;
}
- if (StringUtils.equals(file2.projectUuid(), projectUuid) && !StringUtils.equals(file1.projectUuid(), projectUuid)) {
+ if (sameProject(file2) && !sameProject(file1)) {
// if resource is in the same project, this it must be displayed first
return 1;
}
+
return d1.from().compareTo(d2.from());
}
+
+ private boolean sameFile(@Nullable ComponentDto otherDto) {
+ return otherDto != null && StringUtils.equals(otherDto.uuid(), fileUuid);
+ }
+
+ private boolean sameProject(@Nullable ComponentDto otherDto) {
+ return otherDto == null || StringUtils.equals(otherDto.projectUuid(), projectUuid);
+ }
}
private static class BlockComparator implements Comparator<Block>, Serializable {
}
}
- public static class Duplication {
- private final ComponentDto file;
- private final Integer from;
- private final Integer size;
-
- Duplication(@Nullable ComponentDto file, Integer from, Integer size) {
- this.file = file;
- this.from = from;
- this.size = size;
- }
-
- /**
- * File can be null when duplication is linked on a removed file
- */
- @CheckForNull
- ComponentDto file() {
- return file;
- }
-
- Integer from() {
- return from;
- }
-
- Integer size() {
- return size;
- }
- }
-
static class Block {
private final List<Duplication> duplications;
package org.sonar.server.duplication.ws;
import com.google.common.annotations.VisibleForTesting;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDao;
import org.sonarqube.ws.Duplications.Block;
import org.sonarqube.ws.Duplications.ShowResponse;
-import static com.google.common.collect.Maps.newHashMap;
import static org.sonar.core.util.Protobuf.setNullable;
-// TODO Add UT on branch
public class ShowResponseBuilder {
private final ComponentDao componentDao;
+ private final Map<String, Reference> refByComponentKey = new HashMap<>();
public ShowResponseBuilder(DbClient dbClient) {
this.componentDao = dbClient.componentDao();
}
- @VisibleForTesting
- ShowResponseBuilder(ComponentDao componentDao) {
+ @VisibleForTesting ShowResponseBuilder(ComponentDao componentDao) {
this.componentDao = componentDao;
}
ShowResponse build(DbSession session, List<DuplicationsParser.Block> blocks, @Nullable String branch, @Nullable String pullRequest) {
ShowResponse.Builder response = ShowResponse.newBuilder();
- Map<String, String> refByComponentKey = newHashMap();
blocks.stream()
- .map(block -> toWsDuplication(block, refByComponentKey))
+ .map(this::toWsDuplication)
.forEach(response::addDuplications);
- writeFiles(session, response, refByComponentKey, branch, pullRequest);
-
+ writeFileRefs(session, response, branch, pullRequest);
return response.build();
}
- private static Duplications.Duplication.Builder toWsDuplication(DuplicationsParser.Block block, Map<String, String> refByComponentKey) {
+ private Duplications.Duplication.Builder toWsDuplication(DuplicationsParser.Block block) {
Duplications.Duplication.Builder wsDuplication = Duplications.Duplication.newBuilder();
block.getDuplications().stream()
- .map(d -> toWsBlock(refByComponentKey, d))
+ .map(this::toWsBlock)
.forEach(wsDuplication::addBlocks);
return wsDuplication;
}
- private static Block.Builder toWsBlock(Map<String, String> refByComponentKey, DuplicationsParser.Duplication duplication) {
- String ref = null;
- ComponentDto componentDto = duplication.file();
- if (componentDto != null) {
- String componentKey = componentDto.getDbKey();
- ref = refByComponentKey.computeIfAbsent(componentKey, k -> Integer.toString(refByComponentKey.size() + 1));
+ private Block.Builder toWsBlock(Duplication duplication) {
+ Block.Builder block = Block.newBuilder();
+
+ if (!duplication.removed()) {
+ Reference ref = refByComponentKey.computeIfAbsent(duplication.componentDbKey(), k -> new Reference(
+ Integer.toString(refByComponentKey.size() + 1),
+ duplication.componentDto(),
+ duplication.componentDbKey()));
+ block.setRef(ref.id);
}
- Block.Builder block = Block.newBuilder();
block.setFrom(duplication.from());
block.setSize(duplication.size());
- setNullable(ref, block::setRef);
return block;
}
- private static Duplications.File toWsFile(ComponentDto file, @Nullable ComponentDto project, @Nullable ComponentDto subProject, @Nullable String branch,
- @Nullable String pullRequest) {
+ private void writeFileRefs(DbSession session, ShowResponse.Builder response, @Nullable String branch, @Nullable String pullRequest) {
+ Map<String, ComponentDto> projectsByUuid = new HashMap<>();
+ Map<String, ComponentDto> parentModulesByUuid = new HashMap<>();
+
+ for (Map.Entry<String, Reference> entry : refByComponentKey.entrySet()) {
+ Reference ref = entry.getValue();
+ ComponentDto file = ref.dto();
+
+ if (file != null) {
+ ComponentDto project = getProject(file.projectUuid(), projectsByUuid, session);
+ ComponentDto parentModule = getParentProject(file.getRootUuid(), parentModulesByUuid, session);
+ response.putFiles(ref.id(), toWsFile(file, project, parentModule, branch, pullRequest));
+ } else {
+ response.putFiles(ref.id(), toWsFile(ref.componentKey(), branch, pullRequest));
+ }
+ }
+ }
+
+ private static Duplications.File toWsFile(String componentKey, @Nullable String branch, @Nullable String pullRequest) {
+ Duplications.File.Builder wsFile = Duplications.File.newBuilder();
+ String keyWithoutBranch = ComponentDto.removeBranchAndPullRequestFromKey(componentKey);
+ wsFile.setKey(keyWithoutBranch);
+ wsFile.setName(StringUtils.substringAfterLast(keyWithoutBranch, ":"));
+ setNullable(branch, wsFile::setBranch);
+ setNullable(pullRequest, wsFile::setPullRequest);
+ return wsFile.build();
+ }
+
+ private static Duplications.File toWsFile(ComponentDto file, @Nullable ComponentDto project, @Nullable ComponentDto subProject,
+ @Nullable String branch, @Nullable String pullRequest) {
Duplications.File.Builder wsFile = Duplications.File.newBuilder();
wsFile.setKey(file.getKey());
wsFile.setUuid(file.uuid());
return wsFile.build();
}
- private void writeFiles(DbSession session, ShowResponse.Builder response, Map<String, String> refByComponentKey, @Nullable String branch, @Nullable String pullRequest) {
- Map<String, ComponentDto> projectsByUuid = newHashMap();
- Map<String, ComponentDto> parentModulesByUuid = newHashMap();
- Map<String, Duplications.File> filesByRef = response.getMutableFiles();
-
- for (Map.Entry<String, String> entry : refByComponentKey.entrySet()) {
- String componentKey = entry.getKey();
- String ref = entry.getValue();
- Optional<ComponentDto> fileOptional = componentDao.selectByKey(session, componentKey);
- if (fileOptional.isPresent()) {
- ComponentDto file = fileOptional.get();
-
- ComponentDto project = getProject(file.projectUuid(), projectsByUuid, session);
- ComponentDto parentModule = getParentProject(file.getRootUuid(), parentModulesByUuid, session);
- filesByRef.put(ref, toWsFile(file, project, parentModule, branch, pullRequest));
- }
- }
- }
-
private ComponentDto getProject(String projectUuid, Map<String, ComponentDto> projectsByUuid, DbSession session) {
ComponentDto project = projectsByUuid.get(projectUuid);
if (project == null) {
return project;
}
+ private static class Reference {
+ private final String id;
+ private final ComponentDto dto;
+ private final String componentKey;
+
+ public Reference(String id, @Nullable ComponentDto dto, String componentKey) {
+ this.id = id;
+ this.dto = dto;
+ this.componentKey = componentKey;
+ }
+
+ public String id() {
+ return id;
+ }
+
+ @CheckForNull
+ public ComponentDto dto() {
+ return dto;
+ }
+
+ public String componentKey() {
+ return componentKey;
+ }
+
+ }
+
}
@Rule
public DbTester db = DbTester.create();
- DuplicationsParser parser = new DuplicationsParser(db.getDbClient().componentDao());
+ private DuplicationsParser parser = new DuplicationsParser(db.getDbClient().componentDao());
@Test
public void empty_list_when_no_data() {
"</duplications>", file.getDbKey(), file.getDbKey()));
assertThat(blocks).hasSize(1);
- List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+ List<Duplication> duplications = blocks.get(0).getDuplications();
assertThat(duplications).hasSize(2);
// Smallest line comes first
- DuplicationsParser.Duplication duplication1 = duplications.get(0);
- assertThat(duplication1.file()).isEqualTo(file);
+ Duplication duplication1 = duplications.get(0);
+ assertThat(duplication1.componentDto()).isEqualTo(file);
assertThat(duplication1.from()).isEqualTo(20);
assertThat(duplication1.size()).isEqualTo(5);
- DuplicationsParser.Duplication duplication2 = duplications.get(1);
- assertThat(duplication2.file()).isEqualTo(file);
+ Duplication duplication2 = duplications.get(1);
+ assertThat(duplication2.componentDto()).isEqualTo(file);
assertThat(duplication2.from()).isEqualTo(31);
assertThat(duplication2.size()).isEqualTo(5);
}
"</duplications>", file2.getDbKey(), file1.getDbKey()));
assertThat(blocks).hasSize(1);
- List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+ List<Duplication> duplications = blocks.get(0).getDuplications();
assertThat(duplications).hasSize(2);
// Current file comes first
- DuplicationsParser.Duplication duplication1 = duplications.get(0);
- assertThat(duplication1.file()).isEqualTo(file1);
+ Duplication duplication1 = duplications.get(0);
+ assertThat(duplication1.componentDto()).isEqualTo(file1);
assertThat(duplication1.from()).isEqualTo(31);
assertThat(duplication1.size()).isEqualTo(5);
- DuplicationsParser.Duplication duplication2 = duplications.get(1);
- assertThat(duplication2.file()).isEqualTo(file2);
+ Duplication duplication2 = duplications.get(1);
+ assertThat(duplication2.componentDto()).isEqualTo(file2);
assertThat(duplication2.from()).isEqualTo(20);
assertThat(duplication2.size()).isEqualTo(5);
}
"</duplications>", file1.getDbKey(), fileOnProject2.getDbKey(), file2.getDbKey()));
assertThat(blocks).hasSize(1);
- List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+ List<Duplication> duplications = blocks.get(0).getDuplications();
assertThat(duplications).hasSize(3);
// Current file's project comes first
- DuplicationsParser.Duplication duplication1 = duplications.get(0);
- assertThat(duplication1.file()).isEqualTo(file1);
+ Duplication duplication1 = duplications.get(0);
+ assertThat(duplication1.componentDto()).isEqualTo(file1);
assertThat(duplication1.from()).isEqualTo(148);
assertThat(duplication1.size()).isEqualTo(24);
- DuplicationsParser.Duplication duplication2 = duplications.get(1);
- assertThat(duplication2.file()).isEqualTo(file2);
+ Duplication duplication2 = duplications.get(1);
+ assertThat(duplication2.componentDto()).isEqualTo(file2);
assertThat(duplication2.from()).isEqualTo(111);
assertThat(duplication2.size()).isEqualTo(24);
// Other project comes last
- DuplicationsParser.Duplication duplication3 = duplications.get(2);
- assertThat(duplication3.file()).isEqualTo(fileOnProject2);
+ Duplication duplication3 = duplications.get(2);
+ assertThat(duplication3.componentDto()).isEqualTo(fileOnProject2);
assertThat(duplication3.from()).isEqualTo(137);
assertThat(duplication3.size()).isEqualTo(24);
}
"</duplications>", file.getDbKey(), "not_existing"));
assertThat(blocks).hasSize(1);
- List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+ List<Duplication> duplications = blocks.get(0).getDuplications();
assertThat(duplications).hasSize(2);
// Duplications on removed file
- DuplicationsParser.Duplication duplication1 = duplication(duplications, null);
- assertThat(duplication1.file()).isNull();
+ Duplication duplication1 = duplication(duplications, null);
+ assertThat(duplication1.componentDto()).isNull();
assertThat(duplication1.from()).isEqualTo(31);
assertThat(duplication1.size()).isEqualTo(5);
- DuplicationsParser.Duplication duplication2 = duplication(duplications, file.getDbKey());
- assertThat(duplication2.file()).isEqualTo(file);
+ Duplication duplication2 = duplication(duplications, file.getDbKey());
+ assertThat(duplication2.componentDto()).isEqualTo(file);
assertThat(duplication2.from()).isEqualTo(20);
assertThat(duplication2.size()).isEqualTo(5);
}
DuplicationsParser.DuplicationComparator comparator = new DuplicationsParser.DuplicationComparator(currentFile.uuid(), currentFile.projectUuid());
// On same file
- assertThat(comparator.compare(new DuplicationsParser.Duplication(currentFile, 2, 2), new DuplicationsParser.Duplication(currentFile, 5, 2))).isEqualTo(-1);
+ assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
+ Duplication.newComponent(currentFile, 5, 2))).isEqualTo(-1);
// Different files on same project
- assertThat(comparator.compare(new DuplicationsParser.Duplication(currentFile, 2, 2), new DuplicationsParser.Duplication(fileOnSameProject, 5, 2))).isEqualTo(-1);
- assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnSameProject, 2, 2), new DuplicationsParser.Duplication(currentFile, 5, 2))).isEqualTo(1);
+ assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
+ Duplication.newComponent(fileOnSameProject, 5, 2))).isEqualTo(-1);
+ assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2),
+ Duplication.newComponent(currentFile, 5, 2))).isEqualTo(1);
// Different files on different projects
- assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnSameProject, 5, 2), new DuplicationsParser.Duplication(fileOnDifferentProject, 2, 2))).isEqualTo(-1);
- assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnDifferentProject, 5, 2), new DuplicationsParser.Duplication(fileOnSameProject, 2, 2))).isEqualTo(1);
+ assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 5, 2),
+ Duplication.newComponent(fileOnDifferentProject, 2, 2))).isEqualTo(-1);
+ assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2),
+ Duplication.newComponent(fileOnSameProject, 2, 2))).isEqualTo(1);
// Files on 2 different projects
ComponentDto project3 = db.components().insertPrivateProject();
- assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnDifferentProject, 5, 2),
- new DuplicationsParser.Duplication(project3, 2, 2))).isEqualTo(1);
+ assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2),
+ Duplication.newComponent(project3, 2, 2))).isEqualTo(1);
// With null duplications
- assertThat(comparator.compare(null, new DuplicationsParser.Duplication(fileOnSameProject, 2, 2))).isEqualTo(-1);
- assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnSameProject, 2, 2), null)).isEqualTo(-1);
+ assertThat(comparator.compare(null, Duplication.newComponent(fileOnSameProject, 2, 2))).isEqualTo(-1);
+ assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2), null)).isEqualTo(-1);
assertThat(comparator.compare(null, null)).isEqualTo(-1);
// On some removed file
- assertThat(comparator.compare(new DuplicationsParser.Duplication(currentFile, 2, 2), new DuplicationsParser.Duplication(null, 5, 2))).isEqualTo(-1);
- assertThat(comparator.compare(new DuplicationsParser.Duplication(null, 2, 2), new DuplicationsParser.Duplication(currentFile, 5, 2))).isEqualTo(-1);
+ assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
+ Duplication.newRemovedComponent("key1", 5, 2))).isEqualTo(-1);
+ assertThat(comparator.compare(Duplication.newRemovedComponent("key2", 2, 2),
+ Duplication.newComponent(currentFile, 5, 2))).isEqualTo(1);
}
@Test
"</duplications>", file2.getDbKey(), file1.getDbKey()));
assertThat(blocks).hasSize(1);
- List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+ List<Duplication> duplications = blocks.get(0).getDuplications();
assertThat(duplications).hasSize(2);
// Current file comes first
- DuplicationsParser.Duplication duplication1 = duplications.get(0);
- assertThat(duplication1.file()).isEqualTo(file1);
- assertThat(duplication1.file().getKey()).isEqualTo(file1.getKey());
+ Duplication duplication1 = duplications.get(0);
+ assertThat(duplication1.componentDto()).isEqualTo(file1);
+ assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey());
assertThat(duplication1.from()).isEqualTo(31);
assertThat(duplication1.size()).isEqualTo(5);
- DuplicationsParser.Duplication duplication2 = duplications.get(1);
- assertThat(duplication2.file()).isEqualTo(file2);
- assertThat(duplication2.file().getKey()).isEqualTo(file2.getKey());
+ Duplication duplication2 = duplications.get(1);
+ assertThat(duplication2.componentDto()).isEqualTo(file2);
+ assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey());
assertThat(duplication2.from()).isEqualTo(20);
assertThat(duplication2.size()).isEqualTo(5);
}
"</duplications>", file2.getDbKey(), file1.getDbKey()));
assertThat(blocks).hasSize(1);
- List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+ List<Duplication> duplications = blocks.get(0).getDuplications();
assertThat(duplications).hasSize(2);
// Current file comes first
- DuplicationsParser.Duplication duplication1 = duplications.get(0);
- assertThat(duplication1.file()).isEqualTo(file1);
- assertThat(duplication1.file().getKey()).isEqualTo(file1.getKey());
+ Duplication duplication1 = duplications.get(0);
+ assertThat(duplication1.componentDto()).isEqualTo(file1);
+ assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey());
assertThat(duplication1.from()).isEqualTo(31);
assertThat(duplication1.size()).isEqualTo(5);
- DuplicationsParser.Duplication duplication2 = duplications.get(1);
- assertThat(duplication2.file()).isEqualTo(file2);
- assertThat(duplication2.file().getKey()).isEqualTo(file2.getKey());
+ Duplication duplication2 = duplications.get(1);
+ assertThat(duplication2.componentDto()).isEqualTo(file2);
+ assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey());
assertThat(duplication2.from()).isEqualTo(20);
assertThat(duplication2.size()).isEqualTo(5);
}
- private static DuplicationsParser.Duplication duplication(List<DuplicationsParser.Duplication> duplications, @Nullable final String componentKey) {
- return Iterables.find(duplications, input -> input != null && (componentKey == null ? input.file() == null
- : input.file() != null && componentKey.equals(input.file().getDbKey())));
+ private static Duplication duplication(List<Duplication> duplications, @Nullable final String componentKey) {
+ return Iterables.find(duplications, input -> input != null && (componentKey == null ? input.componentDto() == null
+ : input.componentDto() != null && componentKey.equals(input.componentDto().getDbKey())));
}
}
ComponentDto file2 = db.components().insertComponent(newFileDto(module));
List<DuplicationsParser.Block> blocks = newArrayList();
blocks.add(new DuplicationsParser.Block(newArrayList(
- new DuplicationsParser.Duplication(file1, 57, 12),
- new DuplicationsParser.Duplication(file2, 73, 12))));
+ Duplication.newComponent(file1, 57, 12),
+ Duplication.newComponent(file2, 73, 12))));
test(blocks, null, null,
"{\n" +
ComponentDto file2 = db.components().insertComponent(newFileDto(project));
List<DuplicationsParser.Block> blocks = newArrayList();
blocks.add(new DuplicationsParser.Block(newArrayList(
- new DuplicationsParser.Duplication(file1, 57, 12),
- new DuplicationsParser.Duplication(file2, 73, 12))));
+ Duplication.newComponent(file1, 57, 12),
+ Duplication.newComponent(file2, 73, 12))));
test(blocks, null, null,
"{\n" +
ComponentDto file = db.components().insertComponent(newFileDto(project));
List<DuplicationsParser.Block> blocks = newArrayList();
blocks.add(new DuplicationsParser.Block(newArrayList(
- new DuplicationsParser.Duplication(file, 57, 12),
+ Duplication.newComponent(file, 57, 12),
// Duplication on a removed file
- new DuplicationsParser.Duplication(null, 73, 12))));
+ Duplication.newRemovedComponent("key", 73, 12))));
test(blocks, null, null,
"{\n" +
"}");
}
+ @Test
+ public void write_duplications_with_a_component_without_details() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto file = db.components().insertComponent(newFileDto(project));
+ List<DuplicationsParser.Block> blocks = newArrayList();
+ blocks.add(new DuplicationsParser.Block(newArrayList(
+ Duplication.newComponent(file, 57, 12),
+ // Duplication on a file without details
+ Duplication.newTextComponent("project:path/to/file", 73, 12))));
+
+ test(blocks, null, null,
+ "{\n" +
+ " \"duplications\": [\n" +
+ " {\n" +
+ " \"blocks\": [\n" +
+ " {\n" +
+ " \"from\": 57, \"size\": 12, \"_ref\": \"1\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"from\": 73, \"size\": 12\n" +
+ " }\n" +
+ " ]\n" +
+ " }," +
+ " ],\n" +
+ " \"files\": {\n" +
+ " \"1\": {\n" +
+ " \"key\": \"" + file.getKey() + "\",\n" +
+ " \"name\": \"" + file.longName() + "\",\n" +
+ " \"project\": \"" + project.getKey() + "\",\n" +
+ " \"projectName\": \"" + project.longName() + "\",\n" +
+ " }\n" +
+ " \"2\": {\n" +
+ " \"key\": \"project:path/to/file\",\n" +
+ " \"name\": \"path/to/file\",\n" +
+ " }\n" +
+ " }" +
+ "}");
+ }
+
@Test
public void write_duplications_on_branch() {
ComponentDto project = db.components().insertMainBranch();
ComponentDto file2 = db.components().insertComponent(newFileDto(branch));
List<DuplicationsParser.Block> blocks = newArrayList();
blocks.add(new DuplicationsParser.Block(newArrayList(
- new DuplicationsParser.Duplication(file1, 57, 12),
- new DuplicationsParser.Duplication(file2, 73, 12))));
+ Duplication.newComponent(file1, 57, 12),
+ Duplication.newComponent(file2, 73, 12))));
test(blocks, branch.getBranch(), null,
"{\n" +
ComponentDto file2 = db.components().insertComponent(newFileDto(pullRequest));
List<DuplicationsParser.Block> blocks = newArrayList();
blocks.add(new DuplicationsParser.Block(newArrayList(
- new DuplicationsParser.Duplication(file1, 57, 12),
- new DuplicationsParser.Duplication(file2, 73, 12))));
+ Duplication.newComponent(file1, 57, 12),
+ Duplication.newComponent(file2, 73, 12))));
test(blocks, null, pullRequest.getPullRequest(),
"{\n" +
@Test
public void write_nothing_when_no_data() {
- test(Collections.emptyList(), null, null,"{\"duplications\": [], \"files\": {}}");
+ test(Collections.emptyList(), null, null, "{\"duplications\": [], \"files\": {}}");
}
private void test(List<DuplicationsParser.Block> blocks, @Nullable String branch, @Nullable String pullRequest, String expected) {
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.sonar.api.batch.fs.InputComponent;
-import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputComponent;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.utils.log.Logger;
import org.sonar.scanner.protocol.output.ScannerReport.Duplicate;
import org.sonar.scanner.protocol.output.ScannerReport.Duplication;
import org.sonar.scanner.report.ReportPublisher;
-import org.sonar.scanner.scan.branch.BranchConfiguration;
import org.sonar.scanner.scan.filesystem.InputComponentStore;
import org.sonar.scanner.util.ProgressReport;
private final SonarCpdBlockIndex index;
private final ReportPublisher publisher;
private final InputComponentStore componentStore;
- private final BranchConfiguration branchConfiguration;
private final ProgressReport progressReport;
private final CpdSettings settings;
private final ExecutorService executorService;
private int count = 0;
private int total;
- public CpdExecutor(CpdSettings settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache, BranchConfiguration branchConfiguration) {
- this(settings, index, publisher, inputComponentCache, branchConfiguration, Executors.newSingleThreadExecutor());
+ public CpdExecutor(CpdSettings settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache) {
+ this(settings, index, publisher, inputComponentCache, Executors.newSingleThreadExecutor());
}
public CpdExecutor(CpdSettings settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache,
- BranchConfiguration branchConfiguration, ExecutorService executorService) {
+ ExecutorService executorService) {
this.settings = settings;
this.index = index;
this.publisher = publisher;
this.componentStore = inputComponentCache;
- this.branchConfiguration = branchConfiguration;
this.progressReport = new ProgressReport("CPD computation", TimeUnit.SECONDS.toMillis(10));
this.executorService = executorService;
}
while (it.hasNext()) {
ResourceBlocks resourceBlocks = it.next();
Optional<FileBlocks> fileBlocks = toFileBlocks(resourceBlocks.resourceId(), resourceBlocks.blocks());
- if (!fileBlocks.isPresent() || shouldSkip(fileBlocks.get().getInputFile())) {
+ if (!fileBlocks.isPresent()) {
continue;
}
components.add(fileBlocks.get());
}
}
- private boolean shouldSkip(DefaultInputFile inputFile) {
- return branchConfiguration.isShortOrPullRequest() && inputFile.status() == InputFile.Status.SAME;
- }
-
private static String pluralize(int files) {
return files == 1 ? "file" : "files";
}
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentMatchers;
-import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
private ExecutorService executorService = mock(ExecutorService.class);
private CpdSettings settings = mock(CpdSettings.class);
private ReportPublisher publisher = mock(ReportPublisher.class);
- private BranchConfiguration branchConfiguration = mock(BranchConfiguration.class);
private SonarCpdBlockIndex index = new SonarCpdBlockIndex(publisher, settings);
private ScannerReportReader reader;
private DefaultInputFile batchComponent1;
private DefaultInputFile batchComponent2;
private DefaultInputFile batchComponent3;
- private DefaultInputFile batchComponent4;
private File baseDir;
private InputComponentStore componentStore;
DefaultInputModule inputModule = TestInputFileBuilder.newDefaultInputModule("foo", baseDir);
componentStore = new InputComponentStore(inputModule, mock(BranchConfiguration.class));
- executor = new CpdExecutor(settings, index, publisher, componentStore, branchConfiguration, executorService);
+ executor = new CpdExecutor(settings, index, publisher, componentStore, executorService);
reader = new ScannerReportReader(outputDir);
batchComponent1 = createComponent("src/Foo.php", 5);
batchComponent2 = createComponent("src/Foo2.php", 5);
batchComponent3 = createComponent("src/Foo3.php", 5);
- batchComponent4 = createComponent("src/Foo4.php", 5, f -> f.setStatus(InputFile.Status.SAME));
}
@Test
assertDuplication(dups[1], 15, 214, batchComponent3.batchId(), 15, 214);
}
- @Test
- public void should_ignore_unmodified_files_in_SLB() {
- when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
- Block block = Block.builder()
- .setBlockHash(new ByteArray("AAAABBBBCCCC"))
- .setResourceId(batchComponent4.key())
- .build();
- index.insert(batchComponent4, Collections.singletonList(block));
- executor.execute();
-
- verify(executorService).shutdown();
- verifyNoMoreInteractions(executorService);
- readDuplications(batchComponent4, 0);
- }
-
@Test
public void should_ignore_missing_component() {
Block block = Block.builder()
import org.sonar.api.internal.SonarRuntimeImpl;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.utils.Version;
-import org.sonar.scanner.scan.branch.BranchConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
private SensorStorage sensorStorage;
private AnalysisMode analysisMode;
private SonarRuntime runtime;
- private BranchConfiguration branchConfig;
@Before
public void prepare() throws Exception {
when(metricFinder.<Integer>findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC);
when(metricFinder.<String>findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION);
settings = new MapSettings();
- branchConfig = mock(BranchConfiguration.class);
sensorStorage = mock(SensorStorage.class);
analysisMode = mock(AnalysisMode.class);
runtime = SonarRuntimeImpl.forSonarQube(Version.parse("5.5"), SonarQubeSide.SCANNER);