]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21575 Populate the issue filter chain after optional plugins have been loaded
authorJulien HENRY <julien.henry@sonarsource.com>
Thu, 8 Feb 2024 16:35:55 +0000 (17:35 +0100)
committersonartech <sonartech@sonarsource.com>
Fri, 9 Feb 2024 20:02:43 +0000 (20:02 +0000)
Optional plugins can contribute IssueFilter, so we should not load the list of extensions too early.
Moving the IssueFilters class in the child container was not possible due to many dependencies, so the choice has been made to make it "mutable": it is instantiated early to allow injection, but the list of IssueFilter is populated later.

sonar-core/src/main/java/org/sonar/core/platform/SpringComponentContainer.java
sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumIT.java
sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/optionalplugins/OptionalPluginsMediumIT.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/AbstractExtensionDictionary.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueFilterChain.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilterExtensionDictionary.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/SpringProjectScanContainer.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultIssueFilterChainTest.java
sonar-scanner-engine/test-resources/mediumtest/xoo/sample-with-input-file-filters/xources/hello/xoo_exclude3.xoo [new file with mode: 0644]

index 362b26b7e24e4536079f61db875e825281fb8c16..4d9332f374175604804d094eff0f2d06d46017f8 100644 (file)
@@ -183,6 +183,9 @@ public class SpringComponentContainer implements StartableContainer {
     }
   }
 
+  /**
+   * Lookup all beans of a given type but only in this container, not in its parents.
+   */
   @Override
   public <T> List<T> getComponentsByType(Class<T> type) {
     try {
index 719b060cc279093b01ea9f4b5edf1e972a13291e..d160bd3f50e4d1f6e0abff23bb67733f2957602d 100644 (file)
@@ -28,9 +28,7 @@ import java.nio.file.Files;
 import java.nio.file.LinkOption;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.Map;
 import java.util.Random;
-import java.util.Set;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.SystemUtils;
@@ -41,10 +39,8 @@ import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.slf4j.event.Level;
 import org.sonar.api.CoreProperties;
-import org.sonar.api.Plugin;
 import org.sonar.api.SonarEdition;
 import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputFileFilter;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.testfixtures.log.LogTester;
 import org.sonar.api.utils.MessageException;
@@ -65,7 +61,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.api.Assertions.tuple;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
-import static org.sonar.core.config.ScannerProperties.PLUGIN_LOADING_OPTIMIZATION_KEY;
 
 public class FileSystemMediumIT {
 
@@ -79,7 +74,6 @@ public class FileSystemMediumIT {
   public ScannerMediumTester tester = new ScannerMediumTester()
     .setEdition(SonarEdition.COMMUNITY)
     .registerPlugin("xoo", new XooPlugin())
-    .registerOptionalPlugin("optional-xoo", Set.of("xoo"), new OptionalXooPlugin())
     .addDefaultQProfile("xoo", "Sonar Way")
     .addDefaultQProfile("xoo2", "Sonar Way");
 
@@ -1261,42 +1255,6 @@ public class FileSystemMediumIT {
       .hasMessageEndingWith(format("Failed to preprocess files"));
   }
 
-  @Test
-  public void should_load_input_file_filters_for_required_and_optional_plugins() throws IOException {
-    File projectDir = new File("test-resources/mediumtest/xoo/sample-with-input-file-filters");
-    AnalysisResult result = tester
-      .newAnalysis(new File(projectDir, "sonar-project.properties"))
-      .properties(Map.of(PLUGIN_LOADING_OPTIMIZATION_KEY, "true"))
-      .execute();
-
-    assertThat(result.inputFiles()).hasSize(1);
-
-    assertThat(logTester.logs()).contains("'xources/hello/xoo_exclude2.xoo' excluded by org.sonar.scanner.mediumtest.fs" +
-      ".FileSystemMediumIT$OptionalXooPlugin$OptionalXooFileFilter");
-    assertThat(logTester.logs()).contains("'xources/hello/xoo_exclude.xoo' excluded by org.sonar.xoo.extensions.XooExcludeFileFilter");
-    assertThat(logTester.logs()).contains("'xources/hello/HelloJava.xoo' indexed with language 'xoo'");
-
-    assertThat(result.inputFile("xources/hello/xoo_exclude.xoo")).isNull();
-    assertThat(result.inputFile("xources/hello/xoo_exclude2.xoo")).isNull();
-    assertThat(result.inputFile("xources/hello/HelloJava.xoo")).isNotNull();
-  }
-
-  public static class OptionalXooPlugin implements Plugin {
-
-    @Override
-    public void define(Context context) {
-      context.addExtension(OptionalXooFileFilter.class);
-    }
-
-    public static class OptionalXooFileFilter implements InputFileFilter {
-
-      @Override
-      public boolean accept(InputFile f) {
-        return !f.filename().endsWith("_exclude2.xoo");
-      }
-    }
-  }
-
   private static void assertAnalysedFiles(AnalysisResult result, String... files) {
     assertThat(result.inputFiles().stream().map(InputFile::toString).toList()).contains(files);
   }
diff --git a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/optionalplugins/OptionalPluginsMediumIT.java b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/optionalplugins/OptionalPluginsMediumIT.java
new file mode 100644 (file)
index 0000000..e618719
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.mediumtest.optionalplugins;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.event.Level;
+import org.sonar.api.Plugin;
+import org.sonar.api.SonarEdition;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputFileFilter;
+import org.sonar.api.batch.measure.MetricFinder;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.FileLinesContextFactory;
+import org.sonar.api.scan.issue.filter.FilterableIssue;
+import org.sonar.api.scan.issue.filter.IssueFilter;
+import org.sonar.api.scan.issue.filter.IssueFilterChain;
+import org.sonar.api.scanner.sensor.ProjectSensor;
+import org.sonar.api.testfixtures.log.LogTester;
+import org.sonar.scanner.mediumtest.AnalysisResult;
+import org.sonar.scanner.mediumtest.ScannerMediumTester;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.core.config.ScannerProperties.PLUGIN_LOADING_OPTIMIZATION_KEY;
+
+public class OptionalPluginsMediumIT {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @Rule
+  public LogTester logTester = new LogTester();
+
+  @Rule
+  public ScannerMediumTester tester = new ScannerMediumTester()
+    .setEdition(SonarEdition.COMMUNITY)
+    .registerPlugin("xoo", new XooPlugin())
+    .registerPlugin("nonoptional-plugin", new NonOptionalPlugin())
+    .registerOptionalPlugin("optional-plugin", Set.of("xoo"), new OptionalPlugin())
+    .addDefaultQProfile("xoo", "Sonar Way")
+    .addRules(new XooRulesDefinition())
+    .addActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo");
+
+  @Before
+  public void prepare() throws IOException {
+    logTester.setLevel(Level.DEBUG);
+  }
+
+  @Test
+  public void should_load_input_file_filters_for_required_and_optional_plugins() {
+    File projectDir = new File("test-resources/mediumtest/xoo/sample-with-input-file-filters");
+    AnalysisResult result = tester
+      .newAnalysis(new File(projectDir, "sonar-project.properties"))
+      .properties(Map.of(PLUGIN_LOADING_OPTIMIZATION_KEY, "true"))
+      .execute();
+
+    assertThat(result.inputFiles()).hasSize(1);
+
+    assertThat(logTester.logs()).contains("'xources/hello/xoo_exclude2.xoo' excluded by org.sonar.scanner.mediumtest.optionalplugins" +
+      ".OptionalPluginsMediumIT$NonOptionalPlugin$NonOptionalXooFileFilter");
+    assertThat(logTester.logs()).contains("'xources/hello/xoo_exclude3.xoo' excluded by org.sonar.scanner.mediumtest.optionalplugins" +
+      ".OptionalPluginsMediumIT$OptionalPlugin$OptionalXooFileFilter");
+    assertThat(logTester.logs()).contains("'xources/hello/xoo_exclude.xoo' excluded by org.sonar.xoo.extensions.XooExcludeFileFilter");
+    assertThat(logTester.logs()).contains("'xources/hello/HelloJava.xoo' indexed with language 'xoo'");
+
+    assertThat(result.inputFile("xources/hello/xoo_exclude.xoo")).isNull();
+    assertThat(result.inputFile("xources/hello/xoo_exclude2.xoo")).isNull();
+    assertThat(result.inputFile("xources/hello/xoo_exclude3.xoo")).isNull();
+    assertThat(result.inputFile("xources/hello/HelloJava.xoo")).isNotNull();
+  }
+
+  @Test
+  public void should_load_issue_filters_for_optional_plugins() throws Exception {
+    File projectDir = new File("test-resources/mediumtest/xoo/sample");
+    File tmpDir = temp.newFolder();
+    FileUtils.copyDirectory(projectDir, tmpDir);
+
+    AnalysisResult result = tester
+      .newAnalysis(new File(tmpDir, "sonar-project.properties"))
+      .properties(Map.of(PLUGIN_LOADING_OPTIMIZATION_KEY, "true"))
+      .execute();
+
+    List<ScannerReport.Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+    assertThat(issues).hasSize(8 - 1 /* line 2 excluded by  NonOptionalIssueFiler*/ - 1 /* line 3 excluded by  OptionalIssueFiler*/);
+  }
+
+  @Test
+  public void should_save_measures_for_optional_plugins() throws Exception {
+    File projectDir = new File("test-resources/mediumtest/xoo/sample");
+    File tmpDir = temp.newFolder();
+    FileUtils.copyDirectory(projectDir, tmpDir);
+
+    AnalysisResult result = tester
+      .newAnalysis(new File(tmpDir, "sonar-project.properties"))
+      .properties(Map.of(PLUGIN_LOADING_OPTIMIZATION_KEY, "true"))
+      .execute();
+
+    assertThat(result.allMeasures().get(result.project().key()))
+      .extracting(ScannerReport.Measure::getMetricKey, m -> m.getIntValue().getValue())
+      .containsExactlyInAnyOrder(
+        tuple(CoreMetrics.CLASSES_KEY, 1),
+        tuple(CoreMetrics.FUNCTIONS_KEY, 2));
+  }
+
+  public static class NonOptionalPlugin implements Plugin {
+
+    @Override
+    public void define(Context context) {
+      context.addExtensions(
+        NonOptionalXooFileFilter.class,
+        NonOptionalIssueFilter.class,
+        NonOptionalSensor.class);
+    }
+
+    public static class NonOptionalXooFileFilter implements InputFileFilter {
+
+      @Override
+      public boolean accept(InputFile f) {
+        return !f.filename().endsWith("_exclude2.xoo");
+      }
+    }
+
+    public static class NonOptionalIssueFilter implements IssueFilter {
+
+      @Override
+      public boolean accept(FilterableIssue filterableIssue, IssueFilterChain issueFilterChain) {
+        if (!issueFilterChain.accept(filterableIssue)) {
+          return false;
+        }
+        // Suppress issues on line 2
+        var line = filterableIssue.line();
+        return line == null || line != 2;
+      }
+    }
+
+    public static class NonOptionalSensor implements ProjectSensor {
+
+      private final MetricFinder metricFinder;
+      private final FileLinesContextFactory fileLinesContextFactory;
+
+      public NonOptionalSensor(MetricFinder metricFinder, FileLinesContextFactory fileLinesContextFactory) {
+        this.metricFinder = metricFinder;
+        // Simply verify that FileLinesContextFactory is correctly injected
+        this.fileLinesContextFactory = fileLinesContextFactory;
+      }
+
+      @Override
+      public void describe(SensorDescriptor descriptor) {
+        descriptor.name("NonOptionalSensor");
+      }
+
+      @Override
+      public void execute(SensorContext context) {
+        var metric = metricFinder.findByKey(CoreMetrics.CLASSES_KEY);
+        context.newMeasure().forMetric(metric).withValue(1).on(context.project()).save();
+      }
+    }
+  }
+
+  public static class OptionalPlugin implements Plugin {
+
+    @Override
+    public void define(Context context) {
+      context.addExtensions(
+        OptionalXooFileFilter.class,
+        OptionalIssueFilter.class,
+        OptionalSensor.class);
+    }
+
+    public static class OptionalXooFileFilter implements InputFileFilter {
+
+      @Override
+      public boolean accept(InputFile f) {
+        return !f.filename().endsWith("_exclude3.xoo");
+      }
+    }
+
+    public static class OptionalIssueFilter implements IssueFilter {
+
+      @Override
+      public boolean accept(FilterableIssue filterableIssue, IssueFilterChain issueFilterChain) {
+        if (!issueFilterChain.accept(filterableIssue)) {
+          return false;
+        }
+        // Suppress issues on line 3
+        var line = filterableIssue.line();
+        return line == null || line != 3;
+      }
+    }
+
+    public static class OptionalSensor implements ProjectSensor {
+
+      private final MetricFinder metricFinder;
+      private final FileLinesContextFactory fileLinesContextFactory;
+
+      public OptionalSensor(MetricFinder metricFinder, FileLinesContextFactory fileLinesContextFactory) {
+        this.metricFinder = metricFinder;
+        // Simply verify that FileLinesContextFactory is correctly injected
+        this.fileLinesContextFactory = fileLinesContextFactory;
+      }
+
+      @Override
+      public void describe(SensorDescriptor descriptor) {
+        descriptor.name("OptionalSensor");
+      }
+
+      @Override
+      public void execute(SensorContext context) {
+        var metric = metricFinder.findByKey(CoreMetrics.FUNCTIONS_KEY);
+        context.newMeasure().forMetric(metric).withValue(2).on(context.project()).save();
+      }
+    }
+  }
+
+
+}
index 3718dda86896869590dcb0404d8084e3c62e0694..fd69f0413f50f7daa1cc8ba58575a192925450c2 100644 (file)
@@ -85,7 +85,7 @@ public abstract class AbstractExtensionDictionary {
     }
   }
 
-  protected <T> Collection<T> sort(Collection<T> extensions) {
+  protected <T> List<T> sort(Collection<T> extensions) {
     DirectAcyclicGraph dag = new DirectAcyclicGraph();
 
     for (T extension : extensions) {
@@ -98,9 +98,9 @@ public abstract class AbstractExtensionDictionary {
       }
       completePhaseDependencies(dag, extension);
     }
-    List<?> sortedList = dag.sort();
+    List<T> sortedList = dag.sort();
 
-    return (Collection<T>) sortedList.stream()
+    return sortedList.stream()
       .filter(extensions::contains)
       .toList();
   }
index 81073fcfef2bed94f733cf034dd5f9de3543616a..f60a5ed7e1aed95fe70bed6e183da237170f550c 100644 (file)
@@ -30,15 +30,7 @@ import org.sonar.api.scan.issue.filter.IssueFilterChain;
 public class DefaultIssueFilterChain implements IssueFilterChain {
   private final List<IssueFilter> filters;
 
-  public DefaultIssueFilterChain(IssueFilter... filters) {
-    this.filters = List.of(filters);
-  }
-
-  public DefaultIssueFilterChain() {
-    this.filters = Collections.emptyList();
-  }
-
-  private DefaultIssueFilterChain(List<IssueFilter> filters) {
+  public DefaultIssueFilterChain(List<IssueFilter> filters) {
     this.filters = filters;
   }
 
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilterExtensionDictionary.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilterExtensionDictionary.java
new file mode 100644 (file)
index 0000000..df4550d
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.issue;
+
+import java.util.List;
+import org.sonar.api.scan.issue.filter.IssueFilter;
+import org.sonar.core.platform.SpringComponentContainer;
+import org.sonar.scanner.bootstrap.AbstractExtensionDictionary;
+
+public class IssueFilterExtensionDictionary extends AbstractExtensionDictionary {
+
+
+  public IssueFilterExtensionDictionary(SpringComponentContainer componentContainer) {
+    super(componentContainer);
+  }
+
+  public List<IssueFilter> selectIssueFilters() {
+    return sort(getFilteredExtensions(IssueFilter.class, null));
+  }
+}
index 0303bfe8fea0e7cd2ecb28f1d23b517b8ad7daf0..11b29853534333ed57d5823bf59e41c196f80800 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.scanner.issue;
 
+import java.util.List;
 import org.sonar.api.batch.fs.InputComponent;
 import org.sonar.api.batch.fs.internal.DefaultInputProject;
 import org.sonar.api.scan.issue.filter.FilterableIssue;
@@ -32,23 +33,24 @@ import org.springframework.beans.factory.annotation.Autowired;
  */
 @Deprecated
 public class IssueFilters {
-  private final IssueFilterChain filterChain;
+  private IssueFilterChain filterChain;
   private final DefaultInputProject project;
 
-  @Autowired(required = false)
-  public IssueFilters(DefaultInputProject project, IssueFilter[] exclusionFilters) {
-    this.project = project;
-    this.filterChain = new DefaultIssueFilterChain(exclusionFilters);
-  }
-
   @Autowired(required = false)
   public IssueFilters(DefaultInputProject project) {
-    this(project, new IssueFilter[0]);
+    this.project = project;
   }
 
   public boolean accept(InputComponent component, ScannerReport.Issue rawIssue) {
+    if (filterChain == null) {
+      throw new IllegalStateException("Issue filters must be registered before this class can be used");
+    }
     FilterableIssue fIssue = new DefaultFilterableIssue(project, rawIssue, component);
     return filterChain.accept(fIssue);
   }
 
+  public void registerFilters(List<IssueFilter> exclusionFilters) {
+    this.filterChain = new DefaultIssueFilterChain(exclusionFilters);
+  }
+
 }
index 190c9f648594d554393482115c94c247d82b6cee..7f060c295fd5ce10c0a24a75b06473c3ea1032e7 100644 (file)
@@ -44,6 +44,8 @@ import org.sonar.scanner.bootstrap.PostJobExtensionDictionary;
 import org.sonar.scanner.bootstrap.ScannerPluginRepository;
 import org.sonar.scanner.cpd.CpdExecutor;
 import org.sonar.scanner.fs.InputModuleHierarchy;
+import org.sonar.scanner.issue.IssueFilterExtensionDictionary;
+import org.sonar.scanner.issue.IssueFilters;
 import org.sonar.scanner.mediumtest.AnalysisObservers;
 import org.sonar.scanner.postjob.DefaultPostJobContext;
 import org.sonar.scanner.postjob.PostJobOptimizer;
@@ -124,6 +126,9 @@ public class SpringProjectScanContainer extends SpringComponentContainer {
       ProjectSensorsExecutor.class,
       ProjectSensorOptimizer.class,
 
+      // Issue filters
+      IssueFilterExtensionDictionary.class,
+
       AnalysisObservers.class,
 
       // file system
@@ -144,6 +149,8 @@ public class SpringProjectScanContainer extends SpringComponentContainer {
   @Override
   protected void doAfterStart() {
     getParentComponentByType(ScannerMetrics.class).addPluginMetrics(getComponentsByType(Metrics.class));
+    getParentComponentByType(IssueFilters.class).registerFilters(getComponentByType(IssueFilterExtensionDictionary.class).selectIssueFilters());
+
     getComponentByType(ProjectLock.class).tryLock();
 
     // NOTE: ProjectBuilders executed here will have any changes they make to the ProjectReactor discarded.
index 59cc6d695caea3199eb5cf0dbda9492cef165a91..3957b01c45cf0763af386359c088d06bacf18146 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.scanner.issue;
 
+import java.util.List;
 import org.junit.Test;
 import org.sonar.api.scan.issue.filter.FilterableIssue;
 import org.sonar.api.scan.issue.filter.IssueFilter;
@@ -33,7 +34,7 @@ public class DefaultIssueFilterChainTest {
 
   @Test
   public void should_accept_when_no_filter() {
-    assertThat(new DefaultIssueFilterChain().accept(issue)).isTrue();
+    assertThat(new DefaultIssueFilterChain(List.of()).accept(issue)).isTrue();
   }
 
   static class PassingFilter implements IssueFilter {
@@ -68,28 +69,28 @@ public class DefaultIssueFilterChainTest {
 
   @Test
   public void should_accept_if_all_filters_pass() {
-    assertThat(new DefaultIssueFilterChain(
+    assertThat(new DefaultIssueFilterChain(List.of(
       new PassingFilter(),
       new PassingFilter(),
-      new PassingFilter()
+      new PassingFilter())
       ).accept(issue)).isTrue();
   }
 
   @Test
   public void should_accept_and_not_go_further_if_filter_accepts() {
-    assertThat(new DefaultIssueFilterChain(
+    assertThat(new DefaultIssueFilterChain(List.of(
       new PassingFilter(),
       new AcceptingFilter(),
-      new FailingFilter()
+      new FailingFilter())
       ).accept(issue)).isTrue();
   }
 
   @Test
   public void should_refuse_and_not_go_further_if_filter_refuses() {
-    assertThat(new DefaultIssueFilterChain(
+    assertThat(new DefaultIssueFilterChain(List.of(
       new PassingFilter(),
       new RefusingFilter(),
-      new FailingFilter()
+      new FailingFilter())
       ).accept(issue)).isFalse();
   }
 }
diff --git a/sonar-scanner-engine/test-resources/mediumtest/xoo/sample-with-input-file-filters/xources/hello/xoo_exclude3.xoo b/sonar-scanner-engine/test-resources/mediumtest/xoo/sample-with-input-file-filters/xources/hello/xoo_exclude3.xoo
new file mode 100644 (file)
index 0000000..d04e465
--- /dev/null
@@ -0,0 +1 @@
+this file should ALSO be excluded from indexing.