From: Julien HENRY Date: Thu, 20 Dec 2018 10:48:17 +0000 (+0100) Subject: SONAR-11598 Rework CPD exclusions X-Git-Tag: 7.6~50 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=e3b3e6950a4dcb728025b4a80343ad73c68516bf;p=sonarqube.git SONAR-11598 Rework CPD exclusions --- diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java index 0b60c5e227c..c0fd7c38bcc 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java @@ -60,6 +60,7 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile private Metadata metadata; private boolean published; private boolean excludedForCoverage; + private boolean excludedForDuplication; private final Set noSonarLines = new HashSet<>(); private boolean ignoreAllIssues; private Collection ignoreIssuesOnlineRanges = new ArrayList<>(); @@ -127,6 +128,15 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile return excludedForCoverage; } + public DefaultInputFile setExcludedForDuplication(boolean excludedForDuplication) { + this.excludedForDuplication = excludedForDuplication; + return this; + } + + public boolean isExcludedForDuplication() { + return excludedForDuplication; + } + /** * @deprecated since 6.6 */ diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java index d2cae805cac..a30a4accc76 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java @@ -19,17 +19,14 @@ */ package org.sonar.api.batch.sensor.cpd.internal; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import org.sonar.api.CoreProperties; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.TextRange; -import org.sonar.api.batch.fs.internal.PathPattern; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.cpd.NewCpdTokens; import org.sonar.api.batch.sensor.internal.DefaultStorable; import org.sonar.api.batch.sensor.internal.SensorStorage; -import org.sonar.api.config.Configuration; import static com.google.common.base.Preconditions.checkState; import static java.util.Collections.unmodifiableList; @@ -37,7 +34,6 @@ import static java.util.Objects.requireNonNull; public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens { - private final Configuration config; private final List result = new ArrayList<>(); private InputFile inputFile; private int startLine = Integer.MIN_VALUE; @@ -45,22 +41,14 @@ public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens { private int currentIndex = 0; private StringBuilder sb = new StringBuilder(); private TextRange lastRange; - private boolean excluded; - public DefaultCpdTokens(Configuration config, SensorStorage storage) { + public DefaultCpdTokens(SensorStorage storage) { super(storage); - this.config = config; } @Override public DefaultCpdTokens onFile(InputFile inputFile) { this.inputFile = requireNonNull(inputFile, "file can't be null"); - String[] cpdExclusions = config.getStringArray(CoreProperties.CPD_EXCLUSIONS); - for (PathPattern cpdExclusion : PathPattern.create(cpdExclusions)) { - if (cpdExclusion.match(inputFile.path(), Paths.get(inputFile.relativePath()))) { - this.excluded = true; - } - } return this; } @@ -85,7 +73,7 @@ public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens { requireNonNull(range, "Range should not be null"); requireNonNull(image, "Image should not be null"); checkInputFileNotNull(); - if (excluded) { + if (isExcludedForDuplication()) { return this; } checkState(lastRange == null || lastRange.end().compareTo(range.start()) <= 0, @@ -106,6 +94,10 @@ public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens { return this; } + private boolean isExcludedForDuplication() { + return ((DefaultInputFile) inputFile).isExcludedForDuplication(); + } + public List getTokenLines() { return unmodifiableList(new ArrayList<>(result)); } @@ -120,7 +112,7 @@ public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens { @Override protected void doSave() { checkState(inputFile != null, "Call onFile() first"); - if (excluded) { + if (isExcludedForDuplication()) { return; } addNewTokensLine(result, startIndex, currentIndex, startLine, sb); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java index e8cc9f81b8f..0ee4c211f7b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java @@ -323,7 +323,7 @@ public class SensorContextTester implements SensorContext { @Override public NewCpdTokens newCpdTokens() { - return new DefaultCpdTokens(config(), sensorStorage); + return new DefaultCpdTokens(sensorStorage); } @Override diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokensTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokensTest.java index 2ccd9fd01cf..44827ebcebb 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokensTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokensTest.java @@ -19,12 +19,10 @@ */ package org.sonar.api.batch.sensor.cpd.internal; -import java.io.File; import org.junit.Test; -import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.internal.SensorStorage; -import org.sonar.api.config.internal.MapSettings; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -35,7 +33,7 @@ import static org.mockito.Mockito.verifyZeroInteractions; public class DefaultCpdTokensTest { - private static final InputFile INPUT_FILE = new TestInputFileBuilder("foo", "src/Foo.java") + private final DefaultInputFile inputFile = new TestInputFileBuilder("foo", "src/Foo.java") .setLines(2) .setOriginalLineStartOffsets(new int[] {0, 50}) .setOriginalLineEndOffsets(new int[] {49, 100}) @@ -45,22 +43,22 @@ public class DefaultCpdTokensTest { @Test public void save_no_tokens() { SensorStorage sensorStorage = mock(SensorStorage.class); - DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings().asConfig(), sensorStorage) - .onFile(INPUT_FILE); + DefaultCpdTokens tokens = new DefaultCpdTokens(sensorStorage) + .onFile(inputFile); tokens.save(); verify(sensorStorage).store(tokens); - assertThat(tokens.inputFile()).isEqualTo(INPUT_FILE); + assertThat(tokens.inputFile()).isEqualTo(inputFile); } @Test public void save_one_token() { SensorStorage sensorStorage = mock(SensorStorage.class); - DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings().asConfig(), sensorStorage) - .onFile(INPUT_FILE) - .addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo"); + DefaultCpdTokens tokens = new DefaultCpdTokens(sensorStorage) + .onFile(inputFile) + .addToken(inputFile.newRange(1, 2, 1, 5), "foo"); tokens.save(); @@ -70,13 +68,12 @@ public class DefaultCpdTokensTest { } @Test - public void handle_exclusions_by_pattern() { + public void handle_exclusions() { SensorStorage sensorStorage = mock(SensorStorage.class); - MapSettings settings = new MapSettings(); - settings.setProperty("sonar.cpd.exclusions", "src/Foo.java,another"); - DefaultCpdTokens tokens = new DefaultCpdTokens(settings.asConfig(), sensorStorage) - .onFile(INPUT_FILE) - .addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo"); + inputFile.setExcludedForDuplication(true); + DefaultCpdTokens tokens = new DefaultCpdTokens(sensorStorage) + .onFile(inputFile) + .addToken(inputFile.newRange(1, 2, 1, 5), "foo"); tokens.save(); @@ -88,12 +85,12 @@ public class DefaultCpdTokensTest { @Test public void save_many_tokens() { SensorStorage sensorStorage = mock(SensorStorage.class); - DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings().asConfig(), sensorStorage) - .onFile(INPUT_FILE) - .addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo") - .addToken(INPUT_FILE.newRange(1, 6, 1, 10), "bar") - .addToken(INPUT_FILE.newRange(1, 20, 1, 25), "biz") - .addToken(INPUT_FILE.newRange(2, 1, 2, 10), "next"); + DefaultCpdTokens tokens = new DefaultCpdTokens(sensorStorage) + .onFile(inputFile) + .addToken(inputFile.newRange(1, 2, 1, 5), "foo") + .addToken(inputFile.newRange(1, 6, 1, 10), "bar") + .addToken(inputFile.newRange(1, 20, 1, 25), "biz") + .addToken(inputFile.newRange(2, 1, 2, 10), "next"); tokens.save(); @@ -109,7 +106,7 @@ public class DefaultCpdTokensTest { @Test public void basic_validation() { SensorStorage sensorStorage = mock(SensorStorage.class); - DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings().asConfig(), sensorStorage); + DefaultCpdTokens tokens = new DefaultCpdTokens(sensorStorage); try { tokens.save(); fail("Expected exception"); @@ -117,7 +114,7 @@ public class DefaultCpdTokensTest { assertThat(e).hasMessage("Call onFile() first"); } try { - tokens.addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo"); + tokens.addToken(inputFile.newRange(1, 2, 1, 5), "foo"); fail("Expected exception"); } catch (Exception e) { assertThat(e).hasMessage("Call onFile() first"); @@ -129,7 +126,7 @@ public class DefaultCpdTokensTest { assertThat(e).hasMessage("Range should not be null"); } try { - tokens.addToken(INPUT_FILE.newRange(1, 2, 1, 5), null); + tokens.addToken(inputFile.newRange(1, 2, 1, 5), null); fail("Expected exception"); } catch (Exception e) { assertThat(e).hasMessage("Image should not be null"); @@ -139,12 +136,12 @@ public class DefaultCpdTokensTest { @Test public void validate_tokens_order() { SensorStorage sensorStorage = mock(SensorStorage.class); - DefaultCpdTokens tokens = new DefaultCpdTokens(new MapSettings().asConfig(), sensorStorage) - .onFile(INPUT_FILE) - .addToken(INPUT_FILE.newRange(1, 6, 1, 10), "bar"); + DefaultCpdTokens tokens = new DefaultCpdTokens(sensorStorage) + .onFile(inputFile) + .addToken(inputFile.newRange(1, 6, 1, 10), "bar"); try { - tokens.addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo"); + tokens.addToken(inputFile.newRange(1, 2, 1, 5), "foo"); fail("Expected exception"); } catch (Exception e) { assertThat(e).hasMessage("Tokens of file src/Foo.java should be provided in order.\n" + diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/JavaCpdBlockIndexerSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/JavaCpdBlockIndexerSensor.java index 5770e8869ab..13c59fc40dd 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/JavaCpdBlockIndexerSensor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/JavaCpdBlockIndexerSensor.java @@ -19,22 +19,23 @@ */ package org.sonar.scanner.cpd; -import com.google.common.collect.Lists; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.CoreProperties; import org.sonar.api.batch.Phase; import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.scanner.sensor.ProjectSensor; import org.sonar.duplications.block.Block; import org.sonar.duplications.block.BlockChunker; import org.sonar.duplications.java.JavaStatementBuilder; @@ -48,7 +49,7 @@ import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; * Special case for Java that use a dedicated block indexer. */ @Phase(name = Phase.Name.POST) -public class JavaCpdBlockIndexerSensor implements Sensor { +public class JavaCpdBlockIndexerSensor implements ProjectSensor { private static final int BLOCK_SIZE = 10; private static final Logger LOG = LoggerFactory.getLogger(JavaCpdBlockIndexerSensor.class); @@ -66,12 +67,16 @@ public class JavaCpdBlockIndexerSensor implements Sensor { @Override public void execute(SensorContext context) { - String[] cpdExclusions = context.config().getStringArray(CoreProperties.CPD_EXCLUSIONS); FilePredicates p = context.fileSystem().predicates(); - List sourceFiles = Lists.newArrayList(context.fileSystem().inputFiles(p.and( - p.hasType(InputFile.Type.MAIN), - p.hasLanguage("java"), - p.doesNotMatchPathPatterns(cpdExclusions)))); + List sourceFiles = StreamSupport.stream( + context.fileSystem().inputFiles( + p.and( + p.hasType(InputFile.Type.MAIN), + p.hasLanguage("java") + ) + ).spliterator(), false) + .filter(f -> !((DefaultInputFile) f).isExcludedForDuplication()) + .collect(Collectors.toList()); if (sourceFiles.isEmpty()) { return; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java index 6171c2793de..26b6ffa8079 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java @@ -109,7 +109,7 @@ import org.sonar.scanner.scan.filesystem.FileIndexer; import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.scan.filesystem.LanguageDetection; import org.sonar.scanner.scan.filesystem.MetadataGenerator; -import org.sonar.scanner.scan.filesystem.ProjectCoverageExclusions; +import org.sonar.scanner.scan.filesystem.ProjectCoverageAndDuplicationExclusions; import org.sonar.scanner.scan.filesystem.ProjectExclusionFilters; import org.sonar.scanner.scan.filesystem.ProjectFileIndexer; import org.sonar.scanner.scan.filesystem.ScannerComponentIdGenerator; @@ -244,7 +244,7 @@ public class ProjectScanContainer extends ComponentContainer { ScannerProperties.class, new ProjectConfigurationProvider(), - ProjectCoverageExclusions.class, + ProjectCoverageAndDuplicationExclusions.class, // Report ScannerMetrics.class, diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/AbstractCoverageAndDuplicationExclusions.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/AbstractCoverageAndDuplicationExclusions.java new file mode 100644 index 00000000000..c061aa5add0 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/AbstractCoverageAndDuplicationExclusions.java @@ -0,0 +1,95 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.scan.filesystem; + +import java.util.Collection; +import java.util.function.Function; +import java.util.stream.Stream; +import javax.annotation.concurrent.Immutable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.utils.WildcardPattern; + +import static java.util.stream.Collectors.toList; + +@Immutable +public abstract class AbstractCoverageAndDuplicationExclusions { + private static final Logger LOG = LoggerFactory.getLogger(AbstractCoverageAndDuplicationExclusions.class); + private final Function pathExtractor; + private final String[] coverageExclusionConfig; + private final String[] duplicationExclusionConfig; + + private final Collection coverageExclusionPatterns; + private final Collection duplicationExclusionPatterns; + + public AbstractCoverageAndDuplicationExclusions(Function configProvider, Function pathExtractor) { + this.pathExtractor = pathExtractor; + coverageExclusionConfig = configProvider.apply(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY); + coverageExclusionPatterns = Stream.of(coverageExclusionConfig).map(WildcardPattern::create).collect(toList()); + duplicationExclusionConfig = configProvider.apply(CoreProperties.CPD_EXCLUSIONS); + duplicationExclusionPatterns = Stream.of(duplicationExclusionConfig).map(WildcardPattern::create).collect(toList()); + } + + public String[] getCoverageExclusionConfig() { + return coverageExclusionConfig; + } + + public String[] getDuplicationExclusionConfig() { + return duplicationExclusionConfig; + } + + void log() { + if (!coverageExclusionPatterns.isEmpty()) { + log("Excluded sources for coverage: ", coverageExclusionPatterns); + } + if (!duplicationExclusionPatterns.isEmpty()) { + log("Excluded sources for duplication: ", duplicationExclusionPatterns); + } + } + + public boolean isExcludedForCoverage(DefaultInputFile file) { + return isExcluded(file, coverageExclusionPatterns); + } + + public boolean isExcludedForDuplication(DefaultInputFile file) { + return isExcluded(file, duplicationExclusionPatterns); + } + + private boolean isExcluded(DefaultInputFile file, Collection patterns) { + if (patterns.isEmpty()) { + return false; + } + final String path = pathExtractor.apply(file); + return patterns + .stream() + .anyMatch(p -> p.match(path)); + } + + private static void log(String title, Collection patterns) { + if (!patterns.isEmpty()) { + LOG.info(title); + for (WildcardPattern pattern : patterns) { + LOG.info(" {}", pattern); + } + } + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/AbstractCoverageExclusions.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/AbstractCoverageExclusions.java deleted file mode 100644 index 2d1abe07a67..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/AbstractCoverageExclusions.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.scan.filesystem; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; -import java.util.Collection; -import java.util.Iterator; -import java.util.function.Function; -import javax.annotation.concurrent.Immutable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.CoreProperties; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.utils.WildcardPattern; - -@Immutable -public abstract class AbstractCoverageExclusions { - private static final Logger LOG = LoggerFactory.getLogger(AbstractCoverageExclusions.class); - private final Function pathExtractor; - private final String[] coverageExclusionConfig; - - private Collection exclusionPatterns; - - public AbstractCoverageExclusions(Function configProvider, Function pathExtractor) { - this.pathExtractor = pathExtractor; - Builder builder = ImmutableList.builder(); - coverageExclusionConfig = configProvider.apply(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY); - for (String pattern : coverageExclusionConfig) { - builder.add(WildcardPattern.create(pattern)); - } - exclusionPatterns = builder.build(); - } - - public String[] getCoverageExclusionConfig() { - return coverageExclusionConfig; - } - - void log() { - if (!exclusionPatterns.isEmpty()) { - log("Excluded sources for coverage: ", exclusionPatterns); - } - } - - public boolean isExcluded(DefaultInputFile file) { - boolean found = false; - Iterator iterator = exclusionPatterns.iterator(); - while (!found && iterator.hasNext()) { - found = iterator.next().match(pathExtractor.apply(file)); - } - return found; - } - - private static void log(String title, Collection patterns) { - if (!patterns.isEmpty()) { - LOG.info(title); - for (WildcardPattern pattern : patterns) { - LOG.info(" {}", pattern); - } - } - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java index aba73c172a4..60798fe990b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java @@ -51,7 +51,7 @@ public class FileIndexer { private final ScanProperties properties; private final InputFileFilter[] filters; private final ProjectExclusionFilters projectExclusionFilters; - private final ProjectCoverageExclusions projectCoverageExclusions; + private final ProjectCoverageAndDuplicationExclusions projectCoverageAndDuplicationExclusions; private final IssueExclusionsLoader issueExclusionsLoader; private final MetadataGenerator metadataGenerator; private final DefaultInputProject project; @@ -62,15 +62,16 @@ public class FileIndexer { private boolean warnExclusionsAlreadyLogged; private boolean warnCoverageExclusionsAlreadyLogged; + private boolean warnDuplicationExclusionsAlreadyLogged; public FileIndexer(DefaultInputProject project, ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore, - ProjectExclusionFilters projectExclusionFilters, ProjectCoverageExclusions projectCoverageExclusions, IssueExclusionsLoader issueExclusionsLoader, - MetadataGenerator metadataGenerator, SensorStrategy sensorStrategy, LanguageDetection languageDetection, AnalysisWarnings analysisWarnings, ScanProperties properties, - InputFileFilter[] filters) { + ProjectExclusionFilters projectExclusionFilters, ProjectCoverageAndDuplicationExclusions projectCoverageAndDuplicationExclusions, IssueExclusionsLoader issueExclusionsLoader, + MetadataGenerator metadataGenerator, SensorStrategy sensorStrategy, LanguageDetection languageDetection, AnalysisWarnings analysisWarnings, ScanProperties properties, + InputFileFilter[] filters) { this.project = project; this.scannerComponentIdGenerator = scannerComponentIdGenerator; this.componentStore = componentStore; - this.projectCoverageExclusions = projectCoverageExclusions; + this.projectCoverageAndDuplicationExclusions = projectCoverageAndDuplicationExclusions; this.issueExclusionsLoader = issueExclusionsLoader; this.metadataGenerator = metadataGenerator; this.sensorStrategy = sensorStrategy; @@ -82,17 +83,17 @@ public class FileIndexer { } public FileIndexer(DefaultInputProject project, ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore, - ProjectExclusionFilters projectExclusionFilters, ProjectCoverageExclusions projectCoverageExclusions, IssueExclusionsLoader issueExclusionsLoader, - MetadataGenerator metadataGenerator, SensorStrategy sensorStrategy, LanguageDetection languageDetection, AnalysisWarnings analysisWarnings, ScanProperties properties) { - this(project, scannerComponentIdGenerator, componentStore, projectExclusionFilters, projectCoverageExclusions, issueExclusionsLoader, metadataGenerator, sensorStrategy, + ProjectExclusionFilters projectExclusionFilters, ProjectCoverageAndDuplicationExclusions projectCoverageAndDuplicationExclusions, IssueExclusionsLoader issueExclusionsLoader, + MetadataGenerator metadataGenerator, SensorStrategy sensorStrategy, LanguageDetection languageDetection, AnalysisWarnings analysisWarnings, ScanProperties properties) { + this(project, scannerComponentIdGenerator, componentStore, projectExclusionFilters, projectCoverageAndDuplicationExclusions, issueExclusionsLoader, metadataGenerator, sensorStrategy, languageDetection, analysisWarnings, properties, new InputFileFilter[0]); } - public void indexFile(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageExclusions moduleCoverageExclusions, Path sourceFile, - InputFile.Type type, ProgressReport progressReport, - AtomicInteger excludedByPatternsCount) + public void indexFile(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, Path sourceFile, + InputFile.Type type, ProgressReport progressReport, + AtomicInteger excludedByPatternsCount) throws IOException { // get case of real file without resolving link Path realAbsoluteFile = sourceFile.toRealPath(LinkOption.NOFOLLOW_LINKS).toAbsolutePath().normalize(); @@ -136,7 +137,8 @@ public class FileIndexer { componentStore.put(module.key(), inputFile); issueExclusionsLoader.addMulticriteriaPatterns(inputFile); LOG.debug("'{}' indexed {}with language '{}'", projectRelativePath, type == Type.TEST ? "as test " : "", inputFile.language()); - evaluateCoverageExclusions(moduleCoverageExclusions, inputFile); + evaluateCoverageExclusions(moduleCoverageAndDuplicationExclusions, inputFile); + evaluateDuplicationExclusions(moduleCoverageAndDuplicationExclusions, inputFile); if (properties.preloadFileMetadata()) { inputFile.checkMetadata(); } @@ -151,14 +153,14 @@ public class FileIndexer { } } - private void evaluateCoverageExclusions(ModuleCoverageExclusions moduleCoverageExclusions, DefaultInputFile inputFile) { - boolean excludedByProjectConfiguration = projectCoverageExclusions.isExcluded(inputFile); + private void evaluateCoverageExclusions(ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, DefaultInputFile inputFile) { + boolean excludedByProjectConfiguration = projectCoverageAndDuplicationExclusions.isExcludedForCoverage(inputFile); if (excludedByProjectConfiguration) { inputFile.setExcludedForCoverage(true); LOG.debug("File {} excluded for coverage", inputFile); - } else if (moduleCoverageExclusions.isExcluded(inputFile)) { + } else if (moduleCoverageAndDuplicationExclusions.isExcludedForCoverage(inputFile)) { inputFile.setExcludedForCoverage(true); - if (Arrays.equals(moduleCoverageExclusions.getCoverageExclusionConfig(), projectCoverageExclusions.getCoverageExclusionConfig())) { + if (Arrays.equals(moduleCoverageAndDuplicationExclusions.getCoverageExclusionConfig(), projectCoverageAndDuplicationExclusions.getCoverageExclusionConfig())) { warnOnceDeprecatedCoverageExclusion( "Specifying module-relative paths at project level in the property '" + CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY + "' is deprecated. " + "To continue matching files like '" + inputFile + "', update this property so that patterns refer to project-relative paths."); @@ -167,6 +169,22 @@ public class FileIndexer { } } + private void evaluateDuplicationExclusions(ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, DefaultInputFile inputFile) { + boolean excludedByProjectConfiguration = projectCoverageAndDuplicationExclusions.isExcludedForDuplication(inputFile); + if (excludedByProjectConfiguration) { + inputFile.setExcludedForDuplication(true); + LOG.debug("File {} excluded for duplication", inputFile); + } else if (moduleCoverageAndDuplicationExclusions.isExcludedForDuplication(inputFile)) { + inputFile.setExcludedForDuplication(true); + if (Arrays.equals(moduleCoverageAndDuplicationExclusions.getDuplicationExclusionConfig(), projectCoverageAndDuplicationExclusions.getDuplicationExclusionConfig())) { + warnOnceDeprecatedDuplicationExclusion( + "Specifying module-relative paths at project level in the property '" + CoreProperties.CPD_EXCLUSIONS + "' is deprecated. " + + "To continue matching files like '" + inputFile + "', update this property so that patterns refer to project-relative paths."); + } + LOG.debug("File {} excluded for duplication", inputFile); + } + } + private void warnOnceDeprecatedExclusion(String msg) { if (!warnExclusionsAlreadyLogged) { LOG.warn(msg); @@ -183,6 +201,14 @@ public class FileIndexer { } } + private void warnOnceDeprecatedDuplicationExclusion(String msg) { + if (!warnDuplicationExclusionsAlreadyLogged) { + LOG.warn(msg); + analysisWarnings.addUnique(msg); + warnDuplicationExclusionsAlreadyLogged = true; + } + } + private boolean accept(InputFile indexedFile) { // InputFileFilter extensions. Might trigger generation of metadata for (InputFileFilter filter : filters) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleCoverageAndDuplicationExclusions.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleCoverageAndDuplicationExclusions.java new file mode 100644 index 00000000000..6c9dd27c8d5 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleCoverageAndDuplicationExclusions.java @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.scan.filesystem; + +import javax.annotation.concurrent.Immutable; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.scanner.scan.ModuleConfiguration; + +@Immutable +public class ModuleCoverageAndDuplicationExclusions extends AbstractCoverageAndDuplicationExclusions { + + public ModuleCoverageAndDuplicationExclusions(ModuleConfiguration moduleConfiguration) { + super(moduleConfiguration::getStringArray, DefaultInputFile::getModuleRelativePath); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleCoverageExclusions.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleCoverageExclusions.java deleted file mode 100644 index d49f5aff482..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleCoverageExclusions.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.scan.filesystem; - -import javax.annotation.concurrent.Immutable; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.scanner.scan.ModuleConfiguration; - -@Immutable -public class ModuleCoverageExclusions extends AbstractCoverageExclusions { - - public ModuleCoverageExclusions(ModuleConfiguration moduleConfiguration) { - super(moduleConfiguration::getStringArray, DefaultInputFile::getModuleRelativePath); - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectCoverageAndDuplicationExclusions.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectCoverageAndDuplicationExclusions.java new file mode 100644 index 00000000000..78359037b68 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectCoverageAndDuplicationExclusions.java @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.scan.filesystem; + +import javax.annotation.concurrent.Immutable; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.scanner.scan.ProjectConfiguration; + +@Immutable +public class ProjectCoverageAndDuplicationExclusions extends AbstractCoverageAndDuplicationExclusions { + + public ProjectCoverageAndDuplicationExclusions(ProjectConfiguration projectConfig) { + super(projectConfig::getStringArray, DefaultInputFile::getProjectRelativePath); + log(); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectCoverageExclusions.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectCoverageExclusions.java deleted file mode 100644 index e100450144d..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectCoverageExclusions.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.scan.filesystem; - -import javax.annotation.concurrent.Immutable; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.scanner.scan.ProjectConfiguration; - -@Immutable -public class ProjectCoverageExclusions extends AbstractCoverageExclusions { - - public ProjectCoverageExclusions(ProjectConfiguration projectConfig) { - super(projectConfig::getStringArray, DefaultInputFile::getProjectRelativePath); - log(); - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectFileIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectFileIndexer.java index ece4b46784b..1db64920c9e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectFileIndexer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectFileIndexer.java @@ -105,9 +105,9 @@ public class ProjectFileIndexer { // Emulate creation of module level settings ModuleConfiguration moduleConfig = new ModuleConfigurationProvider().provide(globalConfig, module, projectServerSettings); ModuleExclusionFilters moduleExclusionFilters = new ModuleExclusionFilters(moduleConfig); - ModuleCoverageExclusions moduleCoverageExclusions = new ModuleCoverageExclusions(moduleConfig); - indexFiles(module, moduleExclusionFilters, moduleCoverageExclusions, module.getSourceDirsOrFiles(), Type.MAIN, excludedByPatternsCount); - indexFiles(module, moduleExclusionFilters, moduleCoverageExclusions, module.getTestDirsOrFiles(), Type.TEST, excludedByPatternsCount); + ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions = new ModuleCoverageAndDuplicationExclusions(moduleConfig); + indexFiles(module, moduleExclusionFilters, moduleCoverageAndDuplicationExclusions, module.getSourceDirsOrFiles(), Type.MAIN, excludedByPatternsCount); + indexFiles(module, moduleExclusionFilters, moduleCoverageAndDuplicationExclusions, module.getTestDirsOrFiles(), Type.TEST, excludedByPatternsCount); } private static void logPaths(String label, Path baseDir, List paths) { @@ -139,14 +139,14 @@ public class ProjectFileIndexer { return count == 1 ? "file" : "files"; } - private void indexFiles(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageExclusions moduleCoverageExclusions, List sources, - Type type, AtomicInteger excludedByPatternsCount) { + private void indexFiles(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, List sources, + Type type, AtomicInteger excludedByPatternsCount) { try { for (Path dirOrFile : sources) { if (dirOrFile.toFile().isDirectory()) { - indexDirectory(module, moduleExclusionFilters, moduleCoverageExclusions, dirOrFile, type, excludedByPatternsCount); + indexDirectory(module, moduleExclusionFilters, moduleCoverageAndDuplicationExclusions, dirOrFile, type, excludedByPatternsCount); } else { - fileIndexer.indexFile(module, moduleExclusionFilters, moduleCoverageExclusions, dirOrFile, type, progressReport, excludedByPatternsCount); + fileIndexer.indexFile(module, moduleExclusionFilters, moduleCoverageAndDuplicationExclusions, dirOrFile, type, progressReport, excludedByPatternsCount); } } } catch (IOException e) { @@ -154,25 +154,25 @@ public class ProjectFileIndexer { } } - private void indexDirectory(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageExclusions moduleCoverageExclusions, Path dirToIndex, - Type type, AtomicInteger excludedByPatternsCount) + private void indexDirectory(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, Path dirToIndex, + Type type, AtomicInteger excludedByPatternsCount) throws IOException { Files.walkFileTree(dirToIndex.normalize(), Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, - new IndexFileVisitor(module, moduleExclusionFilters, moduleCoverageExclusions, type, excludedByPatternsCount)); + new IndexFileVisitor(module, moduleExclusionFilters, moduleCoverageAndDuplicationExclusions, type, excludedByPatternsCount)); } private class IndexFileVisitor implements FileVisitor { private final DefaultInputModule module; private final ModuleExclusionFilters moduleExclusionFilters; - private final ModuleCoverageExclusions moduleCoverageExclusions; + private final ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions; private final Type type; private final AtomicInteger excludedByPatternsCount; - IndexFileVisitor(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageExclusions moduleCoverageExclusions, Type type, - AtomicInteger excludedByPatternsCount) { + IndexFileVisitor(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, Type type, + AtomicInteger excludedByPatternsCount) { this.module = module; this.moduleExclusionFilters = moduleExclusionFilters; - this.moduleCoverageExclusions = moduleCoverageExclusions; + this.moduleCoverageAndDuplicationExclusions = moduleCoverageAndDuplicationExclusions; this.type = type; this.excludedByPatternsCount = excludedByPatternsCount; } @@ -193,7 +193,7 @@ public class ProjectFileIndexer { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (!Files.isHidden(file)) { - fileIndexer.indexFile(module, moduleExclusionFilters, moduleCoverageExclusions, file, type, progressReport, excludedByPatternsCount); + fileIndexer.indexFile(module, moduleExclusionFilters, moduleCoverageAndDuplicationExclusions, file, type, progressReport, excludedByPatternsCount); } return FileVisitResult.CONTINUE; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java index ba2418dd296..3e06892cf84 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/ProjectSensorContext.java @@ -186,7 +186,7 @@ public class ProjectSensorContext implements SensorContext { if (analysisMode.isIssues()) { return NO_OP_NEW_CPD_TOKENS; } - return new DefaultCpdTokens(config, sensorStorage); + return new DefaultCpdTokens(sensorStorage); } @Override diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/JavaCpdBlockIndexerSensorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/JavaCpdBlockIndexerSensorTest.java index 203e5b715b0..7a0a2f21695 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/JavaCpdBlockIndexerSensorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/JavaCpdBlockIndexerSensorTest.java @@ -32,8 +32,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.sonar.api.CoreProperties; -import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.sensor.internal.SensorContextTester; import org.sonar.duplications.block.Block; @@ -57,7 +56,7 @@ public class JavaCpdBlockIndexerSensorTest { @Captor private ArgumentCaptor> blockCaptor; - private InputFile file; + private DefaultInputFile file; @Before public void prepare() throws IOException { @@ -75,7 +74,7 @@ public class JavaCpdBlockIndexerSensorTest { @Test public void testExclusions() { - context.settings().setProperty(CoreProperties.CPD_EXCLUSIONS, "**"); + file.setExcludedForDuplication(true); new JavaCpdBlockIndexerSensor(index).execute(context); verifyZeroInteractions(index); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java index 9226bf38199..6b622327bcc 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java @@ -67,7 +67,6 @@ public class CoverageMediumTest { AnalysisResult result = tester.newAnalysis() .properties(ImmutableMap.builder() - .put("sonar.task", "scan") .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) .put("sonar.projectKey", "com.foo.project") .put("sonar.sources", "src") @@ -103,7 +102,6 @@ public class CoverageMediumTest { AnalysisResult result = tester.newAnalysis() .properties(ImmutableMap.builder() - .put("sonar.task", "scan") .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) .put("sonar.projectKey", "com.foo.project") .put("sonar.sources", "src") @@ -141,7 +139,6 @@ public class CoverageMediumTest { AnalysisResult result = tester.newAnalysis() .properties(ImmutableMap.builder() - .put("sonar.task", "scan") .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) .put("sonar.projectKey", "com.foo.project") .put("sonar.sources", "src") @@ -169,35 +166,34 @@ public class CoverageMediumTest { File srcDirB = new File(baseDirModuleB, "src"); srcDirB.mkdirs(); - File xooFileA = new File(srcDirA, "sample.xoo"); - File xooUtCoverageFileA = new File(srcDirA, "sample.xoo.coverage"); + File xooFileA = new File(srcDirA, "sampleA.xoo"); + File xooUtCoverageFileA = new File(srcDirA, "sampleA.xoo.coverage"); FileUtils.write(xooFileA, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}", StandardCharsets.UTF_8); FileUtils.write(xooUtCoverageFileA, "2:2:2:1\n3:1", StandardCharsets.UTF_8); - File xooFileB = new File(srcDirB, "sample.xoo"); - File xooUtCoverageFileB = new File(srcDirB, "sample.xoo.coverage"); + File xooFileB = new File(srcDirB, "sampleB.xoo"); + File xooUtCoverageFileB = new File(srcDirB, "sampleB.xoo.coverage"); FileUtils.write(xooFileB, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}", StandardCharsets.UTF_8); FileUtils.write(xooUtCoverageFileB, "2:2:2:1\n3:1", StandardCharsets.UTF_8); AnalysisResult result = tester.newAnalysis() .properties(ImmutableMap.builder() - .put("sonar.task", "scan") .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) .put("sonar.projectKey", "com.foo.project") .put("sonar.sources", "src") .put("sonar.modules", "moduleA,moduleB") - .put("sonar.coverage.exclusions", "src/sample.xoo") + .put("sonar.coverage.exclusions", "src/sampleA.xoo") .build()) .execute(); - InputFile fileA = result.inputFile("moduleA/src/sample.xoo"); + InputFile fileA = result.inputFile("moduleA/src/sampleA.xoo"); assertThat(result.coverageFor(fileA, 2)).isNull(); - InputFile fileB = result.inputFile("moduleB/src/sample.xoo"); - assertThat(result.coverageFor(fileB, 2)).isNull(); + InputFile fileB = result.inputFile("moduleB/src/sampleB.xoo"); + assertThat(result.coverageFor(fileB, 2)).isNotNull(); assertThat(logTester.logs(LoggerLevel.WARN)).contains("Specifying module-relative paths at project level in the property 'sonar.coverage.exclusions' is deprecated. " + - "To continue matching files like 'moduleA/src/sample.xoo', update this property so that patterns refer to project-relative paths."); + "To continue matching files like 'moduleA/src/sampleA.xoo', update this property so that patterns refer to project-relative paths."); } @Test @@ -225,7 +221,6 @@ public class CoverageMediumTest { AnalysisResult result = tester.newAnalysis() .properties(ImmutableMap.builder() - .put("sonar.task", "scan") .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) .put("sonar.projectKey", "com.foo.project") .put("sonar.sources", "src") @@ -257,7 +252,6 @@ public class CoverageMediumTest { AnalysisResult result = tester.newAnalysis() .properties(ImmutableMap.builder() - .put("sonar.task", "scan") .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) .put("sonar.projectKey", "com.foo.project") .put("sonar.sources", "src") @@ -302,7 +296,6 @@ public class CoverageMediumTest { AnalysisResult result = tester.newAnalysis() .properties(ImmutableMap.builder() - .put("sonar.task", "scan") .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) .put("sonar.projectKey", "com.foo.project") .put("sonar.modules", "module1") diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/cpd/CpdMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/cpd/CpdMediumTest.java index e31dfbb1b4e..d8900cfa569 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/cpd/CpdMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/cpd/CpdMediumTest.java @@ -32,6 +32,7 @@ import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; import org.sonar.scanner.mediumtest.ScannerMediumTester; import org.sonar.scanner.mediumtest.AnalysisResult; import org.sonar.scanner.protocol.output.ScannerReport; @@ -262,6 +263,96 @@ public class CpdMediumTest { assertThat(duplicationGroupsFile2).isEmpty(); } + @Test + public void cross_module_duplication() throws IOException { + + String duplicatedStuff = "Sample xoo\ncontent\n" + + "foo\nbar\ntoto\ntiti\n" + + "foo\nbar\ntoto\ntiti\n" + + "bar\ntoto\ntiti\n" + + "foo\nbar\ntoto\ntiti"; + + File baseDir = temp.getRoot(); + File baseDirModuleA = new File(baseDir, "moduleA"); + File baseDirModuleB = new File(baseDir, "moduleB"); + File srcDirA = new File(baseDirModuleA, "src"); + srcDirA.mkdirs(); + File srcDirB = new File(baseDirModuleB, "src"); + srcDirB.mkdirs(); + + File xooFileA = new File(srcDirA, "sampleA.xoo"); + FileUtils.write(xooFileA, duplicatedStuff, StandardCharsets.UTF_8); + + File xooFileB = new File(srcDirB, "sampleB.xoo"); + FileUtils.write(xooFileB, duplicatedStuff, StandardCharsets.UTF_8); + + AnalysisResult result = tester.newAnalysis() + .properties(ImmutableMap.builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.sources", "src") + .put("sonar.modules", "moduleA,moduleB") + .put("sonar.cpd.xoo.minimumTokens", "10") + .build()) + .execute(); + + InputFile inputFile1 = result.inputFile("moduleA/src/sampleA.xoo"); + InputFile inputFile2 = result.inputFile("moduleB/src/sampleB.xoo"); + + List duplicationGroupsFile1 = result.duplicationsFor(inputFile1); + assertThat(duplicationGroupsFile1).isNotEmpty(); + + List duplicationGroupsFile2 = result.duplicationsFor(inputFile2); + assertThat(duplicationGroupsFile2).isNotEmpty(); + } + + @Test + public void warn_user_for_outdated_inherited_scanner_side_exclusions_for_multi_module_project() throws IOException { + + String duplicatedStuff = "Sample xoo\ncontent\n" + + "foo\nbar\ntoto\ntiti\n" + + "foo\nbar\ntoto\ntiti\n" + + "bar\ntoto\ntiti\n" + + "foo\nbar\ntoto\ntiti"; + + File baseDir = temp.getRoot(); + File baseDirModuleA = new File(baseDir, "moduleA"); + File baseDirModuleB = new File(baseDir, "moduleB"); + File srcDirA = new File(baseDirModuleA, "src"); + srcDirA.mkdirs(); + File srcDirB = new File(baseDirModuleB, "src"); + srcDirB.mkdirs(); + + File xooFileA = new File(srcDirA, "sampleA.xoo"); + FileUtils.write(xooFileA, duplicatedStuff, StandardCharsets.UTF_8); + + File xooFileB = new File(srcDirB, "sampleB.xoo"); + FileUtils.write(xooFileB, duplicatedStuff, StandardCharsets.UTF_8); + + AnalysisResult result = tester.newAnalysis() + .properties(ImmutableMap.builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.sources", "src") + .put("sonar.modules", "moduleA,moduleB") + .put("sonar.cpd.xoo.minimumTokens", "10") + .put("sonar.cpd.exclusions", "src/sampleA.xoo") + .build()) + .execute(); + + InputFile inputFile1 = result.inputFile("moduleA/src/sampleA.xoo"); + InputFile inputFile2 = result.inputFile("moduleB/src/sampleB.xoo"); + + List duplicationGroupsFile1 = result.duplicationsFor(inputFile1); + assertThat(duplicationGroupsFile1).isEmpty(); + + List duplicationGroupsFile2 = result.duplicationsFor(inputFile2); + assertThat(duplicationGroupsFile2).isEmpty(); + + assertThat(logTester.logs(LoggerLevel.WARN)).contains("Specifying module-relative paths at project level in the property 'sonar.cpd.exclusions' is deprecated. " + + "To continue matching files like 'moduleA/src/sampleA.xoo', update this property so that patterns refer to project-relative paths."); + } + @Test public void enableCrossProjectDuplication() throws IOException { File srcDir = new File(baseDir, "src"); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleCoverageAndDuplicationExclusionsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleCoverageAndDuplicationExclusionsTest.java new file mode 100644 index 00000000000..63239c0924a --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleCoverageAndDuplicationExclusionsTest.java @@ -0,0 +1,74 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.phases; + +import java.io.File; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.scanner.scan.ModuleConfiguration; +import org.sonar.scanner.scan.filesystem.ModuleCoverageAndDuplicationExclusions; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ModuleCoverageAndDuplicationExclusionsTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private ModuleCoverageAndDuplicationExclusions coverageExclusions; + private File baseDir; + + @Before + public void prepare() throws Exception { + baseDir = temp.newFolder(); + } + + @Test + public void shouldExcludeFileBasedOnPattern() { + DefaultInputFile file = TestInputFileBuilder.create("foo", new File(baseDir, "moduleA"), new File(baseDir, "moduleA/src/org/polop/File.php")) + .setProjectBaseDir(baseDir.toPath()) + .build(); + coverageExclusions = new ModuleCoverageAndDuplicationExclusions(mockConfig("src/org/polop/*", "")); + assertThat(coverageExclusions.isExcludedForCoverage(file)).isTrue(); + } + + @Test + public void shouldNotExcludeFileBasedOnPattern() { + DefaultInputFile file = TestInputFileBuilder.create("foo", new File(baseDir, "moduleA"), new File(baseDir, "moduleA/src/org/polop/File.php")) + .setProjectBaseDir(baseDir.toPath()) + .build(); + coverageExclusions = new ModuleCoverageAndDuplicationExclusions(mockConfig("src/org/other/*", "")); + assertThat(coverageExclusions.isExcludedForCoverage(file)).isFalse(); + } + + private ModuleConfiguration mockConfig(String coverageExclusions, String cpdExclusions) { + ModuleConfiguration config = mock(ModuleConfiguration.class); + when(config.getStringArray(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY)).thenReturn(new String[] {coverageExclusions}); + when(config.getStringArray(CoreProperties.CPD_EXCLUSIONS)).thenReturn(new String[] {cpdExclusions}); + return config; + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleCoverageExclusionsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleCoverageExclusionsTest.java deleted file mode 100644 index 7a3287dd882..00000000000 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleCoverageExclusionsTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.phases; - -import java.io.File; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.fs.internal.TestInputFileBuilder; -import org.sonar.scanner.scan.ModuleConfiguration; -import org.sonar.scanner.scan.filesystem.ModuleCoverageExclusions; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class ModuleCoverageExclusionsTest { - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - private ModuleCoverageExclusions coverageExclusions; - private File baseDir; - - @Before - public void prepare() throws Exception { - baseDir = temp.newFolder(); - } - - @Test - public void shouldExcludeFileBasedOnPattern() { - DefaultInputFile file = TestInputFileBuilder.create("foo", new File(baseDir, "moduleA"), new File(baseDir, "moduleA/src/org/polop/File.php")) - .setProjectBaseDir(baseDir.toPath()) - .build(); - coverageExclusions = new ModuleCoverageExclusions(mockConfig("src/org/polop/*")); - assertThat(coverageExclusions.isExcluded(file)).isTrue(); - } - - @Test - public void shouldNotExcludeFileBasedOnPattern() { - DefaultInputFile file = TestInputFileBuilder.create("foo", new File(baseDir, "moduleA"), new File(baseDir, "moduleA/src/org/polop/File.php")) - .setProjectBaseDir(baseDir.toPath()) - .build(); - coverageExclusions = new ModuleCoverageExclusions(mockConfig("src/org/other/*")); - assertThat(coverageExclusions.isExcluded(file)).isFalse(); - } - - private ModuleConfiguration mockConfig(String... values) { - ModuleConfiguration config = mock(ModuleConfiguration.class); - when(config.getStringArray("sonar.coverage.exclusions")).thenReturn(values); - return config; - } -} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ProjectCoverageExclusionsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ProjectCoverageExclusionsTest.java index 822c44b6c03..df7fe533b6f 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ProjectCoverageExclusionsTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ProjectCoverageExclusionsTest.java @@ -24,10 +24,11 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.scanner.scan.ProjectConfiguration; -import org.sonar.scanner.scan.filesystem.ProjectCoverageExclusions; +import org.sonar.scanner.scan.filesystem.ProjectCoverageAndDuplicationExclusions; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -38,7 +39,7 @@ public class ProjectCoverageExclusionsTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); - private ProjectCoverageExclusions coverageExclusions; + private ProjectCoverageAndDuplicationExclusions underTest; private File baseDir; @Before @@ -47,26 +48,49 @@ public class ProjectCoverageExclusionsTest { } @Test - public void shouldExcludeFileBasedOnPattern() { + public void shouldExcludeFileCoverageBasedOnPattern() { DefaultInputFile file = TestInputFileBuilder.create("foo", new File(baseDir, "moduleA"), new File(baseDir, "moduleA/src/org/polop/File.php")) .setProjectBaseDir(baseDir.toPath()) .build(); - coverageExclusions = new ProjectCoverageExclusions(mockConfig("moduleA/src/org/polop/*")); - assertThat(coverageExclusions.isExcluded(file)).isTrue(); + underTest = new ProjectCoverageAndDuplicationExclusions(mockConfig("moduleA/src/org/polop/*", "")); + assertThat(underTest.isExcludedForCoverage(file)).isTrue(); + assertThat(underTest.isExcludedForDuplication(file)).isFalse(); } @Test - public void shouldNotExcludeFileBasedOnPattern() { + public void shouldNotExcludeFileCoverageBasedOnPattern() { DefaultInputFile file = TestInputFileBuilder.create("foo", new File(baseDir, "moduleA"), new File(baseDir, "moduleA/src/org/polop/File.php")) .setProjectBaseDir(baseDir.toPath()) .build(); - coverageExclusions = new ProjectCoverageExclusions(mockConfig("moduleA/src/org/other/*")); - assertThat(coverageExclusions.isExcluded(file)).isFalse(); + underTest = new ProjectCoverageAndDuplicationExclusions(mockConfig("moduleA/src/org/other/*", "")); + assertThat(underTest.isExcludedForCoverage(file)).isFalse(); + assertThat(underTest.isExcludedForDuplication(file)).isFalse(); } - private ProjectConfiguration mockConfig(String... values) { + @Test + public void shouldExcludeFileDuplicationBasedOnPattern() { + DefaultInputFile file = TestInputFileBuilder.create("foo", new File(baseDir, "moduleA"), new File(baseDir, "moduleA/src/org/polop/File.php")) + .setProjectBaseDir(baseDir.toPath()) + .build(); + underTest = new ProjectCoverageAndDuplicationExclusions(mockConfig("", "moduleA/src/org/polop/*")); + assertThat(underTest.isExcludedForCoverage(file)).isFalse(); + assertThat(underTest.isExcludedForDuplication(file)).isTrue(); + } + + @Test + public void shouldNotExcludeFileDuplicationBasedOnPattern() { + DefaultInputFile file = TestInputFileBuilder.create("foo", new File(baseDir, "moduleA"), new File(baseDir, "moduleA/src/org/polop/File.php")) + .setProjectBaseDir(baseDir.toPath()) + .build(); + underTest = new ProjectCoverageAndDuplicationExclusions(mockConfig("", "moduleA/src/org/other/*")); + assertThat(underTest.isExcludedForCoverage(file)).isFalse(); + assertThat(underTest.isExcludedForDuplication(file)).isFalse(); + } + + private ProjectConfiguration mockConfig(String coverageExclusions, String cpdExclusions) { ProjectConfiguration config = mock(ProjectConfiguration.class); - when(config.getStringArray("sonar.coverage.exclusions")).thenReturn(values); + when(config.getStringArray(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY)).thenReturn(new String[] {coverageExclusions}); + when(config.getStringArray(CoreProperties.CPD_EXCLUSIONS)).thenReturn(new String[] {cpdExclusions}); return config; }