import org.sonar.server.computation.issue.commonrule.LineCoverageRule;
import org.sonar.server.computation.issue.commonrule.SkippedTestRule;
import org.sonar.server.computation.issue.commonrule.TestErrorRule;
+import org.sonar.server.computation.issue.filter.IssueFilter;
import org.sonar.server.computation.language.LanguageRepositoryImpl;
import org.sonar.server.computation.measure.MeasureComputersHolderImpl;
import org.sonar.server.computation.measure.MeasureComputersVisitor;
IssueLifecycle.class,
ComponentsWithUnprocessedIssues.class,
ComponentIssuesRepositoryImpl.class,
+ IssueFilter.class,
// common rules
CommonRuleEngineImpl.class,
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.TreeRootHolder;
import org.sonar.server.computation.issue.commonrule.CommonRuleEngine;
+import org.sonar.server.computation.issue.filter.IssueFilter;
import org.sonar.server.computation.source.SourceLinesRepository;
import org.sonar.server.rule.CommonRuleKeys;
private final BatchReportReader reportReader;
private final SourceLinesRepository sourceLinesRepository;
private final CommonRuleEngine commonRuleEngine;
+ private final IssueFilter issueFilter;
public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader,
- SourceLinesRepository sourceLinesRepository, CommonRuleEngine commonRuleEngine) {
+ SourceLinesRepository sourceLinesRepository, CommonRuleEngine commonRuleEngine, IssueFilter issueFilter) {
this.treeRootHolder = treeRootHolder;
this.reportReader = reportReader;
this.sourceLinesRepository = sourceLinesRepository;
this.commonRuleEngine = commonRuleEngine;
+ this.issueFilter = issueFilter;
}
public Input<DefaultIssue> create(Component component) {
List<DefaultIssue> result = new ArrayList<>();
for (DefaultIssue commonRuleIssue : commonRuleEngine.process(component)) {
- result.add(init(commonRuleIssue));
+ if (issueFilter.accept(commonRuleIssue, component)) {
+ result.add(init(commonRuleIssue));
+ }
}
try (CloseableIterator<ScannerReport.Issue> reportIssues = reportReader.readComponentIssues(component.getReportAttributes().getRef())) {
// optimization - do not load line hashes if there are no issues -> getLineHashSequence() is executed
ScannerReport.Issue reportIssue = reportIssues.next();
if (isIssueOnUnsupportedCommonRule(reportIssue)) {
DefaultIssue issue = toIssue(getLineHashSequence(), reportIssue);
- result.add(issue);
+ if (issueFilter.accept(issue, component)) {
+ result.add(issue);
+ }
} else {
Loggers.get(getClass()).debug("Ignored issue from analysis report on rule {}:{}", reportIssue.getRuleRepository(), reportIssue.getRuleKey());
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.server.computation.issue.filter;
+
+import com.google.common.base.Splitter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.SettingsRepository;
+import org.sonar.server.computation.component.TreeRootHolder;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.lang.String.format;
+import static org.apache.commons.lang.StringUtils.defaultIfBlank;
+import static org.sonar.core.config.IssueExclusionProperties.PATTERNS_MULTICRITERIA_EXCLUSION_KEY;
+import static org.sonar.core.config.IssueExclusionProperties.PATTERNS_MULTICRITERIA_INCLUSION_KEY;
+import static org.sonar.core.config.IssueExclusionProperties.RESOURCE_KEY;
+import static org.sonar.core.config.IssueExclusionProperties.RULE_KEY;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+
+@ComputeEngineSide
+public class IssueFilter {
+
+ private static final Logger LOG = Loggers.get(IssueFilter.class);
+
+ private final List<IssuePattern> exclusionPatterns;
+ private final List<IssuePattern> inclusionPatterns;
+
+ public IssueFilter(TreeRootHolder treeRootHolder, SettingsRepository settingsRepository) {
+ Settings settings = settingsRepository.getSettings(treeRootHolder.getRoot());
+ this.exclusionPatterns = loadPatterns(PATTERNS_MULTICRITERIA_EXCLUSION_KEY, settings);
+ this.inclusionPatterns = loadPatterns(PATTERNS_MULTICRITERIA_INCLUSION_KEY, settings);
+ }
+
+ public boolean accept(DefaultIssue issue, Component component) {
+ if (component.getType() != FILE || (exclusionPatterns.isEmpty() && inclusionPatterns.isEmpty())) {
+ return true;
+ }
+ if (isExclude(issue, component)) {
+ return false;
+ }
+ return isInclude(issue, component);
+ }
+
+ private boolean isExclude(DefaultIssue issue, Component component) {
+ IssuePattern matchingPattern = null;
+ Iterator<IssuePattern> patternIterator = exclusionPatterns.iterator();
+ while (matchingPattern == null && patternIterator.hasNext()) {
+ IssuePattern nextPattern = patternIterator.next();
+ if (nextPattern.match(issue, component)) {
+ matchingPattern = nextPattern;
+ }
+ }
+ if (matchingPattern != null) {
+ LOG.debug("Issue {} ignored by exclusion pattern {}", issue, matchingPattern);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isInclude(DefaultIssue issue, Component component) {
+ boolean atLeastOneRuleMatched = false;
+ boolean atLeastOnePatternFullyMatched = false;
+ IssuePattern matchingPattern = null;
+
+ for (IssuePattern pattern : inclusionPatterns) {
+ if (pattern.getRulePattern().match(issue.ruleKey().toString())) {
+ atLeastOneRuleMatched = true;
+ String componentPath = component.getReportAttributes().getPath();
+ if (componentPath != null && pattern.getComponentPattern().match(componentPath)) {
+ atLeastOnePatternFullyMatched = true;
+ matchingPattern = pattern;
+ }
+ }
+ }
+
+ if (atLeastOneRuleMatched) {
+ if (atLeastOnePatternFullyMatched) {
+ LOG.debug("Issue {} enforced by pattern {}", issue, matchingPattern);
+ }
+ return atLeastOnePatternFullyMatched;
+ } else {
+ return true;
+ }
+ }
+
+ private static List<IssuePattern> loadPatterns(String propertyKey, Settings settings) {
+ List<IssuePattern> patterns = new ArrayList<>();
+ String patternConf = defaultIfBlank(settings.getString(propertyKey), "");
+ for (String id : Splitter.on(",").omitEmptyStrings().split(patternConf)) {
+ String propPrefix = propertyKey + "." + id + ".";
+ String componentPathPattern = settings.getString(propPrefix + RESOURCE_KEY);
+ checkArgument(!isNullOrEmpty(componentPathPattern), format("File path pattern cannot be empty. Please check '%s' settings", propertyKey));
+ String ruleKeyPattern = settings.getString(propPrefix + RULE_KEY);
+ checkArgument(!isNullOrEmpty(ruleKeyPattern), format("Rule key pattern cannot be empty. Please check '%s' settings", propertyKey));
+ patterns.add(new IssuePattern(componentPathPattern, ruleKeyPattern));
+ }
+ return patterns;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.server.computation.issue.filter;
+
+import javax.annotation.Nullable;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.WildcardPattern;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.component.Component;
+
+public class IssuePattern {
+
+ private WildcardPattern componentPattern;
+ private WildcardPattern rulePattern;
+
+ public IssuePattern(String componentPattern, String rulePattern) {
+ this.componentPattern = WildcardPattern.create(componentPattern);
+ this.rulePattern = WildcardPattern.create(rulePattern);
+ }
+
+ public WildcardPattern getComponentPattern() {
+ return componentPattern;
+ }
+
+ public WildcardPattern getRulePattern() {
+ return rulePattern;
+ }
+
+ boolean match(DefaultIssue issue, Component component) {
+ return matchComponent(component.getReportAttributes().getPath()) && matchRule(issue.ruleKey());
+ }
+
+ boolean matchRule(RuleKey rule) {
+ return rulePattern.match(rule.toString());
+ }
+
+ boolean matchComponent(@Nullable String path) {
+ return path != null && componentPattern.match(path);
+ }
+
+ @Override
+ public String toString() {
+ return "IssuePattern{" +
+ "componentPattern=" + componentPattern +
+ ", rulePattern=" + rulePattern +
+ '}';
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.computation.issue.filter;
+
+import javax.annotation.ParametersAreNonnullByDefault;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.TypeAwareVisitor;
import org.sonar.server.computation.issue.commonrule.CommonRuleEngineImpl;
+import org.sonar.server.computation.issue.filter.IssueFilter;
import org.sonar.server.computation.qualityprofile.ActiveRulesHolderRule;
import org.sonar.server.computation.source.SourceLinesRepositoryRule;
import org.sonar.server.issue.IssueTesting;
import static com.google.common.collect.Sets.newHashSet;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static org.sonar.server.computation.component.ReportComponent.builder;
public class IntegrateIssuesVisitorTest {
ArgumentCaptor<DefaultIssue> defaultIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class);
+ IssueFilter issueFilter = mock(IssueFilter.class);
+
BaseIssuesLoader baseIssuesLoader = new BaseIssuesLoader(treeRootHolder, dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule);
TrackerExecution tracker = new TrackerExecution(new TrackerBaseInputFactory(baseIssuesLoader, dbTester.getDbClient()), new TrackerRawInputFactory(treeRootHolder, reportReader,
- fileSourceRepository, new CommonRuleEngineImpl()), new Tracker<DefaultIssue, DefaultIssue>());
+ fileSourceRepository, new CommonRuleEngineImpl(), issueFilter), new Tracker<DefaultIssue, DefaultIssue>());
IssueCache issueCache;
IssueLifecycle issueLifecycle = mock(IssueLifecycle.class);
public void setUp() throws Exception {
treeRootHolder.setRoot(PROJECT);
issueCache = new IssueCache(temp.newFile(), System2.INSTANCE);
+ when(issueFilter.accept(any(DefaultIssue.class), eq(FILE))).thenReturn(true);
underTest = new IntegrateIssuesVisitor(tracker, issueCache, issueLifecycle, issueVisitors, componentsWithUnprocessedIssues, componentIssuesRepository);
}
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.ReportComponent;
import org.sonar.server.computation.issue.commonrule.CommonRuleEngine;
+import org.sonar.server.computation.issue.filter.IssueFilter;
import org.sonar.server.computation.source.SourceLinesRepositoryRule;
import org.sonar.server.rule.CommonRuleKeys;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
CommonRuleEngine commonRuleEngine = mock(CommonRuleEngine.class);
- TrackerRawInputFactory underTest = new TrackerRawInputFactory(treeRootHolder, reportReader, fileSourceRepository, commonRuleEngine);
+ IssueFilter issueFilter = mock(IssueFilter.class);
+
+ TrackerRawInputFactory underTest = new TrackerRawInputFactory(treeRootHolder, reportReader, fileSourceRepository, commonRuleEngine, issueFilter);
@Test
public void load_source_hash_sequences() throws Exception {
}
@Test
- public void load_issues() throws Exception {
+ public void load_issues_from_report() throws Exception {
+ when(issueFilter.accept(any(DefaultIssue.class), eq(FILE))).thenReturn(true);
fileSourceRepository.addLines(FILE_REF, "line 1;", "line 2;");
ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
.setTextRange(TextRange.newBuilder().setStartLine(2).build())
assertInitializedIssue(issue);
}
+ @Test
+ public void ignore_issue_from_report() throws Exception {
+ when(issueFilter.accept(any(DefaultIssue.class), eq(FILE))).thenReturn(false);
+ fileSourceRepository.addLines(FILE_REF, "line 1;", "line 2;");
+ ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
+ .setTextRange(TextRange.newBuilder().setStartLine(2).build())
+ .setMsg("the message")
+ .setRuleRepository("java")
+ .setRuleKey("S001")
+ .setSeverity(Constants.Severity.BLOCKER)
+ .setGap(3.14)
+ .build();
+ reportReader.putIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
+ Input<DefaultIssue> input = underTest.create(FILE);
+
+ Collection<DefaultIssue> issues = input.getIssues();
+ assertThat(issues).isEmpty();
+ }
+
@Test
public void ignore_report_issues_on_common_rules() throws Exception {
fileSourceRepository.addLines(FILE_REF, "line 1;", "line 2;");
@Test
public void load_issues_of_compute_engine_common_rules() throws Exception {
+ when(issueFilter.accept(any(DefaultIssue.class), eq(FILE))).thenReturn(true);
fileSourceRepository.addLines(FILE_REF, "line 1;", "line 2;");
DefaultIssue ceIssue = new DefaultIssue()
.setRuleKey(RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), "InsufficientCoverage"))
assertInitializedIssue(input.getIssues().iterator().next());
}
+ @Test
+ public void ignore_issue_from_common_rule() throws Exception {
+ when(issueFilter.accept(any(DefaultIssue.class), eq(FILE))).thenReturn(false);
+ fileSourceRepository.addLines(FILE_REF, "line 1;", "line 2;");
+ DefaultIssue ceIssue = new DefaultIssue()
+ .setRuleKey(RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), "InsufficientCoverage"))
+ .setMessage("not enough coverage")
+ .setGap(10.0);
+ when(commonRuleEngine.process(FILE)).thenReturn(asList(ceIssue));
+
+ Input<DefaultIssue> input = underTest.create(FILE);
+
+ assertThat(input.getIssues()).isEmpty();
+ }
+
private void assertInitializedIssue(DefaultIssue issue) {
assertThat(issue.componentKey()).isEqualTo(FILE.getKey());
assertThat(issue.componentUuid()).isEqualTo(FILE.getUuid());
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.server.computation.issue.filter;
+
+import com.google.common.base.Joiner;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.Settings;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.SettingsRepository;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+import static org.sonar.server.computation.component.ReportComponent.builder;
+
+public class IssueFilterTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ static final RuleKey XOO_X1 = RuleKey.of("xoo", "x1");
+ static final RuleKey XOO_X2 = RuleKey.of("xoo", "x2");
+ static final RuleKey XOO_X3 = RuleKey.of("xoo", "x3");
+
+ static final String PATH1 = "src/main/xoo/File1.xoo";
+ static final String PATH2 = "src/main/xoo/File2.xoo";
+ static final String PATH3 = "src/main/xoo/File3.xoo";
+
+ static final Component PROJECT = builder(Component.Type.PROJECT, 10).build();
+
+ static final Component COMPONENT_1 = builder(FILE, 1).setKey("File1").setPath(PATH1).build();
+ static final Component COMPONENT_2 = builder(FILE, 2).setKey("File2").setPath(PATH2).build();
+ static final Component COMPONENT_3 = builder(FILE, 3).setKey("File3").setPath(PATH3).build();
+
+ static final DefaultIssue ISSUE_1 = new DefaultIssue().setRuleKey(XOO_X1);
+ static final DefaultIssue ISSUE_2 = new DefaultIssue().setRuleKey(XOO_X2);
+ static final DefaultIssue ISSUE_3 = new DefaultIssue().setRuleKey(XOO_X3);
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(PROJECT);
+
+ SettingsRepository settingsRepository = mock(SettingsRepository.class);
+
+ @Test
+ public void accept_everything_when_no_filter_properties() throws Exception {
+ IssueFilter underTest = newIssueFilter(new Settings());
+
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isTrue();
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_2)).isTrue();
+ assertThat(underTest.accept(ISSUE_3, COMPONENT_3)).isTrue();
+ }
+
+ @Test
+ public void ignore_all() throws Exception {
+ IssueFilter underTest = newIssueFilter(newSettings(asList("*", "**"), Collections.<String>emptyList()));
+
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isFalse();
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_1)).isFalse();
+ assertThat(underTest.accept(ISSUE_3, COMPONENT_1)).isFalse();
+ }
+
+ @Test
+ public void ignore_some_rule_and_component() throws Exception {
+ IssueFilter underTest = newIssueFilter(newSettings(asList("xoo:x1", "**/xoo/File1*"), Collections.<String>emptyList()));
+
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isFalse();
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_2)).isTrue();
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_1)).isTrue();
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_2)).isTrue();
+ }
+
+ @Test
+ public void ignore_many_rules() throws Exception {
+ IssueFilter underTest = newIssueFilter(newSettings(
+ asList("xoo:x1", "**/xoo/File1*", "xoo:x2", "**/xoo/File1*"),
+ Collections.<String>emptyList())
+ );
+
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isFalse();
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_2)).isTrue();
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_1)).isFalse();
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_2)).isTrue();
+ }
+
+ @Test
+ public void include_all() throws Exception {
+ IssueFilter underTest = newIssueFilter(newSettings(Collections.<String>emptyList(), asList("*", "**")));
+
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isTrue();
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_1)).isTrue();
+ assertThat(underTest.accept(ISSUE_3, COMPONENT_1)).isTrue();
+ }
+
+ @Test
+ public void include_some_rule_and_component() throws Exception {
+ IssueFilter underTest = newIssueFilter(newSettings(Collections.<String>emptyList(), asList("xoo:x1", "**/xoo/File1*")));
+
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isTrue();
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_2)).isFalse();
+ // Issues on other rule are accepted
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_1)).isTrue();
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_2)).isTrue();
+ }
+
+ @Test
+ public void ignore_and_include_same_rule_and_component() throws Exception {
+ IssueFilter underTest = newIssueFilter(newSettings(
+ asList("xoo:x1", "**/xoo/File1*"),
+ asList("xoo:x1", "**/xoo/File1*")));
+
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isFalse();
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_2)).isFalse();
+ // Issues on other rule are accepted
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_1)).isTrue();
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_2)).isTrue();
+ }
+
+ @Test
+ public void include_many_rules() throws Exception {
+ IssueFilter underTest = newIssueFilter(newSettings(
+ Collections.<String>emptyList(),
+ asList("xoo:x1", "**/xoo/File1*", "xoo:x2", "**/xoo/File1*")
+ ));
+
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_1)).isTrue();
+ assertThat(underTest.accept(ISSUE_1, COMPONENT_2)).isFalse();
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_1)).isTrue();
+ assertThat(underTest.accept(ISSUE_2, COMPONENT_2)).isFalse();
+ }
+
+ @Test
+ public void accept_project_issues() throws Exception {
+ IssueFilter underTest = newIssueFilter(newSettings(
+ asList("xoo:x1", "**/xoo/File1*"),
+ asList("xoo:x1", "**/xoo/File1*")));
+
+ assertThat(underTest.accept(ISSUE_1, PROJECT)).isTrue();
+ assertThat(underTest.accept(ISSUE_2, PROJECT)).isTrue();
+ }
+
+ @Test
+ public void fail_when_only_rule_key_parameter() throws Exception {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("File path pattern cannot be empty. Please check 'sonar.issue.ignore.multicriteria' settings");
+
+ newIssueFilter(newSettings(asList("xoo:x1", ""), Collections.<String>emptyList()));
+ }
+
+ @Test
+ public void fail_when_only_path_parameter() throws Exception {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Rule key pattern cannot be empty. Please check 'sonar.issue.enforce.multicriteria' settings");
+
+ newIssueFilter(newSettings(Collections.<String>emptyList(), asList("", "**")));
+ }
+
+ private IssueFilter newIssueFilter(Settings settings) {
+ when(settingsRepository.getSettings(PROJECT)).thenReturn(settings);
+ return new IssueFilter(treeRootHolder, settingsRepository);
+ }
+
+ private static Settings newSettings(List<String> exclusionsProperties, List<String> inclusionsProperties) {
+ Settings settings = new Settings();
+ if (!exclusionsProperties.isEmpty()) {
+ addProperties(exclusionsProperties, "ignore", settings);
+ }
+ if (!inclusionsProperties.isEmpty()) {
+ addProperties(inclusionsProperties, "enforce", settings);
+ }
+ return settings;
+ }
+
+ private static void addProperties(List<String> properties, String property, Settings settings) {
+ if (!properties.isEmpty()) {
+ List<Integer> indexes = new ArrayList<>();
+ int index = 1;
+ for (int i = 0; i < properties.size(); i += 2) {
+ settings.setProperty("sonar.issue." + property + ".multicriteria." + index + ".ruleKey", properties.get(i));
+ settings.setProperty("sonar.issue." + property + ".multicriteria." + index + ".resourceKey", properties.get(i + 1));
+ indexes.add(index);
+ index++;
+ }
+ settings.setProperty("sonar.issue." + property + ".multicriteria", Joiner.on(",").join(indexes));
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.server.computation.issue.filter;
+
+import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IssuePatternTest {
+
+ @Test
+ public void match_file() {
+ String javaFile = "org/foo/Bar.xoo";
+ assertThat(new IssuePattern("org/foo/Bar*", "*").matchComponent(javaFile)).isTrue();
+ assertThat(new IssuePattern("org/foo/*", "*").matchComponent(javaFile)).isTrue();
+ assertThat(new IssuePattern("**/*ar*", "*").matchComponent(javaFile)).isTrue();
+ assertThat(new IssuePattern("org/**/?ar.xoo", "*").matchComponent(javaFile)).isTrue();
+ assertThat(new IssuePattern("**", "*").matchComponent(javaFile)).isTrue();
+
+ assertThat(new IssuePattern("org/other/Hello", "*").matchComponent(javaFile)).isFalse();
+ assertThat(new IssuePattern("org/foo/Hello", "*").matchComponent(javaFile)).isFalse();
+ assertThat(new IssuePattern("org/**/??ar.xoo", "*").matchComponent(javaFile)).isFalse();
+ assertThat(new IssuePattern("org/**/??ar.xoo", "*").matchComponent(null)).isFalse();
+ assertThat(new IssuePattern("org/**/??ar.xoo", "*").matchComponent("plop")).isFalse();
+ }
+
+ @Test
+ public void match_rule() {
+ RuleKey rule = Rule.create("checkstyle", "IllegalRegexp", "").ruleKey();
+ assertThat(new IssuePattern("*", "*").matchRule(rule)).isTrue();
+ assertThat(new IssuePattern("*", "checkstyle:*").matchRule(rule)).isTrue();
+ assertThat(new IssuePattern("*", "checkstyle:IllegalRegexp").matchRule(rule)).isTrue();
+ assertThat(new IssuePattern("*", "checkstyle:Illegal*").matchRule(rule)).isTrue();
+ assertThat(new IssuePattern("*", "*:*Illegal*").matchRule(rule)).isTrue();
+
+ assertThat(new IssuePattern("*", "pmd:IllegalRegexp").matchRule(rule)).isFalse();
+ assertThat(new IssuePattern("*", "pmd:*").matchRule(rule)).isFalse();
+ assertThat(new IssuePattern("*", "*:Foo*IllegalRegexp").matchRule(rule)).isFalse();
+ }
+
+ @Test
+ public void test_to_string() {
+ IssuePattern pattern = new IssuePattern("*", "checkstyle:*");
+
+ assertThat(pattern.toString()).isEqualTo(
+ "IssuePattern{componentPattern=*, rulePattern=checkstyle:*}");
+ }
+}