From 95764997034f8f7e3e8afda432bc87591d857a0b Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Thu, 20 Jun 2019 15:05:37 -0500 Subject: [PATCH] SONAR-11458 Log analysis warning when cross-project duplication detection is used --- .../task/projectanalysis/analysis/Branch.java | 2 +- .../duplication/Duplication.java | 66 +++++++------------ .../IntegrateCrossProjectDuplications.java | 45 +++++++------ ...rossProjectDuplicationsRepositoryStep.java | 28 ++++---- .../DuplicationRepositoryRule.java | 2 +- .../duplication/DuplicationTest.java | 15 ++--- ...IntegrateCrossProjectDuplicationsTest.java | 32 +++++---- 7 files changed, 90 insertions(+), 100 deletions(-) diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/Branch.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/Branch.java index 22c0702dd15..c4a022f0724 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/Branch.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/Branch.java @@ -49,7 +49,7 @@ public interface Branch extends ComponentKeyGenerator { String getMergeBranchUuid(); /** - * Whether the cross-project duplication tracker must be enabled + * Whether the cross-project duplication tracker can be enabled * or not. */ boolean supportsCrossProjectCpd(); diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/Duplication.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/Duplication.java index c858b2d68fc..8f129042d84 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/Duplication.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/Duplication.java @@ -19,41 +19,49 @@ */ package org.sonar.ce.task.projectanalysis.duplication; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.Ordering; import java.util.Comparator; +import java.util.List; import java.util.Objects; import java.util.SortedSet; +import java.util.TreeSet; +import java.util.function.Function; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.FluentIterable.from; import static java.util.Objects.requireNonNull; @Immutable public final class Duplication { - private static final Ordering DUPLICATE_ORDERING = Ordering.from(DuplicateComparatorByType.INSTANCE) - .compound(Ordering.natural().onResultOf(DuplicateToFileKey.INSTANCE)) - .compound(Ordering.natural().onResultOf(DuplicateToTextBlock.INSTANCE)); + private static final Comparator DUPLICATE_COMPARATOR = DuplicateComparatorByType.INSTANCE + .thenComparing(DuplicateToFileKey.INSTANCE).thenComparing(DuplicateToTextBlock.INSTANCE); private final TextBlock original; private final SortedSet duplicates; /** - * @throws NullPointerException if {@code original} is {@code null} or {@code duplicates} is {@code null} or {@code duplicates} contains {@code null} + * @throws NullPointerException if {@code original} is {@code null} or {@code duplicates} is {@code null} or {@code duplicates} contains {@code null} * @throws IllegalArgumentException if {@code duplicates} is empty * @throws IllegalArgumentException if {@code duplicates} contains a {@link InnerDuplicate} with {@code original} */ - public Duplication(final TextBlock original, final Iterable duplicates) { + public Duplication(TextBlock original, List duplicates) { this.original = requireNonNull(original, "original TextBlock can not be null"); - this.duplicates = from(requireNonNull(duplicates, "duplicates can not be null")) - .filter(FailOnNullDuplicate.INSTANCE) - .filter(new EnsureInnerDuplicateIsNotOriginalTextBlock(original)) - .toSortedSet(DUPLICATE_ORDERING); - checkArgument(!this.duplicates.isEmpty(), "duplicates can not be empty"); + validateDuplicates(original, duplicates); + this.duplicates = new TreeSet<>(DUPLICATE_COMPARATOR); + this.duplicates.addAll(duplicates); + } + + private static void validateDuplicates(TextBlock original, List duplicates) { + requireNonNull(duplicates, "duplicates can not be null"); + checkArgument(!duplicates.isEmpty(), "duplicates can not be empty"); + + for (Duplicate dup : duplicates) { + requireNonNull(dup, "duplicates can not contain null"); + if (dup instanceof InnerDuplicate) { + checkArgument(!original.equals(dup.getTextBlock()), "TextBlock of an InnerDuplicate can not be the original TextBlock"); + } + } } /** @@ -67,8 +75,8 @@ public final class Duplication { * The duplicates of the original, sorted by inner duplicates, then project duplicates, then cross-project duplicates. * For each category of duplicate, they are sorted by: *
    - *
  • file key (unless it's an InnerDuplicate)
  • - *
  • then by TextBlocks by start line and in case of same line, by shortest first
  • + *
  • file key (unless it's an InnerDuplicate)
  • + *
  • then by TextBlocks by start line and in case of same line, by shortest first
  • *
The returned set can not be empty and no inner duplicate can contain the original {@link TextBlock}.

*/ @@ -101,16 +109,6 @@ public final class Duplication { '}'; } - private enum FailOnNullDuplicate implements Predicate { - INSTANCE; - - @Override - public boolean apply(@Nullable Duplicate input) { - requireNonNull(input, "duplicates can not contain null"); - return true; - } - } - private enum DuplicateComparatorByType implements Comparator { INSTANCE; @@ -143,22 +141,6 @@ public final class Duplication { } } - private static class EnsureInnerDuplicateIsNotOriginalTextBlock implements Predicate { - private final TextBlock original; - - public EnsureInnerDuplicateIsNotOriginalTextBlock(TextBlock original) { - this.original = original; - } - - @Override - public boolean apply(@Nullable Duplicate input) { - if (input instanceof InnerDuplicate) { - checkArgument(!original.equals(input.getTextBlock()), "TextBlock of an InnerDuplicate can not be the original TextBlock"); - } - return true; - } - } - private enum DuplicateToFileKey implements Function { INSTANCE; diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/IntegrateCrossProjectDuplications.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/IntegrateCrossProjectDuplications.java index 7cc2e8d5d17..c362762edc6 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/IntegrateCrossProjectDuplications.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/IntegrateCrossProjectDuplications.java @@ -19,18 +19,20 @@ */ package org.sonar.ce.task.projectanalysis.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; import java.util.Map; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import org.sonar.api.CoreProperties; import org.sonar.api.config.Configuration; +import org.sonar.api.utils.System2; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; +import org.sonar.ce.task.log.CeTaskMessages; import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.duplications.block.Block; import org.sonar.duplications.detector.suffixtree.SuffixTreeCloneDetectionAlgorithm; @@ -39,16 +41,15 @@ import org.sonar.duplications.index.CloneIndex; import org.sonar.duplications.index.ClonePart; import org.sonar.duplications.index.PackedMemoryCloneIndex; -import static com.google.common.collect.FluentIterable.from; - /** * Transform a list of duplication blocks into clone groups, then add these clone groups into the duplication repository. */ public class IntegrateCrossProjectDuplications { private static final Logger LOGGER = Loggers.get(IntegrateCrossProjectDuplications.class); - private static final String JAVA_KEY = "java"; + private static final String DEPRECATED_WARNING = "This analysis uses the deprecated cross-project duplication feature."; + private static final String DEPRECATED_WARNING_DASHBOARD = "This project uses the deprecated cross-project duplication feature."; private static final int MAX_CLONE_GROUP_PER_FILE = 100; private static final int MAX_CLONE_PART_PER_GROUP = 100; @@ -58,11 +59,12 @@ public class IntegrateCrossProjectDuplications { private Map numberOfUnitsByLanguage = new HashMap<>(); - public IntegrateCrossProjectDuplications(Configuration config, DuplicationRepository duplicationRepository) { + public IntegrateCrossProjectDuplications(Configuration config, DuplicationRepository duplicationRepository, CeTaskMessages ceTaskMessages, System2 system) { this.config = config; this.duplicationRepository = duplicationRepository; if (config.getBoolean(CoreProperties.CPD_CROSS_PROJECT).orElse(false)) { - LOGGER.warn("This analysis uses the deprecated cross-project duplication feature."); + LOGGER.warn(DEPRECATED_WARNING); + ceTaskMessages.add(new CeTaskMessages.Message(DEPRECATED_WARNING_DASHBOARD, system.now())); } } @@ -72,7 +74,9 @@ public class IntegrateCrossProjectDuplications { populateIndex(duplicationIndex, duplicationBlocks); List duplications = SuffixTreeCloneDetectionAlgorithm.detect(duplicationIndex, originBlocks); - Iterable filtered = from(duplications).filter(getNumberOfUnitsNotLessThan(component.getFileAttributes().getLanguageKey())); + Iterable filtered = duplications.stream() + .filter(getNumberOfUnitsNotLessThan(component.getFileAttributes().getLanguageKey())) + .collect(Collectors.toList()); addDuplications(component, filtered); } @@ -96,20 +100,21 @@ public class IntegrateCrossProjectDuplications { private void addDuplication(Component file, CloneGroup duplication) { ClonePart originPart = duplication.getOriginPart(); - Iterable duplicates = convertClonePartsToDuplicates(file, duplication); - if (!Iterables.isEmpty(duplicates)) { + List duplicates = convertClonePartsToDuplicates(file, duplication); + if (!duplicates.isEmpty()) { duplicationRepository.add( file, new Duplication(new TextBlock(originPart.getStartLine(), originPart.getEndLine()), duplicates)); } } - private static Iterable convertClonePartsToDuplicates(final Component file, CloneGroup duplication) { + private static List convertClonePartsToDuplicates(final Component file, CloneGroup duplication) { final ClonePart originPart = duplication.getOriginPart(); - return from(duplication.getCloneParts()) + return duplication.getCloneParts().stream() .filter(new DoesNotMatchSameComponentKey(originPart.getResourceId())) .filter(new DuplicateLimiter(file, originPart)) - .transform(ClonePartToCrossProjectDuplicate.INSTANCE); + .map(ClonePartToCrossProjectDuplicate.INSTANCE) + .collect(Collectors.toList()); } private NumberOfUnitsNotLessThan getNumberOfUnitsNotLessThan(String language) { @@ -132,12 +137,12 @@ public class IntegrateCrossProjectDuplications { private static class NumberOfUnitsNotLessThan implements Predicate { private final int min; - public NumberOfUnitsNotLessThan(int min) { + NumberOfUnitsNotLessThan(int min) { this.min = min; } @Override - public boolean apply(@Nonnull CloneGroup input) { + public boolean test(@Nonnull CloneGroup input) { return input.getLengthInUnits() >= min; } } @@ -150,7 +155,7 @@ public class IntegrateCrossProjectDuplications { } @Override - public boolean apply(@Nonnull ClonePart part) { + public boolean test(@Nonnull ClonePart part) { return !part.getResourceId().equals(componentKey); } } @@ -160,18 +165,18 @@ public class IntegrateCrossProjectDuplications { private final ClonePart originPart; private int counter = 0; - public DuplicateLimiter(Component file, ClonePart originPart) { + DuplicateLimiter(Component file, ClonePart originPart) { this.file = file; this.originPart = originPart; } @Override - public boolean apply(@Nonnull ClonePart input) { + public boolean test(@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.getDbKey(), originPart.getStartLine(), MAX_CLONE_PART_PER_GROUP); } - boolean res = counter <= MAX_CLONE_GROUP_PER_FILE; + boolean res = counter < MAX_CLONE_GROUP_PER_FILE; counter++; return res; } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadCrossProjectDuplicationsRepositoryStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadCrossProjectDuplicationsRepositoryStep.java index adcfdc744bb..09d56ae2f7e 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadCrossProjectDuplicationsRepositoryStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadCrossProjectDuplicationsRepositoryStep.java @@ -19,9 +19,11 @@ */ package org.sonar.ce.task.projectanalysis.step; -import com.google.common.base.Function; +import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -44,12 +46,8 @@ import org.sonar.duplications.block.Block; import org.sonar.duplications.block.ByteArray; import org.sonar.scanner.protocol.output.ScannerReport.CpdTextBlock; -import static com.google.common.collect.FluentIterable.from; -import static com.google.common.collect.Lists.newArrayList; - /** * Feed the duplications repository from the cross project duplication blocks computed with duplications blocks of the analysis report. - * * Blocks can be empty if : * - The file is excluded from the analysis using {@link org.sonar.api.CoreProperties#CPD_EXCLUSIONS} * - On Java, if the number of statements of the file is too small, nothing will be sent. @@ -96,23 +94,25 @@ public class LoadCrossProjectDuplicationsRepositoryStep implements ComputationSt @Override public void visitFile(Component file) { - List cpdTextBlocks; + List cpdTextBlocks = new ArrayList<>(); try (CloseableIterator blocksIt = reportReader.readCpdTextBlocks(file.getReportAttributes().getRef())) { - cpdTextBlocks = newArrayList(blocksIt); - LOGGER.trace("Found {} cpd blocks on file {}", cpdTextBlocks.size(), file.getDbKey()); - if (cpdTextBlocks.isEmpty()) { - return; + while(blocksIt.hasNext()) { + cpdTextBlocks.add(blocksIt.next()); } } + LOGGER.trace("Found {} cpd blocks on file {}", cpdTextBlocks.size(), file.getDbKey()); + if (cpdTextBlocks.isEmpty()) { + return; + } - Collection hashes = from(cpdTextBlocks).transform(CpdTextBlockToHash.INSTANCE).toList(); + Collection hashes = cpdTextBlocks.stream().map(CpdTextBlockToHash.INSTANCE).collect(Collectors.toList()); List dtos = selectDuplicates(file, hashes); if (dtos.isEmpty()) { return; } - Collection duplicatedBlocks = from(dtos).transform(DtoToBlock.INSTANCE).toList(); - Collection originBlocks = from(cpdTextBlocks).transform(new CpdTextBlockToBlock(file.getDbKey())).toList(); + Collection duplicatedBlocks = dtos.stream().map(DtoToBlock.INSTANCE).collect(Collectors.toList()); + Collection originBlocks = cpdTextBlocks.stream().map(new CpdTextBlockToBlock(file.getDbKey())).collect(Collectors.toList()); LOGGER.trace("Found {} duplicated cpd blocks on file {}", duplicatedBlocks.size(), file.getDbKey()); integrateCrossProjectDuplications.computeCpd(file, originBlocks, duplicatedBlocks); @@ -155,7 +155,7 @@ public class LoadCrossProjectDuplicationsRepositoryStep implements ComputationSt private final String fileKey; private int indexInFile = 0; - public CpdTextBlockToBlock(String fileKey) { + CpdTextBlockToBlock(String fileKey) { this.fileKey = fileKey; } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationRepositoryRule.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationRepositoryRule.java index 453f987127d..79e8d1d28e6 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationRepositoryRule.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationRepositoryRule.java @@ -101,7 +101,7 @@ public class DuplicationRepositoryRule extends ExternalResource implements Dupli component, new Duplication( original, - from(Arrays.asList(duplicates)).transform(TextBlockToInnerDuplicate.INSTANCE))); + from(Arrays.asList(duplicates)).transform(TextBlockToInnerDuplicate.INSTANCE).toList())); return this; } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationTest.java index c68e857d7dc..7fb700bd865 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationTest.java @@ -19,11 +19,9 @@ */ package org.sonar.ce.task.projectanalysis.duplication; -import com.google.common.collect.ImmutableSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.List; import org.junit.Rule; import org.junit.Test; @@ -51,7 +49,7 @@ public class DuplicationTest { expectedException.expect(NullPointerException.class); expectedException.expectMessage("original TextBlock can not be null"); - new Duplication(null, Collections.emptySet()); + new Duplication(null, Collections.emptyList()); } @Test @@ -67,7 +65,7 @@ public class DuplicationTest { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("duplicates can not be empty"); - new Duplication(SOME_ORIGINAL_TEXTBLOCK, Collections.emptySet()); + new Duplication(SOME_ORIGINAL_TEXTBLOCK, Collections.emptyList()); } @Test @@ -75,7 +73,7 @@ public class DuplicationTest { expectedException.expect(NullPointerException.class); expectedException.expectMessage("duplicates can not contain null"); - new Duplication(SOME_ORIGINAL_TEXTBLOCK, new HashSet<>(Arrays.asList(mock(Duplicate.class), null, mock(Duplicate.class)))); + new Duplication(SOME_ORIGINAL_TEXTBLOCK, Arrays.asList(mock(Duplicate.class), null, mock(Duplicate.class))); } @Test @@ -83,7 +81,7 @@ public class DuplicationTest { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("TextBlock of an InnerDuplicate can not be the original TextBlock"); - new Duplication(SOME_ORIGINAL_TEXTBLOCK, new HashSet<>(Arrays.asList(mock(Duplicate.class), new InnerDuplicate(SOME_ORIGINAL_TEXTBLOCK), mock(Duplicate.class)))); + new Duplication(SOME_ORIGINAL_TEXTBLOCK, Arrays.asList(mock(Duplicate.class), new InnerDuplicate(SOME_ORIGINAL_TEXTBLOCK), mock(Duplicate.class))); } @Test @@ -91,7 +89,7 @@ public class DuplicationTest { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Unsupported type of Duplicate " + MyDuplicate.class.getName()); - new Duplication(SOME_ORIGINAL_TEXTBLOCK, ImmutableSet.of(new MyDuplicate(), new MyDuplicate())); + new Duplication(SOME_ORIGINAL_TEXTBLOCK, Arrays.asList(new MyDuplicate(), new MyDuplicate())); } private static final class MyDuplicate implements Duplicate { @@ -104,7 +102,8 @@ public class DuplicationTest { @Test public void getOriginal_returns_original() { - assertThat(new Duplication(SOME_ORIGINAL_TEXTBLOCK, ImmutableSet.of(mock(Duplicate.class))).getOriginal()).isSameAs(SOME_ORIGINAL_TEXTBLOCK); + assertThat(new Duplication(SOME_ORIGINAL_TEXTBLOCK, Arrays.asList(new InnerDuplicate(TEXT_BLOCK_1))) + .getOriginal()).isSameAs(SOME_ORIGINAL_TEXTBLOCK); } @Test diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/IntegrateCrossProjectDuplicationsTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/IntegrateCrossProjectDuplicationsTest.java index 4f1b01ebfa2..a2a66c69f7c 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/IntegrateCrossProjectDuplicationsTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/IntegrateCrossProjectDuplicationsTest.java @@ -26,8 +26,10 @@ import java.util.Collections; import org.junit.Rule; import org.junit.Test; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.utils.internal.TestSystem2; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.ce.task.log.CeTaskMessages; import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.FileAttributes; import org.sonar.duplications.block.Block; @@ -38,29 +40,29 @@ 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.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE; import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder; public class IntegrateCrossProjectDuplicationsTest { + private static final String XOO_LANGUAGE = "xoo"; + private static final String ORIGIN_FILE_KEY = "ORIGIN_FILE_KEY"; + private static final Component ORIGIN_FILE = builder(FILE, 1) + .setKey(ORIGIN_FILE_KEY) + .setFileAttributes(new FileAttributes(false, XOO_LANGUAGE, 1)) + .build(); + private static final String OTHER_FILE_KEY = "OTHER_FILE_KEY"; @Rule public LogTester logTester = new LogTester(); @Rule public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(); - static final String XOO_LANGUAGE = "xoo"; - - static final String ORIGIN_FILE_KEY = "ORIGIN_FILE_KEY"; - static final Component ORIGIN_FILE = builder(FILE, 1) - .setKey(ORIGIN_FILE_KEY) - .setFileAttributes(new FileAttributes(false, XOO_LANGUAGE, 1)) - .build(); - - static final String OTHER_FILE_KEY = "OTHER_FILE_KEY"; - - MapSettings settings = new MapSettings(); - - IntegrateCrossProjectDuplications underTest = new IntegrateCrossProjectDuplications(settings.asConfig(), duplicationRepository); + private TestSystem2 system = new TestSystem2(); + private MapSettings settings = new MapSettings(); + private CeTaskMessages ceTaskMessages = mock(CeTaskMessages.class); + private IntegrateCrossProjectDuplications underTest = new IntegrateCrossProjectDuplications(settings.asConfig(), duplicationRepository, ceTaskMessages, system); @Test public void add_duplications_from_two_blocks() { @@ -335,10 +337,12 @@ public class IntegrateCrossProjectDuplicationsTest { @Test public void log_warning_if_this_deprecated_feature_is_enabled() { settings.setProperty("sonar.cpd.cross_project", "true"); + system.setNow(1000L); - new IntegrateCrossProjectDuplications(settings.asConfig(), duplicationRepository); + new IntegrateCrossProjectDuplications(settings.asConfig(), duplicationRepository, ceTaskMessages, system); assertThat(logTester.logs()).containsExactly("This analysis uses the deprecated cross-project duplication feature."); + verify(ceTaskMessages).add(new CeTaskMessages.Message("This project uses the deprecated cross-project duplication feature.", 1000L)); } private static Duplication crossProjectDuplication(TextBlock original, String otherFileKey, TextBlock duplicate) { -- 2.39.5