Browse Source

SONAR-6990 now read duplication only from DuplicationRepository

tags/5.3-RC1
Sébastien Lesaint 8 years ago
parent
commit
1d4bc6788c

+ 15
- 23
server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java View File

@@ -43,6 +43,8 @@ import org.sonar.server.computation.batch.BatchReportReaderImpl;
import org.sonar.server.computation.batch.TreeRootHolderRule;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.ReportComponent;
import org.sonar.server.computation.duplication.DuplicationRepositoryRule;
import org.sonar.server.computation.duplication.TextBlock;
import org.sonar.server.computation.scm.ScmInfoRepositoryImpl;
import org.sonar.server.computation.source.SourceHashRepositoryImpl;
import org.sonar.server.computation.source.SourceLinesRepositoryImpl;
@@ -60,18 +62,16 @@ public class PersistFileSourcesStepTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);

@Rule
public Benchmark benchmark = new Benchmark();

@Rule
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();

@Rule
public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule();
@Rule
public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);

@Test
public void benchmark() throws Exception {
@@ -92,14 +92,14 @@ public class PersistFileSourcesStepTest {
SourceLinesRepositoryImpl sourceLinesRepository = new SourceLinesRepositoryImpl(batchReportReader);
SourceHashRepositoryImpl sourceHashRepository = new SourceHashRepositoryImpl(sourceLinesRepository);
ScmInfoRepositoryImpl scmInfoRepository = new ScmInfoRepositoryImpl(batchReportReader, analysisMetadataHolder, dbClient, sourceHashRepository);
PersistFileSourcesStep step = new PersistFileSourcesStep(dbClient, System2.INSTANCE, treeRootHolder, batchReportReader, sourceLinesRepository, scmInfoRepository);
PersistFileSourcesStep step = new PersistFileSourcesStep(dbClient, System2.INSTANCE, treeRootHolder, batchReportReader, sourceLinesRepository, scmInfoRepository, duplicationRepository);
step.execute();

long end = System.currentTimeMillis();
long duration = end - start;

assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(NUMBER_OF_FILES);
LOGGER.info(String.format("File sources has been persisted in %d ms", duration));
LOGGER.info(String.format("File sources have been persisted in %d ms", duration));

benchmark.expectAround("Duration to persist FILE_SOURCES", duration, 125000, Benchmark.DEFAULT_ERROR_MARGIN_PERCENTS);
}
@@ -118,14 +118,18 @@ public class PersistFileSourcesStepTest {

List<Component> components = new ArrayList<>();
for (int fileRef = 2; fileRef <= NUMBER_OF_FILES + 1; fileRef++) {
components.add(generateFileReport(writer, fileRef));
project.addChildRef(fileRef);
ReportComponent component = ReportComponent.builder(Component.Type.FILE, fileRef).setUuid(Uuids.create()).setKey("PROJECT:" + fileRef).build();
components.add(component);
}
treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1)
.setUuid(PROJECT_UUID)
.setKey("PROJECT")
.addChildren(components.toArray(new Component[components.size()]))
.build());
for (int fileRef = 2; fileRef <= NUMBER_OF_FILES + 1; fileRef++) {
generateFileReport(writer, fileRef);
project.addChildRef(fileRef);
}

writer.writeComponent(project.build());

@@ -136,6 +140,9 @@ public class PersistFileSourcesStepTest {
LineData lineData = new LineData();
for (int line = 1; line <= NUMBER_OF_LINES; line++) {
lineData.generateLineData(line);

duplicationRepository.addDuplication(fileRef, new TextBlock(line, line), new TextBlock(line + 1, line + 1));

}
writer.writeComponent(BatchReport.Component.newBuilder()
.setRef(fileRef)
@@ -148,7 +155,6 @@ public class PersistFileSourcesStepTest {
writer.writeComponentChangesets(lineData.changesetsBuilder.setComponentRef(fileRef).build());
writer.writeComponentSyntaxHighlighting(fileRef, lineData.highlightings);
writer.writeComponentSymbols(fileRef, lineData.symbols);
writer.writeComponentDuplications(fileRef, lineData.duplications);

return ReportComponent.builder(Component.Type.FILE, fileRef).setUuid(Uuids.create()).setKey("PROJECT:" + fileRef).build();
}
@@ -159,7 +165,6 @@ public class PersistFileSourcesStepTest {
List<BatchReport.Coverage> coverages = new ArrayList<>();
List<BatchReport.SyntaxHighlighting> highlightings = new ArrayList<>();
List<BatchReport.Symbol> symbols = new ArrayList<>();
List<BatchReport.Duplication> duplications = new ArrayList<>();

void generateLineData(int line) {
lines.add("line-" + line);
@@ -197,19 +202,6 @@ public class PersistFileSourcesStepTest {
.setStartLine(line + 1).setEndLine(line + 1).setStartOffset(1).setEndOffset(3)
.build())
.build());

duplications.add(BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(line)
.setEndLine(line)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setRange(BatchReport.TextRange.newBuilder()
.setStartLine(line + 1)
.setEndLine(line + 1)
.build())
.build())
.build());
}
}


+ 79
- 59
server/sonar-server/src/main/java/org/sonar/server/computation/source/DuplicationLineReader.java View File

@@ -20,93 +20,113 @@

package org.sonar.server.computation.source;

import java.io.Serializable;
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.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.sonar.batch.protocol.output.BatchReport;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.sonar.db.protobuf.DbFileSources;
import org.sonar.server.computation.duplication.Duplication;
import org.sonar.server.computation.duplication.InnerDuplicate;
import org.sonar.server.computation.duplication.TextBlock;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.FluentIterable.from;

public class DuplicationLineReader implements LineReader {

private final List<BatchReport.Duplication> duplications;
private final Map<BatchReport.TextRange, Integer> duplicationIdsByRange;
private final Map<TextBlock, Integer> duplicatedTextBlockIndexByTextBlock;

public DuplicationLineReader(Iterator<BatchReport.Duplication> duplications) {
this.duplications = newArrayList(duplications);
// Sort duplication to have deterministic results and avoid false variation that would lead to an unnecessary update of the source files
// data
Collections.sort(this.duplications, new DuplicationComparator());

this.duplicationIdsByRange = createDuplicationIdsByRange(this.duplications);
public DuplicationLineReader(Set<Duplication> duplications) {
this.duplicatedTextBlockIndexByTextBlock = createIndexOfDuplicatedTextBlocks(duplications);
}

@Override
public void read(DbFileSources.Line.Builder lineBuilder) {
int line = lineBuilder.getLine();
List<BatchReport.TextRange> blocks = findDuplicationBlockMatchingLine(line);
for (BatchReport.TextRange block : blocks) {
lineBuilder.addDuplication(duplicationIdsByRange.get(block));
Predicate<Map.Entry<TextBlock, Integer>> containsLine = new TextBlockContainsLine(lineBuilder.getLine());
for (Integer textBlockIndex : from(duplicatedTextBlockIndexByTextBlock.entrySet())
.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);
}
}

private List<BatchReport.TextRange> findDuplicationBlockMatchingLine(int line) {
List<BatchReport.TextRange> blocks = newArrayList();
for (BatchReport.Duplication duplication : duplications) {
if (matchLine(duplication.getOriginPosition(), line)) {
blocks.add(duplication.getOriginPosition());
}
for (BatchReport.Duplicate duplicate : duplication.getDuplicateList()) {
if (isDuplicationOnSameFile(duplicate) && matchLine(duplicate.getRange(), line)) {
blocks.add(duplicate.getRange());
}
}
}
return blocks;
private static boolean isLineInBlock(TextBlock range, int line) {
return line >= range.getStart() && line <= range.getEnd();
}

private static boolean isDuplicationOnSameFile(BatchReport.Duplicate duplicate) {
return !duplicate.hasOtherFileRef();
/**
*
* <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(Collection<Duplication> duplications) {
List<TextBlock> duplicatedTextBlocks = extractAllDuplicatedTextBlocks(duplications);
Collections.sort(duplicatedTextBlocks);
return from(duplicatedTextBlocks)
.toMap(new TextBlockIndexGenerator());
}

private static boolean matchLine(BatchReport.TextRange range, int line) {
return range.getStartLine() <= line && line <= range.getEndLine();
/**
* Duplicated blocks in the current file are either {@link Duplication#getOriginal()} or {@link Duplication#getDuplicates()}
* when the {@link org.sonar.server.computation.duplication.Duplicate} is a {@link InnerDuplicate}.
* <p>
* The returned list is mutable on purpose because it will be sorted.
* </p>
*
* @see {@link #createIndexOfDuplicatedTextBlocks(Collection)}
*/
private static List<TextBlock> extractAllDuplicatedTextBlocks(Collection<Duplication> duplications) {
List<TextBlock> duplicatedBlock = new ArrayList<>(duplications.size());
for (Duplication duplication : duplications) {
duplicatedBlock.add(duplication.getOriginal());
for (InnerDuplicate duplicate : from(duplication.getDuplicates()).filter(InnerDuplicate.class)) {
duplicatedBlock.add(duplicate.getTextBlock());
}
}
return duplicatedBlock;
}

private static int length(BatchReport.TextRange range) {
return (range.getEndLine() - range.getStartLine()) + 1;
}
private static class TextBlockContainsLine implements Predicate<Map.Entry<TextBlock, Integer>> {
private final int line;

private static Map<BatchReport.TextRange, Integer> createDuplicationIdsByRange(List<BatchReport.Duplication> duplications) {
Map<BatchReport.TextRange, Integer> map = newHashMap();
int blockId = 1;
for (BatchReport.Duplication duplication : duplications) {
map.put(duplication.getOriginPosition(), blockId);
blockId++;
for (BatchReport.Duplicate duplicate : duplication.getDuplicateList()) {
if (isDuplicationOnSameFile(duplicate)) {
map.put(duplicate.getRange(), blockId);
blockId++;
}
}
public TextBlockContainsLine(int line) {
this.line = line;
}

@Override
public boolean apply(@Nonnull Map.Entry<TextBlock, Integer> input) {
return isLineInBlock(input.getKey(), line);
}
return map;
}

private static class DuplicationComparator implements Comparator<BatchReport.Duplication>, Serializable {
private enum MapEntryToBlockId implements Function<Map.Entry<TextBlock, Integer>, Integer> {
INSTANCE;

@Override
public int compare(BatchReport.Duplication d1, BatchReport.Duplication d2) {
if (d1.getOriginPosition().getStartLine() == d2.getOriginPosition().getStartLine()) {
return Integer.compare(length(d1.getOriginPosition()), length(d2.getOriginPosition()));
} else {
return Integer.compare(d1.getOriginPosition().getStartLine(), d2.getOriginPosition().getStartLine());
}
@Nonnull
public Integer apply(@Nonnull Map.Entry<TextBlock, Integer> input) {
return input.getValue();
}
}

private static class TextBlockIndexGenerator implements Function<TextBlock, Integer> {
int i = 1;

@Nullable
@Override
public Integer apply(TextBlock input) {
return i++;
}
}
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStep.java View File

@@ -48,7 +48,7 @@ public class LoadDuplicationsFromReportStep implements ComputationStep {

@Override
public String getDescription() {
return "Load inner and project duplications";
return "Load inner file and in project duplications";
}

@Override

+ 45
- 40
server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java View File

@@ -20,23 +20,27 @@

package org.sonar.server.computation.step;

import java.util.Iterator;
import java.util.Set;
import org.apache.commons.lang.StringEscapeUtils;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.batch.protocol.output.BatchReport;
import org.sonar.core.util.CloseableIterator;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.MyBatis;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.server.computation.batch.BatchReportReader;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.CrawlerDepthLimit;
import org.sonar.server.computation.component.DbIdsRepository;
import org.sonar.server.computation.component.DepthTraversalTypeAwareCrawler;
import org.sonar.server.computation.component.ReportTreeRootHolder;
import org.sonar.server.computation.component.TypeAwareVisitorAdapter;
import org.sonar.server.computation.duplication.CrossProjectDuplicate;
import org.sonar.server.computation.duplication.Duplicate;
import org.sonar.server.computation.duplication.Duplication;
import org.sonar.server.computation.duplication.DuplicationRepository;
import org.sonar.server.computation.duplication.InProjectDuplicate;
import org.sonar.server.computation.duplication.InnerDuplicate;
import org.sonar.server.computation.duplication.TextBlock;

import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER;

@@ -48,13 +52,14 @@ public class PersistDuplicationsStep implements ComputationStep {
private final DbClient dbClient;
private final DbIdsRepository dbIdsRepository;
private final ReportTreeRootHolder treeRootHolder;
private final BatchReportReader reportReader;
private final DuplicationRepository duplicationRepository;

public PersistDuplicationsStep(DbClient dbClient, DbIdsRepository dbIdsRepository, ReportTreeRootHolder treeRootHolder, BatchReportReader reportReader) {
public PersistDuplicationsStep(DbClient dbClient, DbIdsRepository dbIdsRepository, ReportTreeRootHolder treeRootHolder,
DuplicationRepository duplicationRepository) {
this.dbClient = dbClient;
this.dbIdsRepository = dbIdsRepository;
this.treeRootHolder = treeRootHolder;
this.reportReader = reportReader;
this.duplicationRepository = duplicationRepository;
}

@Override
@@ -63,7 +68,7 @@ public class PersistDuplicationsStep implements ComputationStep {
try {
MetricDto duplicationMetric = dbClient.metricDao().selectOrFailByKey(session, CoreMetrics.DUPLICATIONS_DATA_KEY);
new DepthTraversalTypeAwareCrawler(new DuplicationVisitor(session, duplicationMetric))
.visit(treeRootHolder.getRoot());
.visit(treeRootHolder.getRoot());
session.commit();
} finally {
MyBatis.closeQuietly(session);
@@ -83,36 +88,30 @@ public class PersistDuplicationsStep implements ComputationStep {

@Override
public void visitFile(Component file) {
visitComponent(file);
}

private void visitComponent(Component component) {
try (CloseableIterator<BatchReport.Duplication> duplications = reportReader.readComponentDuplications(component.getReportAttributes().getRef())) {
if (duplications.hasNext()) {
saveDuplications(component, duplications);
}
Set<Duplication> duplications = duplicationRepository.getDuplications(file);
if (!duplications.isEmpty()) {
saveDuplications(file, duplications);
}
}

private void saveDuplications(Component component, Iterator<BatchReport.Duplication> duplications) {
private void saveDuplications(Component component, Iterable<Duplication> duplications) {
String duplicationXml = createXmlDuplications(component.getKey(), duplications);
MeasureDto measureDto = new MeasureDto()
.setMetricId(duplicationMetric.getId())
.setData(duplicationXml)
.setComponentId(dbIdsRepository.getComponentId(component))
.setSnapshotId(dbIdsRepository.getSnapshotId(component));
.setMetricId(duplicationMetric.getId())
.setData(duplicationXml)
.setComponentId(dbIdsRepository.getComponentId(component))
.setSnapshotId(dbIdsRepository.getSnapshotId(component));
dbClient.measureDao().insert(session, measureDto);
}

private String createXmlDuplications(String componentKey, Iterator<BatchReport.Duplication> duplications) {
private String createXmlDuplications(String componentKey, Iterable<Duplication> duplications) {
StringBuilder xml = new StringBuilder();
xml.append("<duplications>");
while (duplications.hasNext()) {
BatchReport.Duplication duplication = duplications.next();
for (Duplication duplication : duplications) {
xml.append("<g>");
appendDuplication(xml, componentKey, duplication.getOriginPosition());
for (BatchReport.Duplicate duplicationBlock : duplication.getDuplicateList()) {
processDuplicationBlock(xml, duplicationBlock, componentKey);
appendDuplication(xml, componentKey, duplication.getOriginal());
for (Duplicate duplicate : duplication.getDuplicates()) {
processDuplicationBlock(xml, duplicate, componentKey);
}
xml.append("</g>");
}
@@ -120,26 +119,32 @@ public class PersistDuplicationsStep implements ComputationStep {
return xml.toString();
}

private void processDuplicationBlock(StringBuilder xml, BatchReport.Duplicate duplicate, String componentKey) {
if (duplicate.hasOtherFileRef()) {
// Duplication is on a different file
appendDuplication(xml, treeRootHolder.getComponentByRef(duplicate.getOtherFileRef()).getKey(), duplicate);
} else {
private void processDuplicationBlock(StringBuilder xml, Duplicate duplicate, String componentKey) {
if (duplicate instanceof InnerDuplicate) {
// Duplication is on a the same file
appendDuplication(xml, componentKey, duplicate);
} else if (duplicate instanceof InProjectDuplicate) {
// Duplication is on a different file
appendDuplication(xml, ((InProjectDuplicate) duplicate).getFile().getKey(), duplicate);
} else if (duplicate instanceof CrossProjectDuplicate) {
// componentKey is only set for cross project duplications
String crossProjectComponentKey = ((CrossProjectDuplicate) duplicate).getFileKey();
appendDuplication(xml, crossProjectComponentKey, duplicate);
} else {
throw new IllegalArgumentException("Unsupported type of Duplicate " + duplicate.getClass().getName());
}
}

private void appendDuplication(StringBuilder xml, String componentKey, BatchReport.Duplicate duplicate) {
appendDuplication(xml, componentKey, duplicate.getRange());
private void appendDuplication(StringBuilder xml, String componentKey, Duplicate duplicate) {
appendDuplication(xml, componentKey, duplicate.getTextBlock());
}

private void appendDuplication(StringBuilder xml, String componentKey, BatchReport.TextRange range) {
int length = range.getEndLine() - range.getStartLine() + 1;
xml.append("<b s=\"").append(range.getStartLine())
.append("\" l=\"").append(length)
.append("\" r=\"").append(StringEscapeUtils.escapeXml(componentKey))
.append("\"/>");
private void appendDuplication(StringBuilder xml, String componentKey, TextBlock textBlock) {
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("\"/>");
}
}


+ 7
- 6
server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java View File

@@ -47,6 +47,7 @@ import org.sonar.server.computation.component.CrawlerDepthLimit;
import org.sonar.server.computation.component.DepthTraversalTypeAwareCrawler;
import org.sonar.server.computation.component.TreeRootHolder;
import org.sonar.server.computation.component.TypeAwareVisitorAdapter;
import org.sonar.server.computation.duplication.DuplicationRepository;
import org.sonar.server.computation.scm.Changeset;
import org.sonar.server.computation.scm.ScmInfo;
import org.sonar.server.computation.scm.ScmInfoRepository;
@@ -70,15 +71,17 @@ public class PersistFileSourcesStep implements ComputationStep {
private final BatchReportReader reportReader;
private final SourceLinesRepository sourceLinesRepository;
private final ScmInfoRepository scmInfoRepository;
private final DuplicationRepository duplicationRepository;

public PersistFileSourcesStep(DbClient dbClient, System2 system2, TreeRootHolder treeRootHolder, BatchReportReader reportReader, SourceLinesRepository sourceLinesRepository,
ScmInfoRepository scmInfoRepository) {
ScmInfoRepository scmInfoRepository, DuplicationRepository duplicationRepository) {
this.dbClient = dbClient;
this.system2 = system2;
this.treeRootHolder = treeRootHolder;
this.reportReader = reportReader;
this.sourceLinesRepository = sourceLinesRepository;
this.scmInfoRepository = scmInfoRepository;
this.duplicationRepository = duplicationRepository;
}

@Override
@@ -123,7 +126,7 @@ public class PersistFileSourcesStep implements ComputationStep {
int fileRef = file.getReportAttributes().getRef();
BatchReport.Component component = reportReader.readComponent(fileRef);
CloseableIterator<String> linesIterator = sourceLinesRepository.readLines(file);
LineReaders lineReaders = new LineReaders(reportReader, scmInfoRepository, file);
LineReaders lineReaders = new LineReaders(reportReader, scmInfoRepository, duplicationRepository, file);
try {
ComputeFileSourceData computeFileSourceData = new ComputeFileSourceData(linesIterator, lineReaders.readers(), component.getLines());
ComputeFileSourceData.Data fileSourceData = computeFileSourceData.compute();
@@ -202,7 +205,7 @@ public class PersistFileSourcesStep implements ComputationStep {
@CheckForNull
private final ScmLineReader scmLineReader;

LineReaders(BatchReportReader reportReader, ScmInfoRepository scmInfoRepository, Component component) {
LineReaders(BatchReportReader reportReader, ScmInfoRepository scmInfoRepository, DuplicationRepository duplicationRepository, Component component) {
int componentRef = component.getReportAttributes().getRef();
CloseableIterator<BatchReport.Coverage> coverageIt = reportReader.readComponentCoverage(componentRef);
closeables.add(coverageIt);
@@ -225,9 +228,7 @@ public class PersistFileSourcesStep implements ComputationStep {
closeables.add(symbolsIt);
readers.add(new SymbolsLineReader(component, symbolsIt, rangeOffsetConverter));

CloseableIterator<BatchReport.Duplication> duplicationsIt = reportReader.readComponentDuplications(componentRef);
closeables.add(duplicationsIt);
readers.add(new DuplicationLineReader(duplicationsIt));
readers.add(new DuplicationLineReader(duplicationRepository.getDuplications(component)));
}

List<LineReader> readers() {

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportReaderRule.java View File

@@ -162,8 +162,8 @@ public class BatchReportReaderRule implements TestRule, BatchReportReader {
return closeableIterator(this.duplications.get(componentRef));
}

public void putDuplications(int componentRef, List<BatchReport.Duplication> duplications) {
this.duplications.put(componentRef, duplications);
public void putDuplications(int componentRef, BatchReport.Duplication... duplications) {
this.duplications.put(componentRef, Arrays.asList(duplications));
}

@Override

+ 83
- 113
server/sonar-server/src/test/java/org/sonar/server/computation/source/DuplicationLineReaderTest.java View File

@@ -20,11 +20,19 @@

package org.sonar.server.computation.source;

import com.google.common.collect.Iterators;
import com.google.common.collect.ImmutableSet;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Test;
import org.sonar.batch.protocol.output.BatchReport;
import org.sonar.core.util.CloseableIterator;
import org.sonar.db.protobuf.DbFileSources;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.ReportComponent;
import org.sonar.server.computation.duplication.CrossProjectDuplicate;
import org.sonar.server.computation.duplication.Duplicate;
import org.sonar.server.computation.duplication.Duplication;
import org.sonar.server.computation.duplication.InProjectDuplicate;
import org.sonar.server.computation.duplication.InnerDuplicate;
import org.sonar.server.computation.duplication.TextBlock;

import static org.assertj.core.api.Assertions.assertThat;

@@ -38,7 +46,7 @@ public class DuplicationLineReaderTest {

@Test
public void read_nothing() {
DuplicationLineReader reader = new DuplicationLineReader(CloseableIterator.<BatchReport.Duplication>emptyCloseableIterator());
DuplicationLineReader reader = new DuplicationLineReader(Collections.<Duplication>emptySet());

reader.read(line1);

@@ -47,19 +55,7 @@ public class DuplicationLineReaderTest {

@Test
public void read_duplication_with_duplicates_on_same_file() {
DuplicationLineReader reader = new DuplicationLineReader(Iterators.forArray(
BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(1)
.setEndLine(2)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setRange(BatchReport.TextRange.newBuilder()
.setStartLine(3)
.setEndLine(4)
.build())
.build())
.build()));
DuplicationLineReader reader = duplicationLineReader(duplication(1, 2, innerDuplicate(3, 4)));

reader.read(line1);
reader.read(line2);
@@ -74,20 +70,28 @@ public class DuplicationLineReaderTest {

@Test
public void read_duplication_with_duplicates_on_other_file() {
DuplicationLineReader reader = new DuplicationLineReader(Iterators.forArray(
BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(1)
.setEndLine(2)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setOtherFileRef(2)
.setRange(BatchReport.TextRange.newBuilder()
.setStartLine(3)
.setEndLine(4)
.build())
.build())
.build()));
DuplicationLineReader reader = duplicationLineReader(
duplication(
1, 2,
new InProjectDuplicate(fileComponent(1).build(), new TextBlock(3, 4))));

reader.read(line1);
reader.read(line2);
reader.read(line3);
reader.read(line4);

assertThat(line1.getDuplicationList()).containsExactly(1);
assertThat(line2.getDuplicationList()).containsExactly(1);
assertThat(line3.getDuplicationList()).isEmpty();
assertThat(line4.getDuplicationList()).isEmpty();
}

@Test
public void read_duplication_with_duplicates_on_other_file_from_other_project() {
DuplicationLineReader reader = duplicationLineReader(
duplication(
1, 2,
new CrossProjectDuplicate("other-component-key-from-another-project", new TextBlock(3, 4))));

reader.read(line1);
reader.read(line2);
@@ -102,38 +106,21 @@ public class DuplicationLineReaderTest {

@Test
public void read_many_duplications() {
DuplicationLineReader reader = new DuplicationLineReader(Iterators.forArray(
BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(1)
.setEndLine(1)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setRange(BatchReport.TextRange.newBuilder()
.setStartLine(2)
.setEndLine(2)
.build())
.build())
.build(),
BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(1)
.setEndLine(2)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setRange(BatchReport.TextRange.newBuilder()
.setStartLine(3)
.setEndLine(4)
.build())
.build())
.build()));
DuplicationLineReader reader = duplicationLineReader(
duplication(
1, 1,
innerDuplicate(2, 2)),
duplication(
1, 2,
innerDuplicate(3, 4))
);

reader.read(line1);
reader.read(line2);
reader.read(line3);
reader.read(line4);

assertThat(line1.getDuplicationList()).containsExactly(1, 3);
assertThat(line1.getDuplicationList()).containsExactly(1, 2);
assertThat(line2.getDuplicationList()).containsExactly(2, 3);
assertThat(line3.getDuplicationList()).containsExactly(4);
assertThat(line4.getDuplicationList()).containsExactly(4);
@@ -141,31 +128,14 @@ public class DuplicationLineReaderTest {

@Test
public void should_be_sorted_by_line_block() {
DuplicationLineReader reader = new DuplicationLineReader(Iterators.forArray(
BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(2)
.setEndLine(2)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setRange(BatchReport.TextRange.newBuilder()
.setStartLine(4)
.setEndLine(4)
.build())
.build())
.build(),
BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(1)
.setEndLine(1)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setRange(BatchReport.TextRange.newBuilder()
.setStartLine(3)
.setEndLine(3)
.build())
.build())
.build()));
DuplicationLineReader reader = duplicationLineReader(
duplication(
2, 2,
innerDuplicate(4, 4)),
duplication(
1, 1,
innerDuplicate(3, 3))
);

reader.read(line1);
reader.read(line2);
@@ -173,48 +143,48 @@ public class DuplicationLineReaderTest {
reader.read(line4);

assertThat(line1.getDuplicationList()).containsExactly(1);
assertThat(line2.getDuplicationList()).containsExactly(3);
assertThat(line3.getDuplicationList()).containsExactly(2);
assertThat(line2.getDuplicationList()).containsExactly(2);
assertThat(line3.getDuplicationList()).containsExactly(3);
assertThat(line4.getDuplicationList()).containsExactly(4);
}

@Test
public void should_be_sorted_by_line_length() {
DuplicationLineReader reader = new DuplicationLineReader(Iterators.forArray(
BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(1)
.setEndLine(2)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setRange(BatchReport.TextRange.newBuilder()
.setStartLine(3)
.setEndLine(4)
.build())
.build())
.build(),
BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(1)
.setEndLine(1)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setRange(BatchReport.TextRange.newBuilder()
.setStartLine(4)
.setEndLine(4)
.build())
.build())
.build()));
DuplicationLineReader reader = duplicationLineReader(
duplication(
1, 2,
innerDuplicate(3, 4)),
duplication(
1, 1,
innerDuplicate(4, 4)
)
);

reader.read(line1);
reader.read(line2);
reader.read(line3);
reader.read(line4);

assertThat(line1.getDuplicationList()).containsExactly(1, 3);
assertThat(line2.getDuplicationList()).containsExactly(3);
assertThat(line3.getDuplicationList()).containsExactly(4);
assertThat(line4.getDuplicationList()).containsExactly(2, 4);
assertThat(line1.getDuplicationList()).containsExactly(1, 2);
assertThat(line2.getDuplicationList()).containsExactly(2);
assertThat(line3.getDuplicationList()).containsExactly(3);
assertThat(line4.getDuplicationList()).containsExactly(3, 4);
}

private static ReportComponent.Builder fileComponent(int ref) {
return ReportComponent.builder(Component.Type.FILE, ref);
}

private static DuplicationLineReader duplicationLineReader(Duplication... duplications) {
return new DuplicationLineReader(ImmutableSet.copyOf(Arrays.asList(duplications)));
}

private static Duplication duplication(int originalStart, int originalEnd, Duplicate... duplicates) {
return new Duplication(new TextBlock(originalStart, originalEnd), Arrays.asList(duplicates));
}

private static InnerDuplicate innerDuplicate(int start, int end) {
return new InnerDuplicate(new TextBlock(start, end));
}

}

+ 199
- 0
server/sonar-server/src/test/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStepTest.java View File

@@ -0,0 +1,199 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.computation.step;

import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.batch.protocol.output.BatchReport;
import org.sonar.server.computation.batch.BatchReportReaderRule;
import org.sonar.server.computation.batch.TreeRootHolderRule;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.duplication.Duplicate;
import org.sonar.server.computation.duplication.Duplication;
import org.sonar.server.computation.duplication.DuplicationRepositoryRule;
import org.sonar.server.computation.duplication.InProjectDuplicate;
import org.sonar.server.computation.duplication.InnerDuplicate;
import org.sonar.server.computation.duplication.TextBlock;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.server.computation.component.Component.Type.FILE;
import static org.sonar.server.computation.component.Component.Type.PROJECT;
import static org.sonar.server.computation.component.ReportComponent.builder;

public class LoadDuplicationsFromReportStepTest {
private static final int LINE = 2;
private static final int OTHER_LINE = 300;
private static final int ROOT_REF = 1;
private static final int FILE_1_REF = 11;
private static final int FILE_2_REF = 12;

@Rule
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(
builder(PROJECT, ROOT_REF)
.addChildren(
builder(FILE, FILE_1_REF).build(),
builder(FILE, FILE_2_REF).build()
)
.build()
);
@Rule
public BatchReportReaderRule reportReader = new BatchReportReaderRule();
@Rule
public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);
@Rule
public ExpectedException expectedException = ExpectedException.none();

private LoadDuplicationsFromReportStep underTest = new LoadDuplicationsFromReportStep(treeRootHolder, reportReader, duplicationRepository);

@Test
public void verify_description() {
assertThat(underTest.getDescription()).isEqualTo("Load inner file and in project duplications");
}

@Test
public void loads_no_duplications_if_reader_has_no_duplication() {
underTest.execute();

assertNoDuplication(FILE_1_REF);
}

@Test
public void loads_duplication_without_otherFileRef_as_inner_duplication() {
reportReader.putDuplications(FILE_2_REF, createDuplication(singleLineTextRange(LINE), createInnerDuplicate(LINE + 1)));

underTest.execute();

assertNoDuplication(FILE_1_REF);
assertDuplications(FILE_2_REF, singleLineTextBlock(LINE), new InnerDuplicate(singleLineTextBlock(LINE + 1)));
}

@Test
public void loads_duplication_with_otherFileRef_as_inProject_duplication() {
reportReader.putDuplications(FILE_1_REF, createDuplication(singleLineTextRange(LINE), createInProjectDuplicate(FILE_2_REF, LINE + 1)));

underTest.execute();

assertDuplications(FILE_1_REF, singleLineTextBlock(LINE), new InProjectDuplicate(treeRootHolder.getComponentByRef(FILE_2_REF), singleLineTextBlock(LINE + 1)));
assertNoDuplication(FILE_2_REF);
}

@Test
public void loads_multiple_duplications_with_multiple_duplicates() {
reportReader.putDuplications(
FILE_2_REF,
createDuplication(
singleLineTextRange(LINE),
createInnerDuplicate(LINE + 1), createInnerDuplicate(LINE + 2), createInProjectDuplicate(FILE_1_REF, LINE), createInProjectDuplicate(FILE_1_REF, LINE + 10)),
createDuplication(
singleLineTextRange(OTHER_LINE),
createInProjectDuplicate(FILE_1_REF, OTHER_LINE)),
createDuplication(
singleLineTextRange(OTHER_LINE + 80),
createInnerDuplicate(LINE), createInnerDuplicate(LINE + 10))
);

underTest.execute();

Component file1Component = treeRootHolder.getComponentByRef(FILE_1_REF);
assertThat(duplicationRepository.getDuplications(FILE_2_REF)).containsOnly(
duplication(
singleLineTextBlock(LINE),
new InnerDuplicate(singleLineTextBlock(LINE + 1)), new InnerDuplicate(singleLineTextBlock(LINE + 2)), new InProjectDuplicate(file1Component, singleLineTextBlock(LINE)),
new InProjectDuplicate(file1Component, singleLineTextBlock(LINE + 10))),
duplication(
singleLineTextBlock(OTHER_LINE),
new InProjectDuplicate(file1Component, singleLineTextBlock(OTHER_LINE))
),
duplication(
singleLineTextBlock(OTHER_LINE + 80),
new InnerDuplicate(singleLineTextBlock(LINE)), new InnerDuplicate(singleLineTextBlock(LINE + 10))
)
);
}

@Test
public void loads_duplication_with_otherFileRef_throws_IAE_if_component_does_not_exist() {
int line = 2;
reportReader.putDuplications(FILE_1_REF, createDuplication(singleLineTextRange(line), createInProjectDuplicate(666, line + 1)));

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Component with ref '666' hasn't been found");

underTest.execute();
}

@Test
public void loads_duplication_with_otherFileRef_throws_IAE_if_references_itself() {
int line = 2;
reportReader.putDuplications(FILE_1_REF, createDuplication(singleLineTextRange(line), createInProjectDuplicate(FILE_1_REF, line + 1)));

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("file and otherFile Components can not be the same");

underTest.execute();
}

private void assertDuplications(int fileRef, TextBlock original, Duplicate... duplicates) {
assertThat(duplicationRepository.getDuplications(fileRef)).containsExactly(duplication(original, duplicates));
}

private static Duplication duplication(TextBlock original, Duplicate... duplicates) {
return new Duplication(original, Arrays.asList(duplicates));
}

private TextBlock singleLineTextBlock(int line) {
return new TextBlock(line, line);
}

private static BatchReport.Duplication createDuplication(BatchReport.TextRange original, BatchReport.Duplicate... duplicates) {
BatchReport.Duplication.Builder builder = BatchReport.Duplication.newBuilder()
.setOriginPosition(original);
for (BatchReport.Duplicate duplicate : duplicates) {
builder.addDuplicate(duplicate);
}
return builder.build();
}

private static BatchReport.Duplicate createInnerDuplicate(int line) {
return BatchReport.Duplicate.newBuilder()
.setRange(singleLineTextRange(line))
.build();
}

private static BatchReport.Duplicate createInProjectDuplicate(int componentRef, int line) {
return BatchReport.Duplicate.newBuilder()
.setOtherFileRef(componentRef)
.setRange(singleLineTextRange(line))
.build();
}

private static BatchReport.TextRange singleLineTextRange(int line) {
return BatchReport.TextRange.newBuilder()
.setStartLine(line)
.setEndLine(line)
.build();
}

private void assertNoDuplication(int fileRef) {
assertThat(duplicationRepository.getDuplications(fileRef)).isEmpty();
}
}

+ 22
- 33
server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistDuplicationsStepTest.java View File

@@ -28,19 +28,18 @@ import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.utils.System2;
import org.sonar.batch.protocol.output.BatchReport;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.metric.MetricDto;
import org.sonar.server.computation.batch.BatchReportReaderRule;
import org.sonar.server.computation.batch.TreeRootHolderRule;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.DbIdsRepositoryImpl;
import org.sonar.server.computation.component.ReportComponent;
import org.sonar.server.computation.duplication.DuplicationRepositoryRule;
import org.sonar.server.computation.duplication.TextBlock;
import org.sonar.test.DbTests;

import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;

@Category(DbTests.class)
@@ -56,12 +55,10 @@ public class PersistDuplicationsStepTest extends BaseStepTest {
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);
@Rule
public BatchReportReaderRule reportReader = new BatchReportReaderRule();
public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);

DbIdsRepositoryImpl dbIdsRepository = new DbIdsRepositoryImpl();

DbSession session = dbTester.getSession();

DbClient dbClient = dbTester.getDbClient();

PersistDuplicationsStep underTest;
@@ -69,7 +66,7 @@ public class PersistDuplicationsStepTest extends BaseStepTest {
@Before
public void setup() {
dbTester.truncateTables();
underTest = new PersistDuplicationsStep(dbClient, dbIdsRepository, treeRootHolder, reportReader);
underTest = new PersistDuplicationsStep(dbClient, dbIdsRepository, treeRootHolder, duplicationRepository);
}

@Override
@@ -97,19 +94,7 @@ public class PersistDuplicationsStepTest extends BaseStepTest {
MetricDto duplicationMetric = saveDuplicationMetric();
initReportWithProjectAndFile();

BatchReport.Duplication duplication = BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(1)
.setEndLine(5)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setRange(BatchReport.TextRange.newBuilder()
.setStartLine(6)
.setEndLine(10)
.build())
.build())
.build();
reportReader.putDuplications(FILE_1_REF, newArrayList(duplication));
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 5), new TextBlock(6, 10));

underTest.execute();

@@ -126,19 +111,7 @@ public class PersistDuplicationsStepTest extends BaseStepTest {
saveDuplicationMetric();
initReportWithProjectAndFile();

BatchReport.Duplication duplication = BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(1)
.setEndLine(5)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setOtherFileRef(FILE_2_REF).setRange(BatchReport.TextRange.newBuilder()
.setStartLine(6)
.setEndLine(10)
.build())
.build())
.build();
reportReader.putDuplications(FILE_1_REF, newArrayList(duplication));
duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 5), 3, new TextBlock(6, 10));

underTest.execute();

@@ -149,6 +122,22 @@ public class PersistDuplicationsStepTest extends BaseStepTest {
assertThat(dto.get("textValue")).isEqualTo("<duplications><g><b s=\"1\" l=\"5\" r=\"PROJECT_KEY:file\"/><b s=\"6\" l=\"5\" r=\"PROJECT_KEY:file2\"/></g></duplications>");
}

@Test
public void persist_duplications_on_different_projects() {
saveDuplicationMetric();
initReportWithProjectAndFile();

duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 5), "PROJECT2_KEY:file2", new TextBlock(6, 10));

underTest.execute();

assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1);

Map<String, Object> dto = dbTester.selectFirst("select snapshot_id as \"snapshotId\", text_value as \"textValue\" from project_measures");
assertThat(dto.get("snapshotId")).isEqualTo(11L);
assertThat(dto.get("textValue")).isEqualTo("<duplications><g><b s=\"1\" l=\"5\" r=\"PROJECT_KEY:file\"/><b s=\"6\" l=\"5\" r=\"PROJECT2_KEY:file2\"/></g></duplications>");
}

private void initReportWithProjectAndFile() {
Component file1 = ReportComponent.builder(Component.Type.FILE, FILE_1_REF).setUuid("BCDE").setKey("PROJECT_KEY:file").build();
Component file2 = ReportComponent.builder(Component.Type.FILE, FILE_2_REF).setUuid("CDEF").setKey("PROJECT_KEY:file2").build();

+ 6
- 14
server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java View File

@@ -38,6 +38,8 @@ import org.sonar.server.computation.batch.BatchReportReaderRule;
import org.sonar.server.computation.batch.TreeRootHolderRule;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.ReportComponent;
import org.sonar.server.computation.duplication.DuplicationRepositoryRule;
import org.sonar.server.computation.duplication.TextBlock;
import org.sonar.server.computation.scm.Changeset;
import org.sonar.server.computation.scm.ScmInfoRepositoryRule;
import org.sonar.server.computation.source.SourceLinesRepositoryRule;
@@ -72,6 +74,8 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
public ScmInfoRepositoryRule scmInfoRepository = new ScmInfoRepositoryRule();
@Rule
public SourceLinesRepositoryRule fileSourceRepository = new SourceLinesRepositoryRule();
@Rule
public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);

private DbClient dbClient = dbTester.getDbClient();
private DbSession session = dbTester.getSession();
@@ -82,7 +86,7 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
public void setup() {
dbTester.truncateTables();
when(system2.now()).thenReturn(NOW);
underTest = new PersistFileSourcesStep(dbClient, system2, treeRootHolder, reportReader, fileSourceRepository, scmInfoRepository);
underTest = new PersistFileSourcesStep(dbClient, system2, treeRootHolder, reportReader, fileSourceRepository, scmInfoRepository, duplicationRepository);
}

@Override
@@ -238,19 +242,7 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
public void persist_duplication() {
initBasicReport(1);

reportReader.putDuplications(FILE_REF, newArrayList(
BatchReport.Duplication.newBuilder()
.setOriginPosition(BatchReport.TextRange.newBuilder()
.setStartLine(1)
.setEndLine(2)
.build())
.addDuplicate(BatchReport.Duplicate.newBuilder()
.setRange(BatchReport.TextRange.newBuilder()
.setStartLine(3)
.setEndLine(4)
.build())
.build())
.build()));
duplicationRepository.addDuplication(FILE_REF, new TextBlock(1, 2), new TextBlock(3, 4));

underTest.execute();


Loading…
Cancel
Save