From 9c311c559136c62930e6463c525dd735b3ecac16 Mon Sep 17 00:00:00 2001
From: =?utf8?q?S=C3=A9bastien=20Lesaint?=
Date: Wed, 16 Dec 2015 17:37:20 +0100
Subject: [PATCH] SONAR-6990 strongly reduce HEAP usage of the
DuplicationRepository
---
.../benchmark/PersistFileSourcesStepTest.java | 14 +-
.../duplication/DuplicationRepository.java | 45 +--
.../DuplicationRepositoryImpl.java | 353 +----------------
.../IntegrateCrossProjectDuplications.java | 59 ++-
.../source/DuplicationLineReader.java | 13 +-
.../step/DuplicationDataMeasuresStep.java | 6 +-
.../step/DuplicationMeasuresStep.java | 5 +-
.../step/LoadDuplicationsFromReportStep.java | 42 +-
.../DuplicationRepositoryImplTest.java | 362 ++----------------
.../DuplicationRepositoryRule.java | 116 ++++--
...IntegrateCrossProjectDuplicationsTest.java | 64 ++--
.../LoadDuplicationsFromReportStepTest.java | 2 +-
.../step/PersistFileSourcesStepTest.java | 9 +-
.../ReportDuplicationMeasuresStepTest.java | 11 +-
14 files changed, 285 insertions(+), 816 deletions(-)
diff --git a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java
index 722b99b9238..8cfa29b8274 100644
--- a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java
+++ b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java
@@ -23,6 +23,7 @@ package org.sonar.server.benchmark;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.junit.Rule;
@@ -43,7 +44,10 @@ 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.Duplicate;
+import org.sonar.server.computation.duplication.Duplication;
import org.sonar.server.computation.duplication.DuplicationRepositoryRule;
+import org.sonar.server.computation.duplication.InnerDuplicate;
import org.sonar.server.computation.duplication.TextBlock;
import org.sonar.server.computation.scm.ScmInfoRepositoryImpl;
import org.sonar.server.computation.source.SourceHashRepositoryImpl;
@@ -141,9 +145,13 @@ 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));
-
+ duplicationRepository.add(
+ fileRef,
+ new Duplication(
+ new TextBlock(line, line),
+ Arrays.asList(new InnerDuplicate(new TextBlock(line + 1, line + 1)))
+ )
+ );
}
writer.writeComponent(BatchReport.Component.newBuilder()
.setRef(fileRef)
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/DuplicationRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/DuplicationRepository.java
index 0bfc05a004a..a3f1721b3f3 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/DuplicationRepository.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/DuplicationRepository.java
@@ -19,7 +19,6 @@
*/
package org.sonar.server.computation.duplication;
-import java.util.Set;
import org.sonar.server.computation.component.Component;
/**
@@ -34,58 +33,20 @@ import org.sonar.server.computation.component.Component;
*
*/
public interface DuplicationRepository {
-
/**
* Returns the duplications in the specified file {@link Component}, if any.
*
* @throws NullPointerException if {@code file} is {@code null}
* @throws IllegalArgumentException if the type of the {@link Component} argument is not {@link Component.Type#FILE}
*/
- Set getDuplications(Component file);
+ Iterable getDuplications(Component file);
/**
- * Adds a project duplication of the specified original {@link TextBlock} in the specified file {@link Component}
- * which is duplicated by the specified duplicate {@link TextBlock} in the same file.
- *
- * This method can be called multiple times with the same {@code file} and {@code original} but a different
- * {@code duplicate} to add multiple duplications of the same block.
- *
- *
- * It must not, however, be called twice with {@code original} and {@code duplicate} swapped, this would raise
- * an {@link IllegalArgumentException} as the duplication already exists.
- *
+ * Adds a project duplication in the specified file {@link Component} to the repository.
*
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if the type of the {@link Component} argument is not {@link Component.Type#FILE}
- * @throws IllegalStateException if {@code original} and {@code duplicate} are the same
- * @throws IllegalStateException if the specified duplication already exists in the repository
- */
- void addDuplication(Component file, TextBlock original, TextBlock duplicate);
-
- /**
- * Adds a project duplication of the specified original {@link TextBlock} in the specified file {@link Component} as
- * duplicated by the duplicate {@link TextBlock} in the other file {@link Component}.
- *
- * Note: the reverse duplication relationship between files is not added automatically (which leaves open the
- * possibility of inconsistent duplication information). This means that it is the responsibility of the repository's
- * user to call this method again with the {@link Component} arguments and the {@link TextBlock} arguments swapped.
- *
- *
- * @throws NullPointerException if any argument is {@code null}
- * @throws IllegalArgumentException if the type of any of the {@link Component} arguments is not {@link Component.Type#FILE}
- * @throws IllegalArgumentException if {@code file} and {@code otherFile} are the same
- * @throws IllegalStateException if the specified duplication already exists in the repository
*/
- void addDuplication(Component file, TextBlock original, Component otherFile, TextBlock duplicate);
+ void add(Component file, Duplication duplication);
- /**
- * Adds a cross-project duplication of the specified original {@link TextBlock} in the specified file {@link Component},
- * as duplicated by the specified duplicate {@link TextBlock} in a file of another project identified by its key.
- *
- * @throws NullPointerException if any argument is {@code null}
- * @throws IllegalArgumentException if the type of the {@link Component} argument is not {@link Component.Type#FILE}
- * @throws IllegalArgumentException if {@code otherFileKey} is the key of a file in the project
- * @throws IllegalStateException if the specified duplication already exists in the repository
- */
- void addDuplication(Component file, TextBlock original, String otherFileKey, TextBlock duplicate);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/DuplicationRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/DuplicationRepositoryImpl.java
index bd3eeb32363..8659661d5fc 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/DuplicationRepositoryImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/DuplicationRepositoryImpl.java
@@ -19,369 +19,44 @@
*/
package org.sonar.server.computation.duplication;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
import org.sonar.server.computation.component.Component;
-import org.sonar.server.computation.component.TreeRootHolder;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.FluentIterable.from;
import static java.util.Objects.requireNonNull;
/**
* In-memory implementation of {@link DuplicationRepository}.
*/
public class DuplicationRepositoryImpl implements DuplicationRepository {
- private final TreeRootHolder treeRootHolder;
- private final Map duplicationsByComponentUuid = new HashMap<>();
-
- public DuplicationRepositoryImpl(TreeRootHolder treeRootHolder) {
- this.treeRootHolder = treeRootHolder;
- }
-
- @Override
- public Set getDuplications(Component file) {
- checkFileComponentArgument(file);
-
- Duplications duplications = duplicationsByComponentUuid.get(file.getUuid());
- if (duplications != null) {
- return from(duplications.getDuplicates())
- .transform(DuplicatesEntryToDuplication.INSTANCE)
- .toSet();
- }
- return Collections.emptySet();
- }
-
- @Override
- public void addDuplication(Component file, TextBlock original, TextBlock duplicate) {
- checkFileComponentArgument(file);
- checkOriginalTextBlockArgument(original);
- checkDuplicateTextBlockArgument(duplicate);
- checkArgument(!original.equals(duplicate), "original and duplicate TextBlocks can not be the same");
-
- Optional duplicates = getDuplicates(file, original);
- if (duplicates.isPresent()) {
- checkDuplicationAlreadyExists(duplicates, file, original, duplicate);
- checkReverseDuplicationAlreadyExists(file, original, duplicate);
-
- duplicates.get().add(duplicate);
- } else {
- checkReverseDuplicationAlreadyExists(file, original, duplicate);
- getOrCreate(file, original).add(duplicate);
- }
- }
+ private Multimap duplications = HashMultimap.create();
@Override
- public void addDuplication(Component file, TextBlock original, Component otherFile, TextBlock duplicate) {
+ public Iterable getDuplications(Component file) {
checkFileComponentArgument(file);
- checkOriginalTextBlockArgument(original);
- checkComponentArgument(otherFile, "otherFile");
- checkDuplicateTextBlockArgument(duplicate);
- checkArgument(!file.equals(otherFile), "file and otherFile Components can not be the same");
- Optional duplicates = getDuplicates(file, original);
- if (duplicates.isPresent()) {
- checkDuplicationAlreadyExists(duplicates, file, original, otherFile, duplicate);
-
- duplicates.get().add(otherFile, duplicate);
- } else {
- getOrCreate(file, original).add(otherFile, duplicate);
+ Collection duplications = this.duplications.asMap().get(file.getKey());
+ if (duplications == null) {
+ return Collections.emptyList();
}
+ return duplications;
}
@Override
- public void addDuplication(Component file, TextBlock original, String otherFileKey, TextBlock duplicate) {
+ public void add(Component file, Duplication duplication) {
checkFileComponentArgument(file);
- checkOriginalTextBlockArgument(original);
- requireNonNull(otherFileKey, "otherFileKey can not be null");
- checkDuplicateTextBlockArgument(duplicate);
- checkArgument(!treeRootHolder.hasComponentWithKey(otherFileKey), "otherFileKey '%s' can not be the key of a Component in the project", otherFileKey);
-
- Optional duplicates = getDuplicates(file, original);
- if (duplicates.isPresent()) {
- checkDuplicationAlreadyExists(duplicates, file, original, otherFileKey, duplicate);
-
- duplicates.get().add(otherFileKey, duplicate);
- } else {
- getOrCreate(file, original).add(otherFileKey, duplicate);
- }
- }
-
- private Optional getDuplicates(Component file, TextBlock original) {
- Duplications duplications = duplicationsByComponentUuid.get(file.getUuid());
- if (duplications == null) {
- return Optional.absent();
- }
- return duplications.get(original);
- }
-
- private void checkDuplicationAlreadyExists(Optional duplicates, Component file, TextBlock original, TextBlock duplicate) {
- checkArgument(!duplicates.get().hasDuplicate(duplicate),
- "Inner duplicate %s is already associated to original %s in file %s", duplicate, original, file.getKey());
- }
-
- private void checkReverseDuplicationAlreadyExists(Component file, TextBlock original, TextBlock duplicate) {
- Optional reverseDuplication = getDuplicates(file, duplicate);
- if (reverseDuplication.isPresent()) {
- checkArgument(!reverseDuplication.get().hasDuplicate(original),
- "Inner duplicate %s is already associated to original %s in file %s", duplicate, original, file.getKey());
- }
- }
-
- private static void checkDuplicationAlreadyExists(Optional duplicates, Component file, TextBlock original, Component otherFile, TextBlock duplicate) {
- checkArgument(!duplicates.get().hasDuplicate(otherFile, duplicate),
- "In-project duplicate %s in file %s is already associated to original %s in file %s", duplicate, otherFile.getKey(), original, file.getKey());
- }
-
- private static void checkDuplicationAlreadyExists(Optional duplicates, Component file, TextBlock original, String otherFileKey, TextBlock duplicate) {
- checkArgument(!duplicates.get().hasDuplicate(otherFileKey, duplicate),
- "Cross-project duplicate %s in file %s is already associated to original %s in file %s", duplicate, otherFileKey, original, file.getKey());
- }
-
- private Duplicates getOrCreate(Component file, TextBlock original) {
- Duplications duplications = duplicationsByComponentUuid.get(file.getUuid());
- if (duplications == null) {
- duplications = new Duplications();
- duplicationsByComponentUuid.put(file.getUuid(), duplications);
- }
-
- return duplications.getOrCreate(original);
- }
-
- /**
- * Represents the location of the file of one or more duplicate {@link TextBlock}.
- */
- private interface DuplicateLocation {
-
- }
-
- private enum InnerDuplicationLocation implements DuplicateLocation {
- INSTANCE
- }
-
- @Immutable
- private static final class InProjectDuplicationLocation implements DuplicateLocation {
- private final Component component;
-
- public InProjectDuplicationLocation(Component component) {
- this.component = component;
- }
-
- public Component getComponent() {
- return component;
- }
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- InProjectDuplicationLocation that = (InProjectDuplicationLocation) o;
- return component.equals(that.component);
- }
-
- @Override
- public int hashCode() {
- return component.hashCode();
- }
- }
-
- @Immutable
- private static final class CrossProjectDuplicationLocation implements DuplicateLocation {
- private final String fileKey;
-
- private CrossProjectDuplicationLocation(String fileKey) {
- this.fileKey = fileKey;
- }
-
- public String getFileKey() {
- return fileKey;
- }
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- CrossProjectDuplicationLocation that = (CrossProjectDuplicationLocation) o;
- return fileKey.equals(that.fileKey);
- }
-
- @Override
- public int hashCode() {
- return fileKey.hashCode();
- }
- }
-
- private static final class Duplications {
- private final Map duplicatesByTextBlock = new HashMap<>();
-
- public Duplicates getOrCreate(TextBlock textBlock) {
- Duplicates duplicates = duplicatesByTextBlock.get(textBlock);
- if (duplicates != null) {
- return duplicates;
- }
- Duplicates res = new Duplicates();
- duplicatesByTextBlock.put(textBlock, res);
- return res;
- }
-
- public Set> getDuplicates() {
- return duplicatesByTextBlock.entrySet();
- }
-
- public Optional get(TextBlock original) {
- return Optional.fromNullable(duplicatesByTextBlock.get(original));
- }
- }
-
- private static final class Duplicates {
- private final Map> duplicatesByLocation = new HashMap<>();
-
- public Iterable getDuplicates() {
- return from(duplicatesByLocation.entrySet())
- .transformAndConcat(MapEntryToDuplicateWithLocation.INSTANCE);
- }
-
- public void add(TextBlock duplicate) {
- add(InnerDuplicationLocation.INSTANCE, duplicate);
- }
-
- public void add(Component otherFile, TextBlock duplicate) {
- InProjectDuplicationLocation key = new InProjectDuplicationLocation(otherFile);
- add(key, duplicate);
- }
-
- public void add(String otherFileKey, TextBlock duplicate) {
- CrossProjectDuplicationLocation key = new CrossProjectDuplicationLocation(otherFileKey);
- add(key, duplicate);
- }
-
- private void add(DuplicateLocation duplicateLocation, TextBlock duplicate) {
- Set textBlocks = duplicatesByLocation.get(duplicateLocation);
- if (textBlocks == null) {
- textBlocks = new HashSet<>(1);
- duplicatesByLocation.put(duplicateLocation, textBlocks);
- }
- textBlocks.add(duplicate);
- }
-
- public boolean hasDuplicate(TextBlock duplicate) {
- return containsEntry(InnerDuplicationLocation.INSTANCE, duplicate);
- }
-
- public boolean hasDuplicate(Component otherFile, TextBlock duplicate) {
- return containsEntry(new InProjectDuplicationLocation(otherFile), duplicate);
- }
-
- public boolean hasDuplicate(String otherFileKey, TextBlock duplicate) {
- return containsEntry(new CrossProjectDuplicationLocation(otherFileKey), duplicate);
- }
-
- private boolean containsEntry(DuplicateLocation duplicateLocation, TextBlock duplicate) {
- Set textBlocks = duplicatesByLocation.get(duplicateLocation);
- return textBlocks != null && textBlocks.contains(duplicate);
- }
-
- private enum MapEntryToDuplicateWithLocation implements Function>, Iterable> {
- INSTANCE;
-
- @Override
- @Nonnull
- public Iterable apply(@Nonnull final Map.Entry> entry) {
- return from(entry.getValue())
- .transform(new Function() {
- @Override
- @Nonnull
- public DuplicateWithLocation apply(@Nonnull TextBlock input) {
- return new DuplicateWithLocation(entry.getKey(), input);
- }
- });
- }
- }
- }
-
- @Immutable
- private static final class DuplicateWithLocation {
- private final DuplicateLocation location;
- private final TextBlock duplicate;
-
- private DuplicateWithLocation(DuplicateLocation location, TextBlock duplicate) {
- this.location = location;
- this.duplicate = duplicate;
- }
-
- public DuplicateLocation getLocation() {
- return location;
- }
-
- public TextBlock getDuplicate() {
- return duplicate;
- }
+ checkNotNull(duplication, "duplication can not be null");
+ duplications.put(file.getKey(), duplication);
}
private static void checkFileComponentArgument(Component file) {
- checkComponentArgument(file, "file");
- }
-
- private static void checkComponentArgument(Component file, String argName) {
- checkNotNull(file, "%s can not be null", argName);
- checkArgument(file.getType() == Component.Type.FILE, "type of %s argument must be FILE", argName);
- }
-
- private static void checkDuplicateTextBlockArgument(TextBlock duplicate) {
- requireNonNull(duplicate, "duplicate can not be null");
- }
-
- private static void checkOriginalTextBlockArgument(TextBlock original) {
- requireNonNull(original, "original can not be null");
+ requireNonNull(file, "file can not be null");
+ checkArgument(file.getType() == Component.Type.FILE, "type of file must be FILE");
}
- private enum DuplicatesEntryToDuplication implements Function, Duplication> {
- INSTANCE;
-
- @Override
- @Nonnull
- public Duplication apply(@Nonnull Map.Entry entry) {
- return new Duplication(
- entry.getKey(),
- from(entry.getValue().getDuplicates()).transform(DuplicateLocationEntryToDuplicate.INSTANCE));
- }
-
- private enum DuplicateLocationEntryToDuplicate implements Function {
- INSTANCE;
-
- @Override
- @Nonnull
- public Duplicate apply(@Nonnull DuplicateWithLocation input) {
- DuplicateLocation duplicateLocation = input.getLocation();
- if (duplicateLocation instanceof InnerDuplicationLocation) {
- return new InnerDuplicate(input.getDuplicate());
- }
- if (duplicateLocation instanceof InProjectDuplicationLocation) {
- return new InProjectDuplicate(((InProjectDuplicationLocation) duplicateLocation).getComponent(), input.getDuplicate());
- }
- if (duplicateLocation instanceof CrossProjectDuplicationLocation) {
- return new CrossProjectDuplicate(((CrossProjectDuplicationLocation) duplicateLocation).getFileKey(), input.getDuplicate());
- }
- throw new IllegalArgumentException("Unsupported DuplicationLocation type " + duplicateLocation.getClass());
- }
- }
- }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/IntegrateCrossProjectDuplications.java b/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/IntegrateCrossProjectDuplications.java
index efee7c5193d..2b2291a8685 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/IntegrateCrossProjectDuplications.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/duplication/IntegrateCrossProjectDuplications.java
@@ -20,7 +20,9 @@
package org.sonar.server.computation.duplication;
+import com.google.common.base.Function;
import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -91,21 +93,23 @@ public class IntegrateCrossProjectDuplications {
private void addDuplication(Component file, CloneGroup duplication) {
ClonePart originPart = duplication.getOriginPart();
- TextBlock originTextBlock = new TextBlock(originPart.getStartLine(), originPart.getEndLine());
- int clonePartCount = 0;
- for (ClonePart part : from(duplication.getCloneParts())
- .filter(new DoesNotMatchSameComponentKey(originPart.getResourceId()))) {
- clonePartCount++;
- if (clonePartCount > MAX_CLONE_PART_PER_GROUP) {
- LOGGER.warn("Too many duplication references on file {} for block at line {}. Keeping only the first {} references.",
- file.getKey(), originPart.getStartLine(), MAX_CLONE_PART_PER_GROUP);
- break;
- }
- duplicationRepository.addDuplication(file, originTextBlock, part.getResourceId(),
- new TextBlock(part.getStartLine(), part.getEndLine()));
+ Iterable duplicates = convertClonePartsToDuplicates(file, duplication);
+ if (!Iterables.isEmpty(duplicates)) {
+ duplicationRepository.add(
+ file,
+ new Duplication(new TextBlock(originPart.getStartLine(), originPart.getEndLine()), duplicates)
+ );
}
}
+ private static Iterable convertClonePartsToDuplicates(final Component file, CloneGroup duplication) {
+ final ClonePart originPart = duplication.getOriginPart();
+ return from(duplication.getCloneParts())
+ .filter(new DoesNotMatchSameComponentKey(originPart.getResourceId()))
+ .filter(new DuplicateLimiter(file, originPart))
+ .transform(ClonePartToCrossProjectDuplicate.INSTANCE);
+ }
+
private NumberOfUnitsNotLessThan getNumberOfUnitsNotLessThan(String language) {
NumberOfUnitsNotLessThan numberOfUnitsNotLessThan = numberOfUnitsByLanguage.get(language);
if (numberOfUnitsNotLessThan == null) {
@@ -153,4 +157,35 @@ public class IntegrateCrossProjectDuplications {
}
}
+ private static class DuplicateLimiter implements Predicate {
+ private final Component file;
+ private final ClonePart originPart;
+ private int counter = 0;
+
+ public DuplicateLimiter(Component file, ClonePart originPart) {
+ this.file = file;
+ this.originPart = originPart;
+ }
+
+ @Override
+ public boolean apply(@Nonnull ClonePart input) {
+ if (counter == MAX_CLONE_PART_PER_GROUP) {
+ LOGGER.warn("Too many duplication references on file {} for block at line {}. Keeping only the first {} references.",
+ file.getKey(), originPart.getStartLine(), MAX_CLONE_PART_PER_GROUP);
+ }
+ return counter++ <= MAX_CLONE_GROUP_PER_FILE;
+ }
+ }
+
+ private enum ClonePartToCrossProjectDuplicate implements Function {
+ INSTANCE;
+
+ @Override
+ @Nonnull
+ public Duplicate apply(@Nonnull ClonePart input) {
+ return new CrossProjectDuplicate(
+ input.getResourceId(),
+ new TextBlock(input.getStartLine(), input.getEndLine()));
+ }
+ }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/source/DuplicationLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/source/DuplicationLineReader.java
index a6a888731a4..2c655572357 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/source/DuplicationLineReader.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/source/DuplicationLineReader.java
@@ -24,11 +24,9 @@ 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.List;
import java.util.Map;
-import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.sonar.db.protobuf.DbFileSources;
@@ -37,12 +35,13 @@ import org.sonar.server.computation.duplication.InnerDuplicate;
import org.sonar.server.computation.duplication.TextBlock;
import static com.google.common.collect.FluentIterable.from;
+import static com.google.common.collect.Iterables.size;
public class DuplicationLineReader implements LineReader {
private final Map duplicatedTextBlockIndexByTextBlock;
- public DuplicationLineReader(Set duplications) {
+ public DuplicationLineReader(Iterable duplications) {
this.duplicatedTextBlockIndexByTextBlock = createIndexOfDuplicatedTextBlocks(duplications);
}
@@ -70,7 +69,7 @@ public class DuplicationLineReader implements LineReader {
* index. It avoids false detections of changes in {@link DbFileSources.Line#getDuplicationList()}.
*
*/
- private static Map createIndexOfDuplicatedTextBlocks(Collection duplications) {
+ private static Map createIndexOfDuplicatedTextBlocks(Iterable duplications) {
List duplicatedTextBlocks = extractAllDuplicatedTextBlocks(duplications);
Collections.sort(duplicatedTextBlocks);
return from(duplicatedTextBlocks)
@@ -84,10 +83,10 @@ public class DuplicationLineReader implements LineReader {
* The returned list is mutable on purpose because it will be sorted.
*
*
- * @see {@link #createIndexOfDuplicatedTextBlocks(Collection)}
+ * @see {@link #createIndexOfDuplicatedTextBlocks(Iterable)}
*/
- private static List extractAllDuplicatedTextBlocks(Collection duplications) {
- List duplicatedBlock = new ArrayList<>(duplications.size());
+ private static List extractAllDuplicatedTextBlocks(Iterable duplications) {
+ List duplicatedBlock = new ArrayList<>(size(duplications));
for (Duplication duplication : duplications) {
duplicatedBlock.add(duplication.getOriginal());
for (InnerDuplicate duplicate : from(duplication.getDuplicates()).filter(InnerDuplicate.class)) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/DuplicationDataMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/DuplicationDataMeasuresStep.java
index 0fbe43ff660..4cba9340e92 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/DuplicationDataMeasuresStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/DuplicationDataMeasuresStep.java
@@ -20,7 +20,6 @@
package org.sonar.server.computation.step;
-import java.util.Set;
import org.apache.commons.lang.StringEscapeUtils;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.CrawlerDepthLimit;
@@ -39,6 +38,7 @@ import org.sonar.server.computation.measure.MeasureRepository;
import org.sonar.server.computation.metric.Metric;
import org.sonar.server.computation.metric.MetricRepository;
+import static com.google.common.collect.Iterables.isEmpty;
import static org.sonar.api.measures.CoreMetrics.DUPLICATIONS_DATA_KEY;
import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER;
@@ -75,8 +75,8 @@ public class DuplicationDataMeasuresStep implements ComputationStep {
@Override
public void visitFile(Component file) {
- Set duplications = duplicationRepository.getDuplications(file);
- if (!duplications.isEmpty()) {
+ Iterable duplications = duplicationRepository.getDuplications(file);
+ if (!isEmpty(duplications)) {
computeDuplications(file, duplications);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/DuplicationMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/DuplicationMeasuresStep.java
index 897d7f231b7..d7b448510d9 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/DuplicationMeasuresStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/DuplicationMeasuresStep.java
@@ -45,6 +45,7 @@ import org.sonar.server.computation.metric.Metric;
import org.sonar.server.computation.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;
@@ -123,9 +124,9 @@ public class DuplicationMeasuresStep implements ComputationStep {
}
private void initializeForFile(Component file) {
- Set duplications = requireNonNull(this.duplicationRepository, "DuplicationRepository missing")
+ Iterable duplications = requireNonNull(this.duplicationRepository, "DuplicationRepository missing")
.getDuplications(file);
- if (duplications.isEmpty()) {
+ if (isEmpty(duplications)) {
return;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStep.java
index 6d0927f9f71..540fc96fa07 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStep.java
@@ -19,6 +19,8 @@
*/
package org.sonar.server.computation.step;
+import com.google.common.base.Function;
+import javax.annotation.Nonnull;
import org.sonar.batch.protocol.output.BatchReport;
import org.sonar.core.util.CloseableIterator;
import org.sonar.server.computation.batch.BatchReportReader;
@@ -28,9 +30,15 @@ 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.DetailedTextBlock;
+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 com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.FluentIterable.from;
import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER;
/**
@@ -72,14 +80,12 @@ public class LoadDuplicationsFromReportStep implements ComputationStep {
}
private void loadDuplications(Component file, BatchReport.Duplication duplication, int id) {
- DetailedTextBlock original = convert(duplication.getOriginPosition(), id);
- for (BatchReport.Duplicate duplicate : duplication.getDuplicateList()) {
- if (duplicate.hasOtherFileRef()) {
- duplicationRepository.addDuplication(file, original, treeRootHolder.getComponentByRef(duplicate.getOtherFileRef()), convert(duplicate.getRange()));
- } else {
- duplicationRepository.addDuplication(file, original, convert(duplicate.getRange()));
- }
- }
+ duplicationRepository.add(file,
+ new Duplication(
+ convert(duplication.getOriginPosition(), id),
+ from(duplication.getDuplicateList())
+ .transform(new BatchDuplicateToCeDuplicate(file))
+ ));
}
private static TextBlock convert(BatchReport.TextRange textRange) {
@@ -89,4 +95,24 @@ public class LoadDuplicationsFromReportStep implements ComputationStep {
private static DetailedTextBlock convert(BatchReport.TextRange textRange, int id) {
return new DetailedTextBlock(id, textRange.getStartLine(), textRange.getEndLine());
}
+
+ private class BatchDuplicateToCeDuplicate implements Function {
+ private final Component file;
+
+ private BatchDuplicateToCeDuplicate(Component file) {
+ this.file = file;
+ }
+
+ @Override
+ @Nonnull
+ public Duplicate apply(@Nonnull BatchReport.Duplicate input) {
+ if (input.hasOtherFileRef()) {
+ checkArgument(input.getOtherFileRef() != file.getReportAttributes().getRef(), "file and otherFile references can not be the same");
+ return new InProjectDuplicate(
+ treeRootHolder.getComponentByRef(input.getOtherFileRef()),
+ convert(input.getRange()));
+ }
+ return new InnerDuplicate(convert(input.getRange()));
+ }
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/DuplicationRepositoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/DuplicationRepositoryImplTest.java
index 65cab2715b5..72e4723aab4 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/DuplicationRepositoryImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/DuplicationRepositoryImplTest.java
@@ -23,12 +23,10 @@ import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Arrays;
-import java.util.Set;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
-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.util.WrapInSingleElementArray;
@@ -36,30 +34,20 @@ import org.sonar.server.util.WrapInSingleElementArray;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.FluentIterable.from;
-import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(DataProviderRunner.class)
public class DuplicationRepositoryImplTest {
- @Rule
- public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
private static final Component FILE_COMPONENT_1 = ReportComponent.builder(Component.Type.FILE, 1).build();
private static final Component FILE_COMPONENT_2 = ReportComponent.builder(Component.Type.FILE, 2).build();
- private static final Component FILE_COMPONENT_3 = ReportComponent.builder(Component.Type.FILE, 3).build();
- private static final TextBlock ORIGINAL_TEXTBLOCK = new TextBlock(1, 2);
- private static final TextBlock COPY_OF_ORIGINAL_TEXTBLOCK = new TextBlock(1, 2);
- private static final TextBlock DUPLICATE_TEXTBLOCK_1 = new TextBlock(15, 15);
- private static final TextBlock DUPLICATE_TEXTBLOCK_2 = new TextBlock(15, 16);
- private static final String FILE_KEY_1 = "1";
- private static final String FILE_KEY_2 = "2";
+ private static final Duplication SOME_DUPLICATION = createDuplication(1, 2);
@Rule
public ExpectedException expectedException = ExpectedException.none();
- private DuplicationRepository underTest = new DuplicationRepositoryImpl(treeRootHolder);
+ private DuplicationRepository underTest = new DuplicationRepositoryImpl();
@Test
public void getDuplications_throws_NPE_if_Component_argument_is_null() {
@@ -84,24 +72,18 @@ public class DuplicationRepositoryImplTest {
}
@Test
- public void addDuplication_inner_throws_NPE_if_file_argument_is_null() {
+ public void add_throws_NPE_if_file_argument_is_null() {
expectFileArgumentNPE();
- underTest.addDuplication(null, ORIGINAL_TEXTBLOCK, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_inner_throws_NPE_if_original_argument_is_null() {
- expectOriginalArgumentNPE();
-
- underTest.addDuplication(FILE_COMPONENT_1, null, DUPLICATE_TEXTBLOCK_1);
+ underTest.add(null, SOME_DUPLICATION);
}
@Test
- public void addDuplication_inner_throws_NPE_if_duplicate_argument_is_null() {
- expectDuplicateArgumentNPE();
+ public void addDuplication_inner_throws_NPE_if_duplication_argument_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("duplication can not be null");
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, null);
+ underTest.add(FILE_COMPONENT_1, null);
}
@Test
@@ -111,317 +93,46 @@ public class DuplicationRepositoryImplTest {
Component component = mockComponentGetType(type);
- underTest.addDuplication(component, ORIGINAL_TEXTBLOCK, DUPLICATE_TEXTBLOCK_1);
+ underTest.add(component, SOME_DUPLICATION);
}
@Test
- public void addDuplication_inner_throws_IAE_if_original_and_duplicate_are_equal() {
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("original and duplicate TextBlocks can not be the same");
+ public void added_duplication_is_returned_as_is_by_getDuplications() {
+ underTest.add(FILE_COMPONENT_1, SOME_DUPLICATION);
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, COPY_OF_ORIGINAL_TEXTBLOCK);
- }
-
- @Test
- public void addDuplication_inner_throws_IAE_if_duplication_already_exists() {
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, DUPLICATE_TEXTBLOCK_1);
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage(format(
- "Inner duplicate %s is already associated to original %s in file %s",
- DUPLICATE_TEXTBLOCK_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_1.getKey()));
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_inner_throws_IAE_if_reverse_duplication_already_exists() {
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, DUPLICATE_TEXTBLOCK_1);
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage(format(
- "Inner duplicate %s is already associated to original %s in file %s",
- ORIGINAL_TEXTBLOCK, DUPLICATE_TEXTBLOCK_1, FILE_COMPONENT_1.getKey()));
-
- underTest.addDuplication(FILE_COMPONENT_1, DUPLICATE_TEXTBLOCK_1, ORIGINAL_TEXTBLOCK);
- }
-
- @Test
- public void addDuplication_inner_throws_IAE_if_reverse_duplication_already_exists_and_duplicate_has_duplicates_of_its_own() {
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, DUPLICATE_TEXTBLOCK_1);
- underTest.addDuplication(FILE_COMPONENT_1, DUPLICATE_TEXTBLOCK_1, DUPLICATE_TEXTBLOCK_2);
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage(format(
- "Inner duplicate %s is already associated to original %s in file %s",
- ORIGINAL_TEXTBLOCK, DUPLICATE_TEXTBLOCK_1, FILE_COMPONENT_1.getKey()));
-
- underTest.addDuplication(FILE_COMPONENT_1, DUPLICATE_TEXTBLOCK_1, ORIGINAL_TEXTBLOCK);
- }
-
- @Test
- public void addDuplication_inner_is_returned_by_getDuplications() {
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, DUPLICATE_TEXTBLOCK_1);
-
- Set duplications = underTest.getDuplications(FILE_COMPONENT_1);
+ Iterable duplications = underTest.getDuplications(FILE_COMPONENT_1);
assertThat(duplications).hasSize(1);
- assertDuplication(
- duplications.iterator().next(),
- ORIGINAL_TEXTBLOCK,
- new InnerDuplicate(DUPLICATE_TEXTBLOCK_1));
+ assertThat(duplications.iterator().next()).isSameAs(SOME_DUPLICATION);
assertNoDuplication(FILE_COMPONENT_2);
}
@Test
- public void addDuplication_inner_called_multiple_times_populate_a_single_Duplication() {
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, DUPLICATE_TEXTBLOCK_2);
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, DUPLICATE_TEXTBLOCK_1);
+ public void added_duplication_does_not_avoid_same_duplication_inserted_twice_but_only_one_is_returned() {
+ underTest.add(FILE_COMPONENT_1, SOME_DUPLICATION);
+ underTest.add(FILE_COMPONENT_1, SOME_DUPLICATION);
- Set duplications = underTest.getDuplications(FILE_COMPONENT_1);
+ Iterable duplications = underTest.getDuplications(FILE_COMPONENT_1);
assertThat(duplications).hasSize(1);
- assertDuplication(
- duplications.iterator().next(),
- ORIGINAL_TEXTBLOCK,
- new InnerDuplicate(DUPLICATE_TEXTBLOCK_1), new InnerDuplicate(DUPLICATE_TEXTBLOCK_2));
+ assertThat(duplications.iterator().next()).isSameAs(SOME_DUPLICATION);
assertNoDuplication(FILE_COMPONENT_2);
}
@Test
- public void addDuplication_inProject_throws_NPE_if_file_argument_is_null() {
- expectFileArgumentNPE();
-
- underTest.addDuplication(null, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_inProject_throws_NPE_if_original_argument_is_null() {
- expectOriginalArgumentNPE();
+ public void added_duplications_are_returned_in_any_order_and_associated_to_the_right_file() {
+ underTest.add(FILE_COMPONENT_1, SOME_DUPLICATION);
+ underTest.add(FILE_COMPONENT_1, createDuplication(2, 4));
+ underTest.add(FILE_COMPONENT_1, createDuplication(2, 3));
+ underTest.add(FILE_COMPONENT_2, createDuplication(2, 3));
+ underTest.add(FILE_COMPONENT_2, createDuplication(1, 2));
- underTest.addDuplication(FILE_COMPONENT_1, null, FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_1);
+ assertThat(underTest.getDuplications(FILE_COMPONENT_1)).containsOnly(SOME_DUPLICATION, createDuplication(2, 3), createDuplication(2, 4));
+ assertThat(underTest.getDuplications(FILE_COMPONENT_2)).containsOnly(createDuplication(1, 2), createDuplication(2, 3));
}
- @Test
- public void addDuplication_inProject_throws_NPE_if_otherFile_argument_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("otherFile can not be null");
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, (Component) null, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- @UseDataProvider("allComponentTypesButFile")
- public void addDuplication_inProject_throws_NPE_if_otherFile_type_is_not_FILE(Component.Type type) {
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("type of otherFile argument must be FILE");
-
- Component component = mockComponentGetType(type);
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, component, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_inProject_throws_NPE_if_duplicate_argument_is_null() {
- expectDuplicateArgumentNPE();
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_2, null);
- }
-
- @Test
- @UseDataProvider("allComponentTypesButFile")
- public void addDuplication_inProject_throws_NPE_if_file_type_is_not_FILE(Component.Type type) {
- expectFileTypeIAE();
-
- Component component = mockComponentGetType(type);
-
- underTest.addDuplication(component, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_inProject_throws_NPE_if_file_and_otherFile_are_the_same() {
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("file and otherFile Components can not be the same");
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_1, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_inProject_throws_IAE_if_duplication_already_exists() {
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_1);
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage(format(
- "In-project duplicate %s in file %s is already associated to original %s in file %s",
- DUPLICATE_TEXTBLOCK_1, FILE_COMPONENT_2.getKey(), ORIGINAL_TEXTBLOCK, FILE_COMPONENT_1.getKey()));
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_inProject_is_returned_by_getDuplications() {
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_1);
-
- Set duplications = underTest.getDuplications(FILE_COMPONENT_1);
- assertThat(duplications).hasSize(1);
- assertDuplication(
- duplications.iterator().next(),
- ORIGINAL_TEXTBLOCK,
- new InProjectDuplicate(FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_1));
-
- assertNoDuplication(FILE_COMPONENT_2);
- }
-
- @Test
- public void addDuplication_inProject_called_multiple_times_populate_a_single_Duplication() {
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_2);
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_1);
-
- Set duplications = underTest.getDuplications(FILE_COMPONENT_1);
- assertThat(duplications).hasSize(1);
- assertDuplication(
- duplications.iterator().next(),
- ORIGINAL_TEXTBLOCK,
- new InProjectDuplicate(FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_1), new InProjectDuplicate(FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_2));
-
- assertNoDuplication(FILE_COMPONENT_2);
- }
-
- @Test
- public void addDuplication_inProject_called_multiple_times_with_different_components_populate_a_single_Duplication() {
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_2);
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_3, DUPLICATE_TEXTBLOCK_2);
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_3, DUPLICATE_TEXTBLOCK_1);
-
- Set duplications = underTest.getDuplications(FILE_COMPONENT_1);
- assertThat(duplications).hasSize(1);
- assertDuplication(
- duplications.iterator().next(),
- ORIGINAL_TEXTBLOCK,
- new InProjectDuplicate(FILE_COMPONENT_2, DUPLICATE_TEXTBLOCK_2), new InProjectDuplicate(FILE_COMPONENT_3, DUPLICATE_TEXTBLOCK_1), new InProjectDuplicate(FILE_COMPONENT_3,
- DUPLICATE_TEXTBLOCK_2));
-
- assertNoDuplication(FILE_COMPONENT_2);
- assertNoDuplication(FILE_COMPONENT_3);
- }
-
- @Test
- public void addDuplication_crossProject_throws_NPE_if_file_argument_is_null() {
- expectFileArgumentNPE();
-
- underTest.addDuplication(null, ORIGINAL_TEXTBLOCK, FILE_KEY_1, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_crossProject_throws_NPE_if_original_argument_is_null() {
- expectOriginalArgumentNPE();
-
- underTest.addDuplication(FILE_COMPONENT_1, null, FILE_KEY_1, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_crossProject_throws_NPE_if_otherFileKey_argument_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("otherFileKey can not be null");
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, (String) null, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_crossProject_throws_NPE_if_duplicate_argument_is_null() {
- expectDuplicateArgumentNPE();
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_KEY_1, null);
- }
-
- @Test
- public void addDuplication_crossProject_throws_IAE_if_otherFileKey_is_key_of_Component_in_the_project() {
- treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 999)
- .addChildren(
- FILE_COMPONENT_1, FILE_COMPONENT_2
- )
- .build());
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage(format("otherFileKey '%s' can not be the key of a Component in the project", FILE_COMPONENT_2.getKey()));
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_2.getKey(), DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- @UseDataProvider("allComponentTypesButFile")
- public void addDuplication_crossProject_throws_NPE_if_file_type_is_not_FILE(Component.Type type) {
- expectFileTypeIAE();
-
- Component component = mockComponentGetType(type);
-
- underTest.addDuplication(component, ORIGINAL_TEXTBLOCK, FILE_KEY_1, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_crossProject_throws_IAE_if_duplication_already_exists() {
- treeRootHolder.setRoot(FILE_COMPONENT_1);
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_KEY_1, DUPLICATE_TEXTBLOCK_1);
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage(format(
- "Cross-project duplicate %s in file %s is already associated to original %s in file %s",
- DUPLICATE_TEXTBLOCK_1, FILE_KEY_1, ORIGINAL_TEXTBLOCK, FILE_COMPONENT_1.getKey()));
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_KEY_1, DUPLICATE_TEXTBLOCK_1);
- }
-
- @Test
- public void addDuplication_crossProject_is_returned_by_getDuplications() {
- treeRootHolder.setRoot(FILE_COMPONENT_1);
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_KEY_1, DUPLICATE_TEXTBLOCK_1);
-
- Set duplications = underTest.getDuplications(FILE_COMPONENT_1);
- assertThat(duplications).hasSize(1);
- assertDuplication(
- duplications.iterator().next(),
- ORIGINAL_TEXTBLOCK,
- new CrossProjectDuplicate(FILE_KEY_1, DUPLICATE_TEXTBLOCK_1));
-
- assertNoDuplication(FILE_COMPONENT_2);
- }
-
- @Test
- public void addDuplication_crossProject_called_multiple_times_populate_a_single_Duplication() {
- treeRootHolder.setRoot(FILE_COMPONENT_1);
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_KEY_1, DUPLICATE_TEXTBLOCK_2);
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_KEY_1, DUPLICATE_TEXTBLOCK_1);
-
- Set duplications = underTest.getDuplications(FILE_COMPONENT_1);
- assertThat(duplications).hasSize(1);
- assertDuplication(
- duplications.iterator().next(),
- ORIGINAL_TEXTBLOCK,
- new CrossProjectDuplicate(FILE_KEY_1, DUPLICATE_TEXTBLOCK_1), new CrossProjectDuplicate(FILE_KEY_1, DUPLICATE_TEXTBLOCK_2));
-
- assertNoDuplication(FILE_COMPONENT_2);
- }
-
- @Test
- public void addDuplication_crossProject_called_multiple_times_with_different_fileKeys_populate_a_single_Duplication() {
- treeRootHolder.setRoot(FILE_COMPONENT_1);
-
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_KEY_1, DUPLICATE_TEXTBLOCK_2);
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_KEY_2, DUPLICATE_TEXTBLOCK_2);
- underTest.addDuplication(FILE_COMPONENT_1, ORIGINAL_TEXTBLOCK, FILE_KEY_2, DUPLICATE_TEXTBLOCK_1);
-
- Set duplications = underTest.getDuplications(FILE_COMPONENT_1);
- assertThat(duplications).hasSize(1);
- assertDuplication(
- duplications.iterator().next(),
- ORIGINAL_TEXTBLOCK,
- new CrossProjectDuplicate(FILE_KEY_1, DUPLICATE_TEXTBLOCK_2), new CrossProjectDuplicate(FILE_KEY_2, DUPLICATE_TEXTBLOCK_1), new CrossProjectDuplicate(FILE_KEY_2,
- DUPLICATE_TEXTBLOCK_2));
-
- assertNoDuplication(FILE_COMPONENT_2);
+ private static Duplication createDuplication(int originalLine, int duplicateLine) {
+ return new Duplication(new TextBlock(originalLine, originalLine), Arrays.asList(new InnerDuplicate(new TextBlock(duplicateLine, duplicateLine))));
}
@DataProvider
@@ -432,11 +143,6 @@ public class DuplicationRepositoryImplTest {
.toArray(Object[].class);
}
- private static void assertDuplication(Duplication duplication, TextBlock original, Duplicate... duplicates) {
- assertThat(duplication.getOriginal()).isEqualTo(original);
- assertThat(duplication.getDuplicates()).containsExactly(duplicates);
- }
-
private void assertNoDuplication(Component component) {
assertThat(underTest.getDuplications(component)).isEmpty();
}
@@ -446,19 +152,9 @@ public class DuplicationRepositoryImplTest {
expectedException.expectMessage("file can not be null");
}
- private void expectOriginalArgumentNPE() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("original can not be null");
- }
-
- private void expectDuplicateArgumentNPE() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("duplicate can not be null");
- }
-
private void expectFileTypeIAE() {
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("type of file argument must be FILE");
+ expectedException.expectMessage("type of file must be FILE");
}
private Component mockComponentGetType(Component.Type type) {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/DuplicationRepositoryRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/DuplicationRepositoryRule.java
index 9feb25dcbf8..8d8ae1d2369 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/DuplicationRepositoryRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/DuplicationRepositoryRule.java
@@ -19,7 +19,12 @@
*/
package org.sonar.server.computation.duplication;
-import java.util.Set;
+import com.google.common.base.Function;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import java.util.Arrays;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
import org.junit.rules.ExternalResource;
import org.sonar.server.computation.batch.TreeRootHolderRule;
import org.sonar.server.computation.component.Component;
@@ -27,78 +32,139 @@ import org.sonar.server.computation.component.ComponentProvider;
import org.sonar.server.computation.component.TreeRootHolder;
import org.sonar.server.computation.component.TreeRootHolderComponentProvider;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.FluentIterable.from;
+import static java.util.Objects.requireNonNull;
+
public class DuplicationRepositoryRule extends ExternalResource implements DuplicationRepository {
- private final TreeRootHolder treeRootHolder;
+ @CheckForNull
private final ComponentProvider componentProvider;
private DuplicationRepositoryImpl delegate;
+ private final Multimap componentRefsWithInnerDuplications = ArrayListMultimap.create();
+ private final Multimap componentRefsWithInProjectDuplications = ArrayListMultimap.create();
+ private final Multimap componentRefsWithCrossProjectDuplications = ArrayListMultimap.create();
private DuplicationRepositoryRule(TreeRootHolder treeRootHolder) {
- this.treeRootHolder = treeRootHolder;
this.componentProvider = new TreeRootHolderComponentProvider(treeRootHolder);
}
+ public DuplicationRepositoryRule() {
+ this.componentProvider = null;
+ }
+
public static DuplicationRepositoryRule create(TreeRootHolderRule treeRootHolder) {
return new DuplicationRepositoryRule(treeRootHolder);
}
+ public static DuplicationRepositoryRule create() {
+ return new DuplicationRepositoryRule();
+ }
+
@Override
protected void before() throws Throwable {
- this.delegate = new DuplicationRepositoryImpl(treeRootHolder);
+ this.delegate = new DuplicationRepositoryImpl();
}
@Override
protected void after() {
- this.componentProvider.reset();
+ if (this.componentProvider != null) {
+ this.componentProvider.reset();
+ }
+ this.componentRefsWithInnerDuplications.clear();
+ this.componentRefsWithInProjectDuplications.clear();
+ this.componentRefsWithCrossProjectDuplications.clear();
this.delegate = null;
}
- public Set getDuplications(int fileRef) {
- componentProvider.ensureInitialized();
+ public Iterable getDuplications(int fileRef) {
+ ensureComponentProviderInitialized();
return delegate.getDuplications(componentProvider.getByRef(fileRef));
}
- public DuplicationRepositoryRule addDuplication(int fileRef, TextBlock original, TextBlock duplicate) {
- componentProvider.ensureInitialized();
+ public void add(int fileRef, Duplication duplication) {
+ ensureComponentProviderInitialized();
+
+ delegate.add(componentProvider.getByRef(fileRef), duplication);
+ }
+
+ public DuplicationRepositoryRule addDuplication(int fileRef, TextBlock original, TextBlock... duplicates) {
+ ensureComponentProviderInitialized();
+ Component component = componentProvider.getByRef(fileRef);
+ checkArgument(!componentRefsWithInnerDuplications.containsEntry(component, original), "Inner duplications for file %s and original %s already set", fileRef, original);
+ checkArgument(!componentRefsWithInProjectDuplications.containsEntry(component, original), "InProject duplications for file %s and original %s already set. Use add(int, Duplication) instead", fileRef, original);
- delegate.addDuplication(componentProvider.getByRef(fileRef), original, duplicate);
+ componentRefsWithInnerDuplications.put(component, original);
+ delegate.add(
+ component,
+ new Duplication(
+ original,
+ from(Arrays.asList(duplicates)).transform(TextBlockToInnerDuplicate.INSTANCE))
+ );
return this;
}
public DuplicationRepositoryRule addDuplication(int fileRef, TextBlock original, int otherFileRef, TextBlock duplicate) {
- componentProvider.ensureInitialized();
-
- delegate.addDuplication(componentProvider.getByRef(fileRef), original, componentProvider.getByRef(otherFileRef), duplicate);
+ ensureComponentProviderInitialized();
+ Component component = componentProvider.getByRef(fileRef);
+ checkArgument(!componentRefsWithInProjectDuplications.containsEntry(component, original), "InProject duplications for file %s and original %s already set", fileRef, original);
+ checkArgument(!componentRefsWithInnerDuplications.containsEntry(component, original), "Inner duplications for file %s and original %s already set. Use add(int, Duplication) instead", fileRef, original);
+
+ componentRefsWithInProjectDuplications.put(component, original);
+ delegate.add(component,
+ new Duplication(
+ original,
+ Arrays.asList(new InProjectDuplicate(componentProvider.getByRef(otherFileRef), duplicate))
+ )
+ );
return this;
}
public DuplicationRepositoryRule addDuplication(int fileRef, TextBlock original, String otherFileKey, TextBlock duplicate) {
- componentProvider.ensureInitialized();
-
- delegate.addDuplication(componentProvider.getByRef(fileRef), original, otherFileKey, duplicate);
+ ensureComponentProviderInitialized();
+ Component component = componentProvider.getByRef(fileRef);
+ checkArgument(!componentRefsWithCrossProjectDuplications.containsEntry(component, original), "CrossProject duplications for file %s and original %s already set", fileRef);
+
+ componentRefsWithCrossProjectDuplications.put(component, original);
+ delegate.add(componentProvider.getByRef(fileRef),
+ new Duplication(
+ original,
+ Arrays.asList(new CrossProjectDuplicate(otherFileKey, duplicate))
+ )
+ );
return this;
}
@Override
- public Set getDuplications(Component file) {
+ public Iterable getDuplications(Component file) {
return delegate.getDuplications(file);
}
@Override
- public void addDuplication(Component file, TextBlock original, TextBlock duplicate) {
- delegate.addDuplication(file, original, duplicate);
+ public void add(Component file, Duplication duplication) {
+ TextBlock original = duplication.getOriginal();
+ checkArgument(!componentRefsWithInnerDuplications.containsEntry(file, original), "Inner duplications for file %s and original %s already set", file, original);
+ checkArgument(!componentRefsWithInProjectDuplications.containsEntry(file, original), "InProject duplications for file %s and original %s already set", file, original);
+ checkArgument(!componentRefsWithCrossProjectDuplications.containsEntry(file, original), "CrossProject duplications for file %s and original %s already set", file, original);
+
+ delegate.add(file, duplication);
}
- @Override
- public void addDuplication(Component file, TextBlock original, Component otherFile, TextBlock duplicate) {
- delegate.addDuplication(file, original, otherFile, duplicate);
+ private void ensureComponentProviderInitialized() {
+ requireNonNull(this.componentProvider, "Methods with component reference can not be used unless a TreeRootHolder has been provided when instantiating the rule");
+ this.componentProvider.ensureInitialized();
}
- @Override
- public void addDuplication(Component file, TextBlock original, String otherFileKey, TextBlock duplicate) {
- delegate.addDuplication(file, original, otherFileKey, duplicate);
+ private enum TextBlockToInnerDuplicate implements Function {
+ INSTANCE;
+
+ @Override
+ @Nonnull
+ public Duplicate apply(@Nonnull TextBlock input) {
+ return new InnerDuplicate(input);
+ }
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/IntegrateCrossProjectDuplicationsTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/IntegrateCrossProjectDuplicationsTest.java
index 557426a572d..49d0c2f2f7e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/IntegrateCrossProjectDuplicationsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/duplication/IntegrateCrossProjectDuplicationsTest.java
@@ -21,6 +21,7 @@
package org.sonar.server.computation.duplication;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import org.junit.Rule;
@@ -38,13 +39,6 @@ import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.sonar.server.computation.component.Component.Type.FILE;
import static org.sonar.server.computation.component.ReportComponent.builder;
@@ -52,6 +46,8 @@ public class IntegrateCrossProjectDuplicationsTest {
@Rule
public LogTester logTester = new LogTester();
+ @Rule
+ public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create();
static final String XOO_LANGUAGE = "xoo";
@@ -65,8 +61,6 @@ public class IntegrateCrossProjectDuplicationsTest {
Settings settings = new Settings();
- DuplicationRepository duplicationRepository = mock(DuplicationRepository.class);
-
IntegrateCrossProjectDuplications underTest = new IntegrateCrossProjectDuplications(settings, duplicationRepository);
@Test
@@ -106,11 +100,9 @@ public class IntegrateCrossProjectDuplicationsTest {
underTest.computeCpd(ORIGIN_FILE, originBlocks, duplicatedBlocks);
- verify(duplicationRepository).addDuplication(
- ORIGIN_FILE,
- new TextBlock(30, 45),
- OTHER_FILE_KEY,
- new TextBlock(40, 55)
+ assertThat(duplicationRepository.getDuplications(ORIGIN_FILE))
+ .containsExactly(
+ crossProjectDuplication(new TextBlock(30, 45), OTHER_FILE_KEY, new TextBlock(40, 55))
);
}
@@ -140,11 +132,9 @@ public class IntegrateCrossProjectDuplicationsTest {
underTest.computeCpd(ORIGIN_FILE, originBlocks, duplicatedBlocks);
- verify(duplicationRepository).addDuplication(
- ORIGIN_FILE,
- new TextBlock(30, 45),
- OTHER_FILE_KEY,
- new TextBlock(40, 55)
+ assertThat(duplicationRepository.getDuplications(ORIGIN_FILE))
+ .containsExactly(
+ crossProjectDuplication(new TextBlock(30, 45), OTHER_FILE_KEY, new TextBlock(40, 55))
);
}
@@ -181,7 +171,7 @@ public class IntegrateCrossProjectDuplicationsTest {
underTest.computeCpd(ORIGIN_FILE, originBlocks, duplicatedBlocks);
- verifyNoMoreInteractions(duplicationRepository);
+ assertNoDuplicationAdded(ORIGIN_FILE);
}
@Test
@@ -210,7 +200,7 @@ public class IntegrateCrossProjectDuplicationsTest {
underTest.computeCpd(ORIGIN_FILE, originBlocks, duplicatedBlocks);
- verifyNoMoreInteractions(duplicationRepository);
+ assertNoDuplicationAdded(ORIGIN_FILE);
}
@Test
@@ -229,7 +219,7 @@ public class IntegrateCrossProjectDuplicationsTest {
underTest.computeCpd(ORIGIN_FILE, originBlocks, Collections.emptyList());
- verifyNoMoreInteractions(duplicationRepository);
+ assertNoDuplicationAdded(ORIGIN_FILE);
}
@Test
@@ -261,11 +251,9 @@ public class IntegrateCrossProjectDuplicationsTest {
underTest.computeCpd(javaFile, originBlocks, duplicatedBlocks);
- verify(duplicationRepository).addDuplication(
- ORIGIN_FILE,
- new TextBlock(30, 45),
- OTHER_FILE_KEY,
- new TextBlock(40, 55)
+ assertThat(duplicationRepository.getDuplications(ORIGIN_FILE))
+ .containsExactly(
+ crossProjectDuplication(new TextBlock(30, 45), OTHER_FILE_KEY, new TextBlock(40, 55))
);
}
@@ -294,11 +282,9 @@ public class IntegrateCrossProjectDuplicationsTest {
underTest.computeCpd(ORIGIN_FILE, originBlocks, duplicatedBlocks);
- verify(duplicationRepository).addDuplication(
- ORIGIN_FILE,
- new TextBlock(30, 45),
- OTHER_FILE_KEY,
- new TextBlock(40, 55)
+ assertThat(duplicationRepository.getDuplications(ORIGIN_FILE))
+ .containsExactly(
+ crossProjectDuplication(new TextBlock(30, 45), OTHER_FILE_KEY, new TextBlock(40, 55))
);
}
@@ -328,7 +314,9 @@ public class IntegrateCrossProjectDuplicationsTest {
assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly(
"Too many duplication references on file " + ORIGIN_FILE_KEY + " for block at line 30. Keeping only the first 100 references.");
- verify(duplicationRepository, times(100)).addDuplication(eq(ORIGIN_FILE), any(TextBlock.class), anyString(), any(TextBlock.class));
+ Iterable duplications = duplicationRepository.getDuplications(ORIGIN_FILE);
+ assertThat(duplications).hasSize(1);
+ assertThat(duplications.iterator().next().getDuplicates()).hasSize(100);
}
@Test
@@ -359,8 +347,16 @@ public class IntegrateCrossProjectDuplicationsTest {
underTest.computeCpd(ORIGIN_FILE, originBlocks, duplicatedBlocks);
- verify(duplicationRepository, times(100)).addDuplication(eq(ORIGIN_FILE), any(TextBlock.class), anyString(), any(TextBlock.class));
+ assertThat(duplicationRepository.getDuplications(ORIGIN_FILE)).hasSize(100);
assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly("Too many duplication groups on file " + ORIGIN_FILE_KEY + ". Keeping only the first 100 groups.");
}
+ private static Duplication crossProjectDuplication(TextBlock original, String otherFileKey, TextBlock duplicate) {
+ return new Duplication(original, Arrays.asList(new CrossProjectDuplicate(otherFileKey, duplicate)));
+ }
+
+ private void assertNoDuplicationAdded(Component file) {
+ assertThat(duplicationRepository.getDuplications(file)).isEmpty();
+ }
+
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStepTest.java
index b1423876afd..42b4c918a59 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/LoadDuplicationsFromReportStepTest.java
@@ -179,7 +179,7 @@ public class LoadDuplicationsFromReportStepTest {
reportReader.putDuplications(FILE_1_REF, createDuplication(singleLineTextRange(line), createInProjectDuplicate(FILE_1_REF, line + 1)));
expectedException.expect(VisitException.class);
- expectedException.expectCause(hasType(IllegalArgumentException.class).andMessage("file and otherFile Components can not be the same"));
+ expectedException.expectCause(hasType(IllegalArgumentException.class).andMessage("file and otherFile references can not be the same"));
underTest.execute();
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java
index 83ba814824b..54749fb9011 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java
@@ -20,6 +20,7 @@
package org.sonar.server.computation.step;
+import java.util.Arrays;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -38,7 +39,10 @@ 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.Duplicate;
+import org.sonar.server.computation.duplication.Duplication;
import org.sonar.server.computation.duplication.DuplicationRepositoryRule;
+import org.sonar.server.computation.duplication.InnerDuplicate;
import org.sonar.server.computation.duplication.TextBlock;
import org.sonar.server.computation.scm.Changeset;
import org.sonar.server.computation.scm.ScmInfoRepositoryRule;
@@ -242,7 +246,10 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
public void persist_duplication() {
initBasicReport(1);
- duplicationRepository.addDuplication(FILE_REF, new TextBlock(1, 2), new TextBlock(3, 4));
+ duplicationRepository.add(
+ FILE_REF,
+ new Duplication(new TextBlock(1, 2), Arrays.asList(new InnerDuplicate(new TextBlock(3, 4))))
+ );
underTest.execute();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportDuplicationMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportDuplicationMeasuresStepTest.java
index 05f4ca5ffeb..10d6e802dbe 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportDuplicationMeasuresStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportDuplicationMeasuresStepTest.java
@@ -107,9 +107,7 @@ public class ReportDuplicationMeasuresStepTest {
@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));
- duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(3, 3));
- duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(2, 3));
+ duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(2, 2), new TextBlock(3, 3), new TextBlock(2, 3));
underTest.execute();
@@ -213,8 +211,7 @@ public class ReportDuplicationMeasuresStepTest {
@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));
- duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(11, 15));
+ 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();
@@ -454,9 +451,11 @@ public class ReportDuplicationMeasuresStepTest {
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++) {
- duplicationRepository.addDuplication(fileRef, original, new TextBlock(i, i));
+ duplicates[i - 10] = new TextBlock(i, i);
}
+ duplicationRepository.addDuplication(fileRef, original, duplicates);
}
private void addRawMeasure(int componentRef, String metricKey, int value) {
--
2.39.5