]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11465 Introduce a new annotation for project level extensions
authorJulien HENRY <julien.henry@sonarsource.com>
Fri, 9 Nov 2018 10:19:38 +0000 (11:19 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 16 Jan 2019 08:42:59 +0000 (09:42 +0100)
and deprecate issue filters

18 files changed:
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/extensions/XooIssueFilter.java [new file with mode: 0644]
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/ScannerSide.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/package-info.java
sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/FilterableIssue.java
sonar-plugin-api/src/main/java/org/sonar/api/scan/issue/filter/IssueFilter.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ExtensionUtils.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssueFilters.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/task/TaskContainer.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ExtensionUtilsTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ModuleIssuesTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java

index 5445c753ea2c2c8121e67a08e997d6c7575b15f5..ba3c4ef052889132872559d4cd42fa9af3a8d62c 100644 (file)
@@ -28,6 +28,7 @@ import org.sonar.api.utils.Version;
 import org.sonar.xoo.coverage.ItCoverageSensor;
 import org.sonar.xoo.coverage.OverallCoverageSensor;
 import org.sonar.xoo.coverage.UtCoverageSensor;
+import org.sonar.xoo.extensions.XooIssueFilter;
 import org.sonar.xoo.extensions.XooPostJob;
 import org.sonar.xoo.extensions.XooProjectBuilder;
 import org.sonar.xoo.global.GlobalSensor;
@@ -154,7 +155,8 @@ public class XooPlugin implements Plugin {
 
       // Other
       XooProjectBuilder.class,
-      XooPostJob.class);
+      XooPostJob.class,
+      XooIssueFilter.class);
 
     if (context.getRuntime().getProduct() != SonarProduct.SONARLINT) {
       context.addExtension(MeasureSensor.class);
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/extensions/XooIssueFilter.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/extensions/XooIssueFilter.java
new file mode 100644 (file)
index 0000000..57f923f
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.xoo.extensions;
+
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.scan.issue.filter.FilterableIssue;
+import org.sonar.api.scan.issue.filter.IssueFilter;
+import org.sonar.api.scan.issue.filter.IssueFilterChain;
+
+public class XooIssueFilter implements IssueFilter {
+
+  private final Configuration config;
+
+  public XooIssueFilter(Configuration config) {
+    this.config = config;
+  }
+
+
+  @Override
+  public boolean accept(FilterableIssue issue, IssueFilterChain chain) {
+    if (config.getBoolean("sonar.xoo.excludeAllIssuesOnOddLines").orElse(false) && isOdd(issue)) {
+      return false;
+    }
+    return chain.accept(issue);
+  }
+
+  private static boolean isOdd(FilterableIssue issue) {
+    TextRange textRange = issue.textRange();
+    return textRange != null && textRange.start().line() % 2 == 1;
+  }
+}
index 2fcf3a38458da5c085a17355467b935e1e399f53..3e4c6aa5a8272b5ffaed5f6b84acca43c9111097 100644 (file)
@@ -40,7 +40,7 @@ public class XooPluginTest {
     Plugin.Context context = new PluginContextImpl.Builder().setSonarRuntime(runtime).build();
     new XooPlugin().define(context);
     assertThat(getExtensions(context))
-      .hasSize(46)
+      .hasSize(47)
       .doesNotContain(XooBuiltInQualityProfilesDefinition.class);
   }
 
@@ -50,7 +50,7 @@ public class XooPluginTest {
     Plugin.Context context = new PluginContextImpl.Builder().setSonarRuntime(runtime).build();
     new XooPlugin().define(context);
     assertThat(getExtensions(context))
-      .hasSize(49)
+      .hasSize(50)
       .contains(XooBuiltInQualityProfilesDefinition.class);
   }
 
@@ -60,7 +60,7 @@ public class XooPluginTest {
     Plugin.Context context = new PluginContextImpl.Builder().setSonarRuntime(runtime).build();
     new XooPlugin().define(context);
     assertThat(getExtensions(context))
-      .hasSize(53)
+      .hasSize(54)
       .contains(OneExternalIssuePerLineSensor.class);
   }
 
@@ -70,7 +70,7 @@ public class XooPluginTest {
     Plugin.Context context = new PluginContextImpl.Builder().setSonarRuntime(runtime).build();
     new XooPlugin().define(context);
     assertThat(getExtensions(context))
-      .hasSize(54)
+      .hasSize(55)
       .contains(OneExternalIssuePerLineSensor.class);
   }
 
index 9158e4f4fb567008ba70818d5e535d4d157f792d..e69ce941a98e08bc12917d76a0d7edc3d36d5628 100644 (file)
@@ -44,7 +44,9 @@ import java.lang.annotation.Target;
  * </pre>
  *
  * @since 6.0
+ * @deprecated since 7.6 use {@link org.sonar.api.scanner.ScannerSide} that will move the component to the project container
  */
+@Deprecated
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
index f9e75219d2424eb35a4573eed9b722dc900928f2..28d70972fa3bc917071a4fa5fea2dc55dcd7bc6a 100644 (file)
@@ -18,4 +18,4 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 @javax.annotation.ParametersAreNonnullByDefault
-package org.sonar.api.batch.sensor;
+package org.sonar.api.batch;
index 9c9e337589858c2bd6ea15d895826f1f6555189e..09f7a2f37e1b988ef865e072df0f59683203cd9f 100644 (file)
@@ -27,8 +27,10 @@ import org.sonar.api.rule.RuleKey;
 
 /**
  * @since 5.3
+ * @deprecated since 7.6
  */
 @ThreadSafe
+@Deprecated
 public interface FilterableIssue {
 
   /**
index eea90b73454f3af1ca8d87bafe7378cc7ab64d71..0db66a578d3f71daf6c2445e813548e2e0d1e6e6 100644 (file)
@@ -32,7 +32,9 @@ import org.sonarsource.api.sonarlint.SonarLintSide;
 @ThreadSafe
 /**
  * @since 5.3
+ * @deprecated since 7.6
  */
+@Deprecated
 public interface IssueFilter {
 
   /**
index e80b14b64a459d15a66968a78f66c511ff1f3227..3cd2928bb724ebf43aabddb05d2399b8e4cd1358 100644 (file)
@@ -24,7 +24,6 @@ import java.util.Date;
 import java.util.Optional;
 import org.picocontainer.Startable;
 import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.ScannerSide;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.utils.DateUtils;
 
@@ -33,7 +32,6 @@ import org.sonar.api.utils.DateUtils;
  * 
  * Immutable after {@link #start()}
  */
-@ScannerSide
 public class ProjectAnalysisInfo implements Startable {
   private final Clock clock;
   private Configuration settings;
index c82261e21bdc3108670240f181ff8e95eec91e1a..b80e2bfcf3e0cf689ea3456e897b8225207f182a 100644 (file)
@@ -20,7 +20,7 @@
 package org.sonar.scanner.bootstrap;
 
 import org.sonar.api.batch.InstantiationStrategy;
-import org.sonar.api.batch.ScannerSide;
+import org.sonar.api.scanner.ScannerSide;
 import org.sonar.api.utils.AnnotationUtils;
 
 public class ExtensionUtils {
@@ -37,6 +37,10 @@ public class ExtensionUtils {
     return InstantiationStrategy.PER_PROJECT.equals(strategy);
   }
   
+  public static boolean isDeprecatedScannerSide(Object extension) {
+    return AnnotationUtils.getAnnotation(extension, org.sonar.api.batch.ScannerSide.class) != null;
+  }
+
   public static boolean isScannerSide(Object extension) {
     return AnnotationUtils.getAnnotation(extension, ScannerSide.class) != null;
   }
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java
deleted file mode 100644 (file)
index 65776c8..0000000
+++ /dev/null
@@ -1,51 +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.issue;
-
-import org.sonar.api.batch.ScannerSide;
-import org.sonar.api.batch.fs.internal.DefaultInputModule;
-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.scanner.ProjectAnalysisInfo;
-import org.sonar.scanner.protocol.output.ScannerReport;
-
-@ScannerSide
-public class IssueFilters {
-  private final IssueFilterChain filterChain;
-  private final DefaultInputModule module;
-  private final ProjectAnalysisInfo projectAnalysisInfo;
-
-  public IssueFilters(DefaultInputModule module, ProjectAnalysisInfo projectAnalysisInfo, IssueFilter[] exclusionFilters) {
-    this.module = module;
-    this.filterChain = new DefaultIssueFilterChain(exclusionFilters);
-    this.projectAnalysisInfo = projectAnalysisInfo;
-  }
-
-  public IssueFilters(DefaultInputModule module, ProjectAnalysisInfo projectAnalysisInfo) {
-    this(module, projectAnalysisInfo, new IssueFilter[0]);
-  }
-
-  public boolean accept(String componentKey, ScannerReport.Issue rawIssue) {
-    FilterableIssue fIssue = new DefaultFilterableIssue(module, projectAnalysisInfo, rawIssue, componentKey);
-    return filterChain.accept(fIssue);
-  }
-
-}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssueFilters.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssueFilters.java
new file mode 100644 (file)
index 0000000..1d9885f
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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.issue;
+
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+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.scanner.ProjectAnalysisInfo;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+/**
+ * @deprecated since 7.6, {@link IssueFilter} is deprecated
+ */
+@Deprecated
+public class ModuleIssueFilters {
+  private final IssueFilterChain filterChain;
+  private final DefaultInputModule module;
+  private final ProjectAnalysisInfo projectAnalysisInfo;
+
+  public ModuleIssueFilters(DefaultInputModule module, ProjectAnalysisInfo projectAnalysisInfo, IssueFilter[] exclusionFilters) {
+    this.module = module;
+    this.filterChain = new DefaultIssueFilterChain(exclusionFilters);
+    this.projectAnalysisInfo = projectAnalysisInfo;
+  }
+
+  public ModuleIssueFilters(DefaultInputModule module, ProjectAnalysisInfo projectAnalysisInfo) {
+    this(module, projectAnalysisInfo, new IssueFilter[0]);
+  }
+
+  public boolean accept(String componentKey, ScannerReport.Issue rawIssue) {
+    FilterableIssue fIssue = new DefaultFilterableIssue(module, projectAnalysisInfo, rawIssue, componentKey);
+    return filterChain.accept(fIssue);
+  }
+
+}
index 8ba909af250804e266e6c520c9606930550b79ef..edf5417d4f6f0a910df28f062a959313e2b69a95 100644 (file)
@@ -44,10 +44,10 @@ import static com.google.common.base.Strings.nullToEmpty;
 public class ModuleIssues {
 
   private final ActiveRules activeRules;
-  private final IssueFilters filters;
+  private final ModuleIssueFilters filters;
   private final ReportPublisher reportPublisher;
 
-  public ModuleIssues(ActiveRules activeRules, IssueFilters filters, ReportPublisher reportPublisher) {
+  public ModuleIssues(ActiveRules activeRules, ModuleIssueFilters filters, ReportPublisher reportPublisher) {
     this.activeRules = activeRules;
     this.filters = filters;
     this.reportPublisher = reportPublisher;
index 78e1a6f391c3c8a39857d39489a736d6602a8d14..033ec860832de5e62bbffeb64dde267e36e390d1 100644 (file)
@@ -33,7 +33,7 @@ import org.sonar.scanner.bootstrap.ExtensionInstaller;
 import org.sonar.scanner.bootstrap.GlobalAnalysisMode;
 import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary;
 import org.sonar.scanner.deprecated.perspectives.ScannerPerspectives;
-import org.sonar.scanner.issue.IssueFilters;
+import org.sonar.scanner.issue.ModuleIssueFilters;
 import org.sonar.scanner.issue.ModuleIssues;
 import org.sonar.scanner.issue.ignore.EnforceIssuesFilter;
 import org.sonar.scanner.issue.ignore.IgnoreIssuesFilter;
@@ -66,8 +66,8 @@ import org.sonar.scanner.sensor.SensorOptimizer;
 
 import static org.sonar.api.batch.InstantiationStrategy.PER_PROJECT;
 import static org.sonar.core.extension.CoreExtensionsInstaller.noExtensionFilter;
+import static org.sonar.scanner.bootstrap.ExtensionUtils.isDeprecatedScannerSide;
 import static org.sonar.scanner.bootstrap.ExtensionUtils.isInstantiationStrategy;
-import static org.sonar.scanner.bootstrap.ExtensionUtils.isScannerSide;
 
 public class ModuleScanContainer extends ComponentContainer {
   private static final Logger LOG = LoggerFactory.getLogger(ModuleScanContainer.class);
@@ -127,7 +127,7 @@ public class ModuleScanContainer extends ComponentContainer {
       DefaultSensorStorage.class,
       DefaultSensorContext.class,
       ScannerExtensionDictionnary.class,
-      IssueFilters.class,
+      ModuleIssueFilters.class,
       CoverageExclusions.class,
 
       // rules
@@ -156,7 +156,7 @@ public class ModuleScanContainer extends ComponentContainer {
     CoreExtensionsInstaller coreExtensionsInstaller = getComponentByType(CoreExtensionsInstaller.class);
     coreExtensionsInstaller.install(this, noExtensionFilter(), t -> isInstantiationStrategy(t, PER_PROJECT));
     ExtensionInstaller pluginInstaller = getComponentByType(ExtensionInstaller.class);
-    pluginInstaller.install(this, e -> isScannerSide(e) && isInstantiationStrategy(e, PER_PROJECT));
+    pluginInstaller.install(this, e -> isDeprecatedScannerSide(e) && isInstantiationStrategy(e, PER_PROJECT));
   }
 
   @Override
index d15a08dcb6ba5ec69a59666f13b5ba573f9f1f40..a5c38184cba03f030715e14c7a843065080301e6 100644 (file)
@@ -100,6 +100,7 @@ import org.sonar.scanner.storage.Storages;
 
 import static org.sonar.api.batch.InstantiationStrategy.PER_BATCH;
 import static org.sonar.core.extension.CoreExtensionsInstaller.noExtensionFilter;
+import static org.sonar.scanner.bootstrap.ExtensionUtils.isDeprecatedScannerSide;
 import static org.sonar.scanner.bootstrap.ExtensionUtils.isInstantiationStrategy;
 import static org.sonar.scanner.bootstrap.ExtensionUtils.isScannerSide;
 
@@ -256,7 +257,7 @@ public class ProjectScanContainer extends ComponentContainer {
 
   @VisibleForTesting
   static ExtensionMatcher getBatchPluginExtensionsFilter() {
-    return extension -> isScannerSide(extension) && isInstantiationStrategy(extension, PER_BATCH);
+    return extension -> isScannerSide(extension) || (isDeprecatedScannerSide(extension) && isInstantiationStrategy(extension, PER_BATCH));
   }
 
   @Override
index 0d96841a4752df27076a0e4f27d5525a4010d677..a1f057b3c88fb04b3a9fe636489505aeb5ae7790 100644 (file)
@@ -33,7 +33,7 @@ import org.sonar.scanner.bootstrap.GlobalProperties;
 import static org.sonar.api.batch.InstantiationStrategy.PER_TASK;
 import static org.sonar.core.extension.CoreExtensionsInstaller.noExtensionFilter;
 import static org.sonar.scanner.bootstrap.ExtensionUtils.isInstantiationStrategy;
-import static org.sonar.scanner.bootstrap.ExtensionUtils.isScannerSide;
+import static org.sonar.scanner.bootstrap.ExtensionUtils.isDeprecatedScannerSide;
 
 public class TaskContainer extends ComponentContainer {
 
@@ -63,7 +63,7 @@ public class TaskContainer extends ComponentContainer {
     getComponentByType(CoreExtensionsInstaller.class)
       .install(this, noExtensionFilter(), t -> isInstantiationStrategy(t, PER_TASK));
     getComponentByType(ExtensionInstaller.class)
-      .install(this, extension -> isScannerSide(extension) && isInstantiationStrategy(extension, PER_TASK));
+      .install(this, extension -> isDeprecatedScannerSide(extension) && isInstantiationStrategy(extension, PER_TASK));
   }
 
   @Override
index 0baea1f9f9442ab08a69c5bcff8fff4a0cc91d05..f67ecb0d4c3cd72790fee3db29b2c37df10e0abb 100644 (file)
@@ -53,12 +53,12 @@ public class ExtensionUtilsTest {
 
   @Test
   public void testIsScannerSide() {
-    assertThat(ExtensionUtils.isScannerSide(ScannerService.class)).isTrue();
+    assertThat(ExtensionUtils.isDeprecatedScannerSide(ScannerService.class)).isTrue();
 
-    assertThat(ExtensionUtils.isScannerSide(ServerService.class)).isFalse();
-    assertThat(ExtensionUtils.isScannerSide(new ServerService())).isFalse();
-    assertThat(ExtensionUtils.isScannerSide(new WebServerService())).isFalse();
-    assertThat(ExtensionUtils.isScannerSide(new ComputeEngineService())).isFalse();
+    assertThat(ExtensionUtils.isDeprecatedScannerSide(ServerService.class)).isFalse();
+    assertThat(ExtensionUtils.isDeprecatedScannerSide(new ServerService())).isFalse();
+    assertThat(ExtensionUtils.isDeprecatedScannerSide(new WebServerService())).isFalse();
+    assertThat(ExtensionUtils.isDeprecatedScannerSide(new ComputeEngineService())).isFalse();
   }
 
   @ScannerSide
index d1b1e5a1cb89c5342f60eed9d9b75b26c00e02bb..ed972c1dbc94608ee1970bacd17b0a28329473b6 100644 (file)
@@ -35,12 +35,10 @@ import org.sonar.api.batch.sensor.issue.internal.DefaultIssueLocation;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.RuleType;
-import org.sonar.api.utils.MessageException;
 import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.scanner.report.ReportPublisher;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -57,7 +55,7 @@ public class ModuleIssuesTest {
   static final String SQUID_RULE_NAME = "Avoid Cycle";
 
   @Mock
-  IssueFilters filters;
+  ModuleIssueFilters filters;
 
   ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder();
   RulesBuilder ruleBuilder = new RulesBuilder();
index a4fdc9caa9e9b33662a425bf18014d8f5ade404f..8115c9e8ed99b595d41c9845a41006b6c707aef5 100644 (file)
@@ -65,9 +65,6 @@ public class IssuesMediumTest {
     
     List<ExternalIssue> externalIssues = result.externalIssuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
     assertThat(externalIssues).isEmpty();
-
-    Issue issue = issues.get(0);
-    assertThat(issue.getTextRange().getStartLine()).isEqualTo(issue.getTextRange().getStartLine());
   }
   
   @Test
@@ -83,9 +80,6 @@ public class IssuesMediumTest {
 
     List<ExternalIssue> externalIssues = result.externalIssuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
     assertThat(externalIssues).hasSize(8 /* lines */);
-
-    ExternalIssue externalIssue = externalIssues.get(0);
-    assertThat(externalIssue.getTextRange().getStartLine()).isEqualTo(externalIssue.getTextRange().getStartLine());
   }
 
   @Test
@@ -163,4 +157,19 @@ public class IssuesMediumTest {
       .contains(tuple("This issue is generated on each line", 1, 0.0));
   }
 
+  @Test
+  public void testIssueFilter() throws Exception {
+    File projectDir = new File(IssuesMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+    File tmpDir = temp.newFolder();
+    FileUtils.copyDirectory(projectDir, tmpDir);
+
+    TaskResult result = tester
+      .newScanTask(new File(tmpDir, "sonar-project.properties"))
+      .property("sonar.xoo.excludeAllIssuesOnOddLines", "true")
+      .execute();
+
+    List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/HelloJava.xoo"));
+    assertThat(issues).hasSize(4 /* even lines */);
+  }
+
 }