]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11508 Rework inclusion/exclusion to restore support of module overrides
authorJulien HENRY <julien.henry@sonarsource.com>
Thu, 3 Jan 2019 16:36:03 +0000 (17:36 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 16 Jan 2019 08:43:11 +0000 (09:43 +0100)
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/AbstractExclusionFilters.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ProjectFileIndexer.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/cpd/CpdMediumTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/ProjectExclusionFiltersTest.java

index 89809081735224805505d5e0be2cb346a6c09d5d..0a74fad22617aa3a8c71588367e45308753d6ffa 100644 (file)
@@ -91,38 +91,6 @@ public abstract class AbstractExclusionFilters {
     }
   }
 
-  public boolean accept(Path absolutePath, Path relativePath, InputFile.Type type) {
-    PathPattern[] inclusionPatterns;
-    PathPattern[] exclusionPatterns;
-    if (InputFile.Type.MAIN == type) {
-      inclusionPatterns = mainInclusionsPattern;
-      exclusionPatterns = mainExclusionsPattern;
-    } else if (InputFile.Type.TEST == type) {
-      inclusionPatterns = testInclusionsPattern;
-      exclusionPatterns = testExclusionsPattern;
-    } else {
-      throw new IllegalArgumentException("Unknown file type: " + type);
-    }
-
-    if (inclusionPatterns.length > 0) {
-      boolean matchInclusion = false;
-      for (PathPattern pattern : inclusionPatterns) {
-        matchInclusion |= pattern.match(absolutePath, relativePath);
-      }
-      if (!matchInclusion) {
-        return false;
-      }
-    }
-    if (exclusionPatterns.length > 0) {
-      for (PathPattern pattern : exclusionPatterns) {
-        if (pattern.match(absolutePath, relativePath)) {
-          return false;
-        }
-      }
-    }
-    return true;
-  }
-
   private static PathPattern[] prepareMainInclusions(String[] sourceInclusions) {
     if (sourceInclusions.length > 0) {
       // User defined params
@@ -145,27 +113,43 @@ public abstract class AbstractExclusionFilters {
     return PathPattern.create(testExclusions);
   }
 
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
+  public String[] getInclusionsConfig(InputFile.Type type) {
+    return type == InputFile.Type.MAIN ? sourceInclusions : testInclusions;
+  }
+
+  public String[] getExclusionsConfig(InputFile.Type type) {
+    return type == InputFile.Type.MAIN ? sourceExclusions : testExclusions;
+  }
+
+  public boolean isIncluded(Path absolutePath, Path relativePath, InputFile.Type type) {
+    PathPattern[] inclusionPatterns = InputFile.Type.MAIN == type ? mainInclusionsPattern : testInclusionsPattern;
+
+    if (inclusionPatterns.length == 0) {
       return true;
     }
-    if (!(o instanceof AbstractExclusionFilters)) {
-      return false;
+
+    for (PathPattern pattern : inclusionPatterns) {
+      if (pattern.match(absolutePath, relativePath)) {
+        return true;
+      }
     }
-    AbstractExclusionFilters that = (AbstractExclusionFilters) o;
-    return Arrays.equals(sourceInclusions, that.sourceInclusions) &&
-      Arrays.equals(testInclusions, that.testInclusions) &&
-      Arrays.equals(sourceExclusions, that.sourceExclusions) &&
-      Arrays.equals(testExclusions, that.testExclusions);
+
+    return false;
   }
 
-  @Override
-  public int hashCode() {
-    int result = Arrays.hashCode(sourceInclusions);
-    result = 31 * result + Arrays.hashCode(testInclusions);
-    result = 31 * result + Arrays.hashCode(sourceExclusions);
-    result = 31 * result + Arrays.hashCode(testExclusions);
-    return result;
+  public boolean isExcluded(Path absolutePath, Path relativePath, InputFile.Type type) {
+    PathPattern[] exclusionPatterns = InputFile.Type.MAIN == type ? mainExclusionsPattern : testExclusionsPattern;
+
+    if (exclusionPatterns.length == 0) {
+      return false;
+    }
+
+    for (PathPattern pattern : exclusionPatterns) {
+      if (pattern.match(absolutePath, relativePath)) {
+        return true;
+      }
+    }
+
+    return false;
   }
 }
index 60798fe990b9b7f4177746352cc1d1f52e0040b6..2464a6350c7efd91fed31f76ba845e7eeca44700 100644 (file)
@@ -24,6 +24,8 @@ import java.nio.file.LinkOption;
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BooleanSupplier;
+import org.apache.commons.io.FilenameUtils;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.InputFile.Type;
@@ -60,14 +62,15 @@ public class FileIndexer {
   private final SensorStrategy sensorStrategy;
   private final LanguageDetection langDetection;
 
+  private boolean warnInclusionsAlreadyLogged;
   private boolean warnExclusionsAlreadyLogged;
   private boolean warnCoverageExclusionsAlreadyLogged;
   private boolean warnDuplicationExclusionsAlreadyLogged;
 
   public FileIndexer(DefaultInputProject project, ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore,
-                     ProjectExclusionFilters projectExclusionFilters, ProjectCoverageAndDuplicationExclusions projectCoverageAndDuplicationExclusions, 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;
@@ -83,17 +86,19 @@ public class FileIndexer {
   }
 
   public FileIndexer(DefaultInputProject project, ScannerComponentIdGenerator scannerComponentIdGenerator, InputComponentStore componentStore,
-                     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,
+    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, ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, Path sourceFile,
-                        InputFile.Type type, ProgressReport progressReport,
-                        AtomicInteger excludedByPatternsCount)
+  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();
@@ -103,17 +108,13 @@ public class FileIndexer {
     }
     Path projectRelativePath = project.getBaseDir().relativize(realAbsoluteFile);
     Path moduleRelativePath = module.getBaseDir().relativize(realAbsoluteFile);
-    if (!projectExclusionFilters.accept(realAbsoluteFile, projectRelativePath, type)) {
+    boolean included = evaluateInclusionsFilters(moduleExclusionFilters, realAbsoluteFile, projectRelativePath, moduleRelativePath, type);
+    if (!included) {
       excludedByPatternsCount.incrementAndGet();
       return;
     }
-    if (!moduleExclusionFilters.accept(realAbsoluteFile, moduleRelativePath, type)) {
-      if (projectExclusionFilters.equals(moduleExclusionFilters)) {
-        warnOnceDeprecatedExclusion(
-          "Specifying module-relative paths at project level in the files exclusions/inclusions properties is deprecated. " +
-            "To continue matching files like '" + projectRelativePath + "', " +
-            "update these properties so that patterns refer to project-relative paths.");
-      }
+    boolean excluded = evaluateExclusionsFilters(moduleExclusionFilters, realAbsoluteFile, projectRelativePath, moduleRelativePath, type);
+    if (excluded) {
       excludedByPatternsCount.incrementAndGet();
       return;
     }
@@ -146,6 +147,42 @@ public class FileIndexer {
     progressReport.message(count + " " + pluralizeFiles(count) + " indexed...  (last one was " + inputFile.getProjectRelativePath() + ")");
   }
 
+  private boolean evaluateInclusionsFilters(ModuleExclusionFilters moduleExclusionFilters, Path realAbsoluteFile, Path projectRelativePath, Path moduleRelativePath,
+    InputFile.Type type) {
+    if (!Arrays.equals(moduleExclusionFilters.getInclusionsConfig(type), projectExclusionFilters.getInclusionsConfig(type))) {
+      // Module specific configuration
+      return moduleExclusionFilters.isIncluded(realAbsoluteFile, moduleRelativePath, type);
+    }
+    boolean includedByProjectConfiguration = projectExclusionFilters.isIncluded(realAbsoluteFile, projectRelativePath, type);
+    if (includedByProjectConfiguration) {
+      return true;
+    } else if (moduleExclusionFilters.isIncluded(realAbsoluteFile, moduleRelativePath, type)) {
+      warnOnce(
+        type == Type.MAIN ? CoreProperties.PROJECT_INCLUSIONS_PROPERTY : CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY,
+        FilenameUtils.normalize(projectRelativePath.toString(), true), () -> warnInclusionsAlreadyLogged, () -> warnInclusionsAlreadyLogged = true);
+      return true;
+    }
+    return false;
+  }
+
+  private boolean evaluateExclusionsFilters(ModuleExclusionFilters moduleExclusionFilters, Path realAbsoluteFile, Path projectRelativePath, Path moduleRelativePath,
+    InputFile.Type type) {
+    if (!Arrays.equals(moduleExclusionFilters.getExclusionsConfig(type), projectExclusionFilters.getExclusionsConfig(type))) {
+      // Module specific configuration
+      return moduleExclusionFilters.isExcluded(realAbsoluteFile, moduleRelativePath, type);
+    }
+    boolean includedByProjectConfiguration = projectExclusionFilters.isExcluded(realAbsoluteFile, projectRelativePath, type);
+    if (includedByProjectConfiguration) {
+      return true;
+    } else if (moduleExclusionFilters.isExcluded(realAbsoluteFile, moduleRelativePath, type)) {
+      warnOnce(
+        type == Type.MAIN ? CoreProperties.PROJECT_EXCLUSIONS_PROPERTY : CoreProperties.PROJECT_TEST_EXCLUSIONS_PROPERTY,
+        FilenameUtils.normalize(projectRelativePath.toString(), true), () -> warnExclusionsAlreadyLogged, () -> warnExclusionsAlreadyLogged = true);
+      return true;
+    }
+    return false;
+  }
+
   private void checkIfAlreadyIndexed(DefaultInputFile inputFile) {
     if (componentStore.inputFile(inputFile.getProjectRelativePath()) != null) {
       throw MessageException.of("File " + inputFile + " can't be indexed twice. Please check that inclusion/exclusion patterns produce "
@@ -154,58 +191,60 @@ public class FileIndexer {
   }
 
   private void evaluateCoverageExclusions(ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, DefaultInputFile inputFile) {
+    boolean excludedForCoverage = isExcludedForCoverage(moduleCoverageAndDuplicationExclusions, inputFile);
+    inputFile.setExcludedForCoverage(excludedForCoverage);
+    if (excludedForCoverage) {
+      LOG.debug("File {} excluded for coverage", inputFile);
+    }
+  }
+
+  private boolean isExcludedForCoverage(ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, DefaultInputFile inputFile) {
+    if (!Arrays.equals(moduleCoverageAndDuplicationExclusions.getCoverageExclusionConfig(), projectCoverageAndDuplicationExclusions.getCoverageExclusionConfig())) {
+      // Module specific configuration
+      return moduleCoverageAndDuplicationExclusions.isExcludedForCoverage(inputFile);
+    }
     boolean excludedByProjectConfiguration = projectCoverageAndDuplicationExclusions.isExcludedForCoverage(inputFile);
     if (excludedByProjectConfiguration) {
-      inputFile.setExcludedForCoverage(true);
-      LOG.debug("File {} excluded for coverage", inputFile);
+      return true;
     } else if (moduleCoverageAndDuplicationExclusions.isExcludedForCoverage(inputFile)) {
-      inputFile.setExcludedForCoverage(true);
-      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.");
-      }
-      LOG.debug("File {} excluded for coverage", inputFile);
+      warnOnce(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY, inputFile.getProjectRelativePath(), () -> warnCoverageExclusionsAlreadyLogged,
+        () -> warnCoverageExclusionsAlreadyLogged = true);
+      return true;
     }
+    return false;
   }
 
   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.");
-      }
+    boolean excludedForDuplications = isExcludedForDuplications(moduleCoverageAndDuplicationExclusions, inputFile);
+    inputFile.setExcludedForDuplication(excludedForDuplications);
+    if (excludedForDuplications) {
       LOG.debug("File {} excluded for duplication", inputFile);
     }
   }
 
-  private void warnOnceDeprecatedExclusion(String msg) {
-    if (!warnExclusionsAlreadyLogged) {
-      LOG.warn(msg);
-      analysisWarnings.addUnique(msg);
-      warnExclusionsAlreadyLogged = true;
+  private boolean isExcludedForDuplications(ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, DefaultInputFile inputFile) {
+    if (!Arrays.equals(moduleCoverageAndDuplicationExclusions.getDuplicationExclusionConfig(), projectCoverageAndDuplicationExclusions.getDuplicationExclusionConfig())) {
+      // Module specific configuration
+      return moduleCoverageAndDuplicationExclusions.isExcludedForDuplication(inputFile);
     }
-  }
-
-  private void warnOnceDeprecatedCoverageExclusion(String msg) {
-    if (!warnCoverageExclusionsAlreadyLogged) {
-      LOG.warn(msg);
-      analysisWarnings.addUnique(msg);
-      warnCoverageExclusionsAlreadyLogged = true;
+    boolean excludedByProjectConfiguration = projectCoverageAndDuplicationExclusions.isExcludedForDuplication(inputFile);
+    if (excludedByProjectConfiguration) {
+      return true;
+    } else if (moduleCoverageAndDuplicationExclusions.isExcludedForDuplication(inputFile)) {
+      warnOnce(CoreProperties.CPD_EXCLUSIONS, inputFile.getProjectRelativePath(), () -> warnDuplicationExclusionsAlreadyLogged,
+        () -> warnDuplicationExclusionsAlreadyLogged = true);
+      return true;
     }
+    return false;
   }
 
-  private void warnOnceDeprecatedDuplicationExclusion(String msg) {
-    if (!warnDuplicationExclusionsAlreadyLogged) {
+  private void warnOnce(String propKey, String filePath, BooleanSupplier alreadyLoggedGetter, Runnable markAsLogged) {
+    if (!alreadyLoggedGetter.getAsBoolean()) {
+      String msg = "Specifying module-relative paths at project level in the property '" + propKey + "' is deprecated. " +
+        "To continue matching files like '" + filePath + "', update this property so that patterns refer to project-relative paths.";
       LOG.warn(msg);
       analysisWarnings.addUnique(msg);
-      warnDuplicationExclusionsAlreadyLogged = true;
+      markAsLogged.run();
     }
   }
 
index a0372a6d6330b11195bb7985c4d9c9e48906234d..83d1382ec3e625db74ac25962e4552e06a74ac08 100644 (file)
@@ -84,8 +84,8 @@ public class ProjectFileIndexer {
     int totalIndexed = componentStore.inputFiles().size();
     progressReport.stop(totalIndexed + " " + pluralizeFiles(totalIndexed) + " indexed");
 
-    if (projectExclusionFilters.hasPattern()) {
-      int excludedFileCount = excludedByPatternsCount.get();
+    int excludedFileCount = excludedByPatternsCount.get();
+    if (projectExclusionFilters.hasPattern() || excludedFileCount > 0) {
       LOG.info("{} {} ignored because of inclusion/exclusion patterns", excludedFileCount, pluralizeFiles(excludedFileCount));
     }
   }
@@ -139,8 +139,8 @@ public class ProjectFileIndexer {
     return count == 1 ? "file" : "files";
   }
 
-  private void indexFiles(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, List<Path> sources,
-                          Type type, AtomicInteger excludedByPatternsCount) {
+  private void indexFiles(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions,
+    List<Path> sources, Type type, AtomicInteger excludedByPatternsCount) {
     try {
       for (Path dirOrFile : sources) {
         if (dirOrFile.toFile().isDirectory()) {
@@ -154,8 +154,8 @@ public class ProjectFileIndexer {
     }
   }
 
-  private void indexDirectory(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, 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, moduleCoverageAndDuplicationExclusions, type, excludedByPatternsCount));
@@ -168,8 +168,9 @@ public class ProjectFileIndexer {
     private final Type type;
     private final AtomicInteger excludedByPatternsCount;
 
-    IndexFileVisitor(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions, Type type,
-                     AtomicInteger excludedByPatternsCount) {
+    IndexFileVisitor(DefaultInputModule module, ModuleExclusionFilters moduleExclusionFilters, ModuleCoverageAndDuplicationExclusions moduleCoverageAndDuplicationExclusions,
+      Type type,
+      AtomicInteger excludedByPatternsCount) {
       this.module = module;
       this.moduleExclusionFilters = moduleExclusionFilters;
       this.moduleCoverageAndDuplicationExclusions = moduleCoverageAndDuplicationExclusions;
index 6b622327bccffad59de8b940de6b0e8d65853549..0b6744565debb931af820a883857b38281c2e190 100644 (file)
@@ -196,6 +196,45 @@ public class CoverageMediumTest {
       "To continue matching files like 'moduleA/src/sampleA.xoo', update this property so that patterns refer to project-relative paths.");
   }
 
+  @Test
+  public void module_level_exclusions_override_parent_for_multi_module_project() throws IOException {
+
+    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");
+    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, "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.<String, String>builder()
+        .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+        .put("sonar.projectKey", "com.foo.project")
+        .put("sonar.sources", "src")
+        .put("sonar.modules", "moduleA,moduleB")
+        .put("sonar.coverage.exclusions", "**/*.xoo")
+        .put("moduleA.sonar.coverage.exclusions", "**/*.nothing")
+        .build())
+      .execute();
+
+    InputFile fileA = result.inputFile("moduleA/src/sampleA.xoo");
+    assertThat(result.coverageFor(fileA, 2)).isNotNull();
+
+    InputFile fileB = result.inputFile("moduleB/src/sampleB.xoo");
+    assertThat(result.coverageFor(fileB, 2)).isNull();
+  }
+
   @Test
   public void warn_user_for_outdated_server_side_exclusions_for_multi_module_project() throws IOException {
 
index d8900cfa569ebd20059f82591e161341b5e628ef..eaea6f109ea299dd1bd4c3fbe36895e8c7506db7 100644 (file)
@@ -353,6 +353,52 @@ public class CpdMediumTest {
       "To continue matching files like 'moduleA/src/sampleA.xoo', update this property so that patterns refer to project-relative paths.");
   }
 
+  @Test
+  public void module_level_exclusions_override_parent_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.<String, String>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", "**/*")
+        .put("moduleA.sonar.cpd.exclusions", "**/*.nothing")
+        .put("moduleB.sonar.cpd.exclusions", "**/*.nothing")
+        .build())
+      .execute();
+
+    InputFile inputFile1 = result.inputFile("moduleA/src/sampleA.xoo");
+    InputFile inputFile2 = result.inputFile("moduleB/src/sampleB.xoo");
+
+    List<ScannerReport.Duplication> duplicationGroupsFile1 = result.duplicationsFor(inputFile1);
+    assertThat(duplicationGroupsFile1).isNotEmpty();
+
+    List<ScannerReport.Duplication> duplicationGroupsFile2 = result.duplicationsFor(inputFile2);
+    assertThat(duplicationGroupsFile2).isNotEmpty();
+  }
+
   @Test
   public void enableCrossProjectDuplication() throws IOException {
     File srcDir = new File(baseDir, "src");
index 293ef226b5f8d3424ea3ab0865f6614af922f5f9..1eaec75bde60637438254fe04f379c1e3fc79856 100644 (file)
@@ -508,6 +508,99 @@ public class FileSystemMediumTest {
     assertThat(result.inputFiles()).hasSize(2);
   }
 
+  @Test
+  public void test_inclusions_on_multi_modules() throws IOException {
+    File baseDir = temp.getRoot();
+    File baseDirModuleA = new File(baseDir, "moduleA");
+    File baseDirModuleB = new File(baseDir, "moduleB");
+    File srcDirA = new File(baseDirModuleA, "tests");
+    srcDirA.mkdirs();
+    File srcDirB = new File(baseDirModuleB, "tests");
+    srcDirB.mkdirs();
+
+    File xooFileA = new File(srcDirA, "sampleTestA.xoo");
+    FileUtils.write(xooFileA, "Sample xoo\ncontent", StandardCharsets.UTF_8);
+
+    File xooFileB = new File(srcDirB, "sampleTestB.xoo");
+    FileUtils.write(xooFileB, "Sample xoo\ncontent", StandardCharsets.UTF_8);
+
+    final ImmutableMap.Builder<String, String> builder = ImmutableMap.<String, String>builder()
+      .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+      .put("sonar.projectKey", "com.foo.project")
+      .put("sonar.sources", "")
+      .put("sonar.tests", "tests")
+      .put("sonar.modules", "moduleA,moduleB");
+
+
+    AnalysisResult result = tester.newAnalysis()
+      .properties(builder.build())
+      .execute();
+
+    assertThat(result.inputFiles()).hasSize(2);
+    
+    InputFile fileA = result.inputFile("moduleA/tests/sampleTestA.xoo");
+    assertThat(fileA).isNotNull();
+
+    InputFile fileB = result.inputFile("moduleB/tests/sampleTestB.xoo");
+    assertThat(fileB).isNotNull();
+
+    result = tester.newAnalysis()
+      .properties(builder
+        .put("sonar.test.inclusions", "moduleA/tests/**")
+        .build())
+      .execute();
+
+    assertThat(result.inputFiles()).hasSize(1);
+
+    fileA = result.inputFile("moduleA/tests/sampleTestA.xoo");
+    assertThat(fileA).isNotNull();
+
+    fileB = result.inputFile("moduleB/tests/sampleTestB.xoo");
+    assertThat(fileB).isNull();
+  }
+
+  @Test
+  public void test_module_level_inclusions_override_parent_on_multi_modules() throws IOException {
+    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, "Sample xoo\ncontent", StandardCharsets.UTF_8);
+
+    File xooFileB = new File(srcDirB, "sampleB.xoo");
+    FileUtils.write(xooFileB, "Sample xoo\ncontent", StandardCharsets.UTF_8);
+
+    final ImmutableMap.Builder<String, String> builder = ImmutableMap.<String, String>builder()
+      .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+      .put("sonar.projectKey", "com.foo.project")
+      .put("sonar.sources", "src")
+      .put("sonar.modules", "moduleA,moduleB")
+      .put("sonar.inclusions", "**/*.php");
+
+
+    AnalysisResult result = tester.newAnalysis()
+      .properties(builder.build())
+      .execute();
+
+    assertThat(result.inputFiles()).isEmpty();
+
+    result = tester.newAnalysis()
+      .properties(builder
+        .put("moduleA.sonar.inclusions", "**/*.xoo")
+        .build())
+      .execute();
+
+    assertThat(result.inputFiles()).hasSize(1);
+
+    InputFile fileA = result.inputFile("moduleA/src/sampleA.xoo");
+    assertThat(fileA).isNotNull();
+  }
+
   @Test
   public void warn_user_for_outdated_scanner_side_inherited_exclusions_for_multi_module_project() throws IOException {
     File baseDir = temp.getRoot();
@@ -526,7 +619,6 @@ public class FileSystemMediumTest {
 
     AnalysisResult result = tester.newAnalysis()
       .properties(ImmutableMap.<String, String>builder()
-        .put("sonar.task", "scan")
         .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
         .put("sonar.projectKey", "com.foo.project")
         .put("sonar.sources", "src")
@@ -542,8 +634,8 @@ public class FileSystemMediumTest {
     assertThat(fileB).isNull();
 
     assertThat(logTester.logs(LoggerLevel.WARN))
-      .contains("Specifying module-relative paths at project level in the files exclusions/inclusions properties is deprecated. " +
-        "To continue matching files like 'moduleA/src/sample.xoo', update these properties so that patterns refer to project-relative paths.");
+      .contains("Specifying module-relative paths at project level in the property 'sonar.exclusions' is deprecated. " +
+        "To continue matching files like 'moduleA/src/sample.xoo', update this property so that patterns refer to project-relative paths.");
   }
 
   @Test
@@ -566,7 +658,6 @@ public class FileSystemMediumTest {
 
     AnalysisResult result = tester.newAnalysis()
       .properties(ImmutableMap.<String, String>builder()
-        .put("sonar.task", "scan")
         .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
         .put("sonar.projectKey", "com.foo.project")
         .put("sonar.sources", "src")
@@ -581,8 +672,8 @@ public class FileSystemMediumTest {
     assertThat(fileB).isNull();
 
     assertThat(logTester.logs(LoggerLevel.WARN))
-      .contains("Specifying module-relative paths at project level in the files exclusions/inclusions properties is deprecated. " +
-        "To continue matching files like 'moduleA/src/sample.xoo', update these properties so that patterns refer to project-relative paths.");
+      .contains("Specifying module-relative paths at project level in the property 'sonar.exclusions' is deprecated. " +
+        "To continue matching files like 'moduleA/src/sample.xoo', update this property so that patterns refer to project-relative paths.");
   }
 
   @Test
index a4f0a30761df8f3f87ca6650cc3c2ceedaec63c3..7e6540f69f1e87799308d90b8bc4599a08ae245e 100644 (file)
@@ -53,8 +53,10 @@ public class ProjectExclusionFiltersTest {
     ProjectExclusionFilters filter = new ProjectExclusionFilters(settings.asConfig());
 
     IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null);
-    assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue();
-    assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.TEST)).isTrue();
+    assertThat(filter.isExcluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isFalse();
+    assertThat(filter.isExcluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.TEST)).isFalse();
+    assertThat(filter.isIncluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue();
+    assertThat(filter.isIncluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.TEST)).isTrue();
   }
 
   @Test
@@ -63,10 +65,10 @@ public class ProjectExclusionFiltersTest {
     ProjectExclusionFilters filter = new ProjectExclusionFilters(settings.asConfig());
 
     IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null);
-    assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue();
+    assertThat(filter.isIncluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue();
 
     indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java", null);
-    assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isFalse();
+    assertThat(filter.isIncluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isFalse();
   }
 
   @Test
@@ -75,10 +77,10 @@ public class ProjectExclusionFiltersTest {
     ProjectExclusionFilters filter = new ProjectExclusionFilters(settings.asConfig());
 
     IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java", null);
-    assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isFalse();
+    assertThat(filter.isIncluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isFalse();
 
     indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDto.java", null);
-    assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue();
+    assertThat(filter.isIncluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue();
   }
 
   @Test
@@ -89,14 +91,14 @@ public class ProjectExclusionFiltersTest {
     ProjectExclusionFilters filter = new ProjectExclusionFilters(settings.asConfig());
 
     IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null);
-    assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isFalse();
+    assertThat(filter.isExcluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue();
 
     indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java", null);
-    assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue();
+    assertThat(filter.isExcluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isFalse();
 
     // source exclusions do not apply to tests
     indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/test/java/com/mycompany/FooDao.java", null);
-    assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.TEST)).isTrue();
+    assertThat(filter.isExcluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.TEST)).isFalse();
   }
 
   @Test
@@ -108,10 +110,10 @@ public class ProjectExclusionFiltersTest {
     ProjectExclusionFilters filter = new ProjectExclusionFilters(settings.asConfig());
 
     IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/org/bar/Foo.java", null);
-    assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue();
+    assertThat(filter.isExcluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isFalse();
 
     indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/org/bar/Bar.java", null);
-    assertThat(filter.accept(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isFalse();
+    assertThat(filter.isExcluded(indexedFile.path(), Paths.get(indexedFile.relativePath()), InputFile.Type.MAIN)).isTrue();
   }
 
   @Test