@@ -21,7 +21,6 @@ package org.sonar.api.batch.rule.internal; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import org.sonar.api.batch.rule.ActiveRules; | |||
import org.sonar.api.rule.RuleKey; | |||
/** | |||
@@ -42,7 +41,7 @@ public class ActiveRulesBuilder { | |||
return this; | |||
} | |||
public ActiveRules build() { | |||
public DefaultActiveRules build() { | |||
return new DefaultActiveRules(map.values()); | |||
} | |||
} |
@@ -21,7 +21,9 @@ package org.sonar.api.batch.rule.internal; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import javax.annotation.concurrent.Immutable; | |||
import org.sonar.api.batch.rule.ActiveRule; | |||
import org.sonar.api.rule.RuleKey; | |||
@@ -37,6 +39,7 @@ public class DefaultActiveRule implements ActiveRule { | |||
private final long createdAt; | |||
private final long updatedAt; | |||
private final String qProfileKey; | |||
private final Set<RuleKey> deprecatedKeys; | |||
public DefaultActiveRule(NewActiveRule newActiveRule) { | |||
this.severity = newActiveRule.severity; | |||
@@ -48,6 +51,7 @@ public class DefaultActiveRule implements ActiveRule { | |||
this.createdAt = newActiveRule.createdAt; | |||
this.updatedAt = newActiveRule.updatedAt; | |||
this.qProfileKey = newActiveRule.qProfileKey; | |||
this.deprecatedKeys = Collections.unmodifiableSet(new HashSet<>(newActiveRule.deprecatedKeys)); | |||
} | |||
@Override | |||
@@ -98,4 +102,9 @@ public class DefaultActiveRule implements ActiveRule { | |||
public String qpKey() { | |||
return qProfileKey; | |||
} | |||
public Set<RuleKey> getDeprecatedKeys() { | |||
// already immutable | |||
return deprecatedKeys; | |||
} | |||
} |
@@ -23,16 +23,22 @@ import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.LinkedHashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.stream.Collectors; | |||
import javax.annotation.concurrent.Immutable; | |||
import org.sonar.api.batch.rule.ActiveRule; | |||
import org.sonar.api.batch.rule.ActiveRules; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.WildcardPattern; | |||
import static java.util.Collections.emptySet; | |||
@Immutable | |||
public class DefaultActiveRules implements ActiveRules { | |||
private final Map<RuleKey, Set<String>> deprecatedRuleKeysByRuleKey = new LinkedHashMap<>(); | |||
private final Map<String, List<ActiveRule>> activeRulesByRepository = new HashMap<>(); | |||
private final Map<String, Map<String, ActiveRule>> activeRulesByRepositoryAndKey = new HashMap<>(); | |||
private final Map<String, Map<String, ActiveRule>> activeRulesByRepositoryAndInternalKey = new HashMap<>(); | |||
@@ -52,9 +58,19 @@ public class DefaultActiveRules implements ActiveRules { | |||
if (internalKey != null) { | |||
activeRulesByRepositoryAndInternalKey.computeIfAbsent(repo, r -> new HashMap<>()).put(internalKey, ar); | |||
} | |||
deprecatedRuleKeysByRuleKey.put(ar.ruleKey(), ar.getDeprecatedKeys().stream().map(RuleKey::toString).collect(Collectors.toSet())); | |||
} | |||
} | |||
public Set<String> getDeprecatedRuleKeys(RuleKey ruleKey) { | |||
return deprecatedRuleKeysByRuleKey.getOrDefault(ruleKey, emptySet()); | |||
} | |||
public boolean matchesDeprecatedKeys(RuleKey ruleKey, WildcardPattern rulePattern) { | |||
return getDeprecatedRuleKeys(ruleKey).contains(rulePattern.toString()); | |||
} | |||
@Override | |||
public ActiveRule find(RuleKey ruleKey) { | |||
return activeRulesByRepositoryAndKey.getOrDefault(ruleKey.repository(), Collections.emptyMap()) |
@@ -20,7 +20,9 @@ | |||
package org.sonar.api.batch.rule.internal; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import javax.annotation.Nullable; | |||
import javax.annotation.concurrent.Immutable; | |||
import org.apache.commons.lang.StringUtils; | |||
@@ -42,6 +44,7 @@ public class NewActiveRule { | |||
final String language; | |||
final String templateRuleKey; | |||
final String qProfileKey; | |||
final Set<RuleKey> deprecatedKeys; | |||
NewActiveRule(Builder builder) { | |||
this.ruleKey = builder.ruleKey; | |||
@@ -54,6 +57,7 @@ public class NewActiveRule { | |||
this.language = builder.language; | |||
this.templateRuleKey = builder.templateRuleKey; | |||
this.qProfileKey = builder.qProfileKey; | |||
this.deprecatedKeys = builder.deprecatedKeys; | |||
} | |||
public RuleKey ruleKey() { | |||
@@ -71,6 +75,7 @@ public class NewActiveRule { | |||
private String language; | |||
private String templateRuleKey; | |||
private String qProfileKey; | |||
private Set<RuleKey> deprecatedKeys = new HashSet<>(); | |||
public Builder setRuleKey(RuleKey ruleKey) { | |||
this.ruleKey = ruleKey; | |||
@@ -127,6 +132,11 @@ public class NewActiveRule { | |||
return this; | |||
} | |||
public Builder setDeprecatedKeys(Set<RuleKey> deprecatedKeys) { | |||
this.deprecatedKeys = deprecatedKeys; | |||
return this; | |||
} | |||
public NewActiveRule build() { | |||
return new NewActiveRule(this); | |||
} |
@@ -0,0 +1,63 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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.api.batch.rule.internal; | |||
import com.google.common.collect.ImmutableSet; | |||
import java.util.Collections; | |||
import org.junit.Test; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.WildcardPattern; | |||
import static java.util.Collections.singleton; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class DefaultActiveRulesTest { | |||
private final RuleKey ruleKey = RuleKey.of("repo", "rule"); | |||
@Test | |||
public void empty_returns_nothing() { | |||
DefaultActiveRules underTest = new DefaultActiveRules(Collections.emptyList()); | |||
assertThat(underTest.getDeprecatedRuleKeys(ruleKey)).isEmpty(); | |||
assertThat(underTest.matchesDeprecatedKeys(ruleKey, WildcardPattern.create("**"))).isFalse(); | |||
} | |||
@Test | |||
public void finds_match() { | |||
DefaultActiveRules underTest = new DefaultActiveRules(ImmutableSet.of(new NewActiveRule.Builder() | |||
.setRuleKey(ruleKey) | |||
.setDeprecatedKeys(singleton(RuleKey.of("oldrepo", "oldrule"))) | |||
.build())); | |||
assertThat(underTest.getDeprecatedRuleKeys(ruleKey)).containsOnly("oldrepo:oldrule"); | |||
assertThat(underTest.matchesDeprecatedKeys(ruleKey, WildcardPattern.create("oldrepo:oldrule"))).isTrue(); | |||
} | |||
@Test | |||
public void finds_match_with_multiple_deprecated_keys() { | |||
DefaultActiveRules underTest = new DefaultActiveRules(ImmutableSet.of(new NewActiveRule.Builder() | |||
.setRuleKey(ruleKey) | |||
.setDeprecatedKeys(ImmutableSet.of(RuleKey.of("oldrepo", "oldrule"), (RuleKey.of("oldrepo2", "oldrule2")))) | |||
.build())); | |||
assertThat(underTest.getDeprecatedRuleKeys(ruleKey)).containsOnly("oldrepo:oldrule", "oldrepo2:oldrule2"); | |||
assertThat(underTest.matchesDeprecatedKeys(ruleKey, WildcardPattern.create("oldrepo:oldrule"))).isTrue(); | |||
} | |||
} |
@@ -21,16 +21,20 @@ package org.sonar.scanner.issue.ignore; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import javax.annotation.concurrent.ThreadSafe; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.rule.internal.DefaultActiveRules; | |||
import org.sonar.api.notifications.AnalysisWarnings; | |||
import org.sonar.api.rule.RuleKey; | |||
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.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.scanner.issue.DefaultFilterableIssue; | |||
import org.sonar.scanner.issue.ignore.pattern.IssueInclusionPatternInitializer; | |||
import org.sonar.scanner.issue.ignore.pattern.IssuePattern; | |||
@@ -41,12 +45,15 @@ public class EnforceIssuesFilter implements IssueFilter { | |||
private final List<IssuePattern> multicriteriaPatterns; | |||
private final AnalysisWarnings analysisWarnings; | |||
private final DefaultActiveRules activeRules; | |||
private final Set<RuleKey> warnedDeprecatedRuleKeys = new LinkedHashSet<>(); | |||
private boolean warnDeprecatedIssuePatternAlreadyLogged; | |||
public EnforceIssuesFilter(IssueInclusionPatternInitializer patternInitializer, AnalysisWarnings analysisWarnings) { | |||
public EnforceIssuesFilter(IssueInclusionPatternInitializer patternInitializer, AnalysisWarnings analysisWarnings, DefaultActiveRules activeRules) { | |||
this.multicriteriaPatterns = Collections.unmodifiableList(new ArrayList<>(patternInitializer.getMulticriteriaPatterns())); | |||
this.analysisWarnings = analysisWarnings; | |||
this.activeRules = activeRules; | |||
} | |||
@Override | |||
@@ -56,7 +63,7 @@ public class EnforceIssuesFilter implements IssueFilter { | |||
IssuePattern matchingPattern = null; | |||
for (IssuePattern pattern : multicriteriaPatterns) { | |||
if (pattern.matchRule(issue.ruleKey())) { | |||
if (ruleMatches(pattern, issue.ruleKey())) { | |||
atLeastOneRuleMatched = true; | |||
InputComponent component = ((DefaultFilterableIssue) issue).getComponent(); | |||
if (component.isFile()) { | |||
@@ -78,7 +85,7 @@ public class EnforceIssuesFilter implements IssueFilter { | |||
if (atLeastOneRuleMatched) { | |||
if (atLeastOnePatternFullyMatched) { | |||
LOG.debug("Issue {} enforced by pattern {}", issue, matchingPattern); | |||
LOG.debug("Issue '{}' enforced by pattern '{}'", issue, matchingPattern); | |||
} | |||
return atLeastOnePatternFullyMatched; | |||
} else { | |||
@@ -86,6 +93,19 @@ public class EnforceIssuesFilter implements IssueFilter { | |||
} | |||
} | |||
private boolean ruleMatches(IssuePattern pattern, RuleKey ruleKey) { | |||
if (activeRules.matchesDeprecatedKeys(ruleKey, pattern.getRulePattern())) { | |||
String msg = String.format("The issue multicriteria pattern '%s' matches a rule key that has been changed. The pattern should be updated to '%s'", | |||
pattern.getRulePattern(), ruleKey); | |||
analysisWarnings.addUnique(msg); | |||
if (warnedDeprecatedRuleKeys.add(ruleKey)) { | |||
LOG.warn(msg); | |||
} | |||
return true; | |||
} | |||
return pattern.matchRule(ruleKey); | |||
} | |||
private void warnOnceDeprecatedIssuePattern(String msg) { | |||
if (!warnDeprecatedIssuePatternAlreadyLogged) { | |||
LOG.warn(msg); |
@@ -21,11 +21,16 @@ package org.sonar.scanner.issue.ignore; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.LinkedHashSet; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.rule.internal.DefaultActiveRules; | |||
import org.sonar.api.notifications.AnalysisWarnings; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.scan.issue.filter.FilterableIssue; | |||
import org.sonar.api.scan.issue.filter.IssueFilter; | |||
import org.sonar.api.scan.issue.filter.IssueFilterChain; | |||
@@ -36,10 +41,17 @@ import org.sonar.scanner.issue.DefaultFilterableIssue; | |||
public class IgnoreIssuesFilter implements IssueFilter { | |||
private Map<InputComponent, List<WildcardPattern>> rulePatternByComponent = new HashMap<>(); | |||
private final DefaultActiveRules activeRules; | |||
private final AnalysisWarnings analysisWarnings; | |||
private final Map<InputComponent, List<WildcardPattern>> rulePatternByComponent = new HashMap<>(); | |||
private final Set<RuleKey> warnedDeprecatedRuleKeys = new LinkedHashSet<>(); | |||
private static final Logger LOG = Loggers.get(IgnoreIssuesFilter.class); | |||
public IgnoreIssuesFilter(DefaultActiveRules activeRules, AnalysisWarnings analysisWarnings) { | |||
this.activeRules = activeRules; | |||
this.analysisWarnings = analysisWarnings; | |||
} | |||
@Override | |||
public boolean accept(FilterableIssue issue, IssueFilterChain chain) { | |||
InputComponent component = ((DefaultFilterableIssue) issue).getComponent(); | |||
@@ -73,7 +85,18 @@ public class IgnoreIssuesFilter implements IssueFilter { | |||
private boolean hasRuleMatchFor(InputComponent component, FilterableIssue issue) { | |||
for (WildcardPattern pattern : rulePatternByComponent.getOrDefault(component, Collections.emptyList())) { | |||
if (pattern.match(issue.ruleKey().toString())) { | |||
LOG.debug("Issue {} ignored by exclusion pattern {}", issue, pattern); | |||
LOG.debug("Issue '{}' ignored by exclusion pattern '{}'", issue, pattern); | |||
return true; | |||
} | |||
RuleKey ruleKey = issue.ruleKey(); | |||
if (activeRules.matchesDeprecatedKeys(ruleKey, pattern)) { | |||
String msg = String.format("The issue multicriteria pattern '%s' matches a rule key that has been changed. The pattern should be updated to '%s'", pattern, ruleKey); | |||
analysisWarnings.addUnique(msg); | |||
if (warnedDeprecatedRuleKeys.add(ruleKey)) { | |||
LOG.warn(msg); | |||
} | |||
LOG.debug("Issue '{}' ignored by exclusion pattern '{}' matching a deprecated rule key", issue, pattern); | |||
return true; | |||
} | |||
} |
@@ -26,7 +26,7 @@ import org.sonar.api.config.Configuration; | |||
import org.sonar.api.utils.MessageException; | |||
public abstract class AbstractPatternInitializer { | |||
private Configuration settings; | |||
private final Configuration settings; | |||
private List<IssuePattern> multicriteriaPatterns; | |||
protected AbstractPatternInitializer(Configuration config) { | |||
@@ -63,7 +63,7 @@ public abstract class AbstractPatternInitializer { | |||
if (StringUtils.isBlank(ruleKeyPattern)) { | |||
throw MessageException.of("Issue exclusions are misconfigured. Rule key pattern is mandatory for each entry of '" + getMulticriteriaConfigurationKey() + "'"); | |||
} | |||
IssuePattern pattern = new IssuePattern(filePathPattern != null ? filePathPattern : "*", ruleKeyPattern != null ? ruleKeyPattern : "*"); | |||
IssuePattern pattern = new IssuePattern(filePathPattern, ruleKeyPattern); | |||
multicriteriaPatterns.add(pattern); | |||
} |
@@ -19,6 +19,7 @@ | |||
*/ | |||
package org.sonar.scanner.issue.ignore.pattern; | |||
import com.google.common.base.MoreObjects; | |||
import javax.annotation.Nullable; | |||
import javax.annotation.concurrent.Immutable; | |||
import org.sonar.api.rule.RuleKey; | |||
@@ -47,4 +48,12 @@ public class IssuePattern { | |||
return filePath != null && filePattern.match(filePath); | |||
} | |||
@Override | |||
public String toString() { | |||
return MoreObjects.toStringHelper(this) | |||
.add("filePattern", filePattern) | |||
.add("rulePattern", rulePattern) | |||
.toString(); | |||
} | |||
} |
@@ -26,9 +26,9 @@ import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.picocontainer.injectors.ProviderAdapter; | |||
import org.sonar.api.batch.rule.ActiveRules; | |||
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; | |||
import org.sonar.api.batch.rule.LoadedActiveRule; | |||
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; | |||
import org.sonar.api.batch.rule.internal.DefaultActiveRules; | |||
import org.sonar.api.batch.rule.internal.NewActiveRule; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.log.Logger; | |||
@@ -42,9 +42,9 @@ import org.sonar.api.utils.log.Profiler; | |||
public class ActiveRulesProvider extends ProviderAdapter { | |||
private static final Logger LOG = Loggers.get(ActiveRulesProvider.class); | |||
private static final String LOG_MSG = "Load active rules"; | |||
private ActiveRules singleton = null; | |||
private DefaultActiveRules singleton = null; | |||
public ActiveRules provide(ActiveRulesLoader loader, QualityProfiles qProfiles) { | |||
public DefaultActiveRules provide(ActiveRulesLoader loader, QualityProfiles qProfiles) { | |||
if (singleton == null) { | |||
Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); | |||
singleton = load(loader, qProfiles); | |||
@@ -53,7 +53,7 @@ public class ActiveRulesProvider extends ProviderAdapter { | |||
return singleton; | |||
} | |||
private static ActiveRules load(ActiveRulesLoader loader, QualityProfiles qProfiles) { | |||
private static DefaultActiveRules load(ActiveRulesLoader loader, QualityProfiles qProfiles) { | |||
Collection<String> qProfileKeys = getKeys(qProfiles); | |||
Set<RuleKey> loadedRulesKey = new HashSet<>(); | |||
@@ -65,7 +65,7 @@ public class ActiveRulesProvider extends ProviderAdapter { | |||
for (LoadedActiveRule r : qProfileRules) { | |||
if (!loadedRulesKey.contains(r.getRuleKey())) { | |||
loadedRulesKey.add(r.getRuleKey()); | |||
builder.addRule(transform(r, qProfileKey)); | |||
builder.addRule(transform(r, qProfileKey, r.getDeprecatedKeys())); | |||
} | |||
} | |||
} | |||
@@ -73,7 +73,7 @@ public class ActiveRulesProvider extends ProviderAdapter { | |||
return builder.build(); | |||
} | |||
private static NewActiveRule transform(LoadedActiveRule activeRule, String qProfileKey) { | |||
private static NewActiveRule transform(LoadedActiveRule activeRule, String qProfileKey, Set<RuleKey> deprecatedKeys) { | |||
NewActiveRule.Builder builder = new NewActiveRule.Builder(); | |||
builder | |||
.setRuleKey(activeRule.getRuleKey()) | |||
@@ -84,7 +84,8 @@ public class ActiveRulesProvider extends ProviderAdapter { | |||
.setLanguage(activeRule.getLanguage()) | |||
.setInternalKey(activeRule.getInternalKey()) | |||
.setTemplateRuleKey(activeRule.getTemplateRuleKey()) | |||
.setQProfileKey(qProfileKey); | |||
.setQProfileKey(qProfileKey) | |||
.setDeprecatedKeys(deprecatedKeys); | |||
// load parameters | |||
if (activeRule.getParams() != null) { | |||
for (Map.Entry<String, String> params : activeRule.getParams().entrySet()) { |
@@ -20,6 +20,7 @@ | |||
package org.sonar.scanner.issue.ignore; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.ImmutableSet; | |||
import java.io.IOException; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
@@ -27,6 +28,8 @@ import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.batch.fs.InputComponent; | |||
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; | |||
import org.sonar.api.batch.rule.internal.DefaultActiveRules; | |||
import org.sonar.api.batch.rule.internal.NewActiveRule; | |||
import org.sonar.api.notifications.AnalysisWarnings; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.scan.issue.filter.IssueFilterChain; | |||
@@ -34,10 +37,11 @@ import org.sonar.scanner.issue.DefaultFilterableIssue; | |||
import org.sonar.scanner.issue.ignore.pattern.IssueInclusionPatternInitializer; | |||
import org.sonar.scanner.issue.ignore.pattern.IssuePattern; | |||
import static java.util.Collections.singleton; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyZeroInteractions; | |||
import static org.mockito.Mockito.verifyNoInteractions; | |||
import static org.mockito.Mockito.when; | |||
public class EnforceIssuesFilterTest { | |||
@@ -45,59 +49,74 @@ public class EnforceIssuesFilterTest { | |||
@Rule | |||
public TemporaryFolder tempFolder = new TemporaryFolder(); | |||
private IssueInclusionPatternInitializer exclusionPatternInitializer; | |||
private final IssueInclusionPatternInitializer exclusionPatternInitializer = mock(IssueInclusionPatternInitializer.class); | |||
private final DefaultFilterableIssue issue = mock(DefaultFilterableIssue.class); | |||
private final IssueFilterChain chain = mock(IssueFilterChain.class); | |||
private final AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class); | |||
private EnforceIssuesFilter ignoreFilter; | |||
private DefaultFilterableIssue issue; | |||
private IssueFilterChain chain; | |||
@Before | |||
public void init() { | |||
exclusionPatternInitializer = mock(IssueInclusionPatternInitializer.class); | |||
issue = mock(DefaultFilterableIssue.class); | |||
chain = mock(IssueFilterChain.class); | |||
when(chain.accept(issue)).thenReturn(true); | |||
} | |||
@Test | |||
public void shouldPassToChainIfNoConfiguredPatterns() { | |||
ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, mock(AnalysisWarnings.class)); | |||
DefaultActiveRules activeRules = new DefaultActiveRules(ImmutableSet.of()); | |||
ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, analysisWarnings, activeRules); | |||
assertThat(ignoreFilter.accept(issue, chain)).isTrue(); | |||
verify(chain).accept(issue); | |||
} | |||
@Test | |||
public void shouldPassToChainIfRuleDoesNotMatch() { | |||
String rule = "rule"; | |||
RuleKey ruleKey = mock(RuleKey.class); | |||
when(ruleKey.toString()).thenReturn(rule); | |||
DefaultActiveRules activeRules = new DefaultActiveRules(ImmutableSet.of()); | |||
RuleKey ruleKey = RuleKey.of("repo", "rule"); | |||
when(issue.ruleKey()).thenReturn(ruleKey); | |||
IssuePattern matching = mock(IssuePattern.class); | |||
when(matching.matchRule(ruleKey)).thenReturn(false); | |||
IssuePattern matching = new IssuePattern("**", "unknown"); | |||
when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); | |||
ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, mock(AnalysisWarnings.class)); | |||
ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, analysisWarnings, activeRules); | |||
assertThat(ignoreFilter.accept(issue, chain)).isTrue(); | |||
verify(chain).accept(issue); | |||
} | |||
@Test | |||
public void shouldAcceptIssueIfFullyMatched() { | |||
String rule = "rule"; | |||
DefaultActiveRules activeRules = new DefaultActiveRules(ImmutableSet.of()); | |||
String path = "org/sonar/api/Issue.java"; | |||
RuleKey ruleKey = mock(RuleKey.class); | |||
when(ruleKey.toString()).thenReturn(rule); | |||
RuleKey ruleKey = RuleKey.of("repo", "rule"); | |||
when(issue.ruleKey()).thenReturn(ruleKey); | |||
IssuePattern matching = mock(IssuePattern.class); | |||
when(matching.matchRule(ruleKey)).thenReturn(true); | |||
when(matching.matchFile(path)).thenReturn(true); | |||
IssuePattern matching = new IssuePattern(path, ruleKey.toString()); | |||
when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); | |||
when(issue.getComponent()).thenReturn(createComponentWithPath(path)); | |||
ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, mock(AnalysisWarnings.class)); | |||
ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, analysisWarnings, activeRules); | |||
assertThat(ignoreFilter.accept(issue, chain)).isTrue(); | |||
verifyZeroInteractions(chain); | |||
verifyNoInteractions(chain); | |||
} | |||
@Test | |||
public void shouldAcceptIssueIfMatchesDeprecatedRuleKey() { | |||
RuleKey ruleKey = RuleKey.of("repo", "rule"); | |||
DefaultActiveRules activeRules = new DefaultActiveRules(ImmutableSet.of(new NewActiveRule.Builder() | |||
.setRuleKey(ruleKey) | |||
.setDeprecatedKeys(singleton(RuleKey.of("repo2", "deprecated"))) | |||
.build())); | |||
String path = "org/sonar/api/Issue.java"; | |||
when(issue.ruleKey()).thenReturn(ruleKey); | |||
IssuePattern matching = new IssuePattern("org/**", "repo2:deprecated"); | |||
when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); | |||
when(issue.getComponent()).thenReturn(createComponentWithPath(path)); | |||
ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, analysisWarnings, activeRules); | |||
assertThat(ignoreFilter.accept(issue, chain)).isTrue(); | |||
verify(analysisWarnings) | |||
.addUnique("The issue multicriteria pattern 'repo2:deprecated' matches a rule key that has been changed. The pattern should be updated to 'repo:rule'"); | |||
verifyNoInteractions(chain); | |||
} | |||
private InputComponent createComponentWithPath(String path) { | |||
@@ -106,42 +125,35 @@ public class EnforceIssuesFilterTest { | |||
@Test | |||
public void shouldRefuseIssueIfRuleMatchesButNotPath() { | |||
String rule = "rule"; | |||
DefaultActiveRules activeRules = new DefaultActiveRules(ImmutableSet.of()); | |||
String path = "org/sonar/api/Issue.java"; | |||
String componentKey = "org.sonar.api.Issue"; | |||
RuleKey ruleKey = mock(RuleKey.class); | |||
when(ruleKey.toString()).thenReturn(rule); | |||
RuleKey ruleKey = RuleKey.of("repo", "rule"); | |||
when(issue.ruleKey()).thenReturn(ruleKey); | |||
when(issue.componentKey()).thenReturn(componentKey); | |||
IssuePattern matching = mock(IssuePattern.class); | |||
when(matching.matchRule(ruleKey)).thenReturn(true); | |||
when(matching.matchFile(path)).thenReturn(false); | |||
IssuePattern matching = new IssuePattern("no match", "repo:rule"); | |||
when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); | |||
when(issue.getComponent()).thenReturn(createComponentWithPath(path)); | |||
ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, mock(AnalysisWarnings.class)); | |||
ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, analysisWarnings, activeRules); | |||
assertThat(ignoreFilter.accept(issue, chain)).isFalse(); | |||
verifyZeroInteractions(chain); | |||
verifyNoInteractions(chain, analysisWarnings); | |||
} | |||
@Test | |||
public void shouldRefuseIssueIfRuleMatchesAndNotFile() throws IOException { | |||
String rule = "rule"; | |||
DefaultActiveRules activeRules = new DefaultActiveRules(ImmutableSet.of()); | |||
String path = "org/sonar/api/Issue.java"; | |||
String componentKey = "org.sonar.api.Issue"; | |||
RuleKey ruleKey = mock(RuleKey.class); | |||
when(ruleKey.toString()).thenReturn(rule); | |||
RuleKey ruleKey = RuleKey.of("repo", "key"); | |||
when(issue.ruleKey()).thenReturn(ruleKey); | |||
IssuePattern matching = mock(IssuePattern.class); | |||
when(matching.matchRule(ruleKey)).thenReturn(true); | |||
when(matching.matchFile(path)).thenReturn(true); | |||
IssuePattern matching = new IssuePattern(path, ruleKey.toString()); | |||
when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); | |||
when(issue.getComponent()).thenReturn(TestInputFileBuilder.newDefaultInputProject("foo", tempFolder.newFolder())); | |||
ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, mock(AnalysisWarnings.class)); | |||
ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, analysisWarnings, activeRules); | |||
assertThat(ignoreFilter.accept(issue, chain)).isFalse(); | |||
verifyZeroInteractions(chain); | |||
verifyNoInteractions(chain, analysisWarnings); | |||
} | |||
} |
@@ -19,27 +19,38 @@ | |||
*/ | |||
package org.sonar.scanner.issue.ignore; | |||
import com.google.common.collect.ImmutableSet; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.batch.rule.internal.DefaultActiveRules; | |||
import org.sonar.api.batch.rule.internal.NewActiveRule; | |||
import org.sonar.api.notifications.AnalysisWarnings; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.scan.issue.filter.IssueFilterChain; | |||
import org.sonar.api.utils.WildcardPattern; | |||
import org.sonar.api.batch.fs.internal.DefaultInputFile; | |||
import org.sonar.api.utils.log.LogTester; | |||
import org.sonar.scanner.issue.DefaultFilterableIssue; | |||
import static java.util.Collections.singleton; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.ArgumentMatchers.any; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyNoInteractions; | |||
import static org.mockito.Mockito.when; | |||
public class IgnoreIssuesFilterTest { | |||
@Rule | |||
public LogTester logTester = new LogTester(); | |||
private final DefaultFilterableIssue issue = mock(DefaultFilterableIssue.class); | |||
private final IssueFilterChain chain = mock(IssueFilterChain.class); | |||
private final AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class); | |||
private final RuleKey ruleKey = RuleKey.of("foo", "bar"); | |||
private DefaultFilterableIssue issue = mock(DefaultFilterableIssue.class); | |||
private IssueFilterChain chain = mock(IssueFilterChain.class); | |||
private IgnoreIssuesFilter underTest = new IgnoreIssuesFilter(); | |||
private DefaultInputFile component; | |||
private RuleKey ruleKey = RuleKey.of("foo", "bar"); | |||
@Before | |||
public void prepare() { | |||
@@ -50,6 +61,9 @@ public class IgnoreIssuesFilterTest { | |||
@Test | |||
public void shouldPassToChainIfMatcherHasNoPatternForIssue() { | |||
DefaultActiveRules activeRules = new DefaultActiveRules(ImmutableSet.of()); | |||
IgnoreIssuesFilter underTest = new IgnoreIssuesFilter(activeRules, analysisWarnings); | |||
when(chain.accept(issue)).thenReturn(true); | |||
assertThat(underTest.accept(issue, chain)).isTrue(); | |||
verify(chain).accept(any()); | |||
@@ -57,15 +71,39 @@ public class IgnoreIssuesFilterTest { | |||
@Test | |||
public void shouldRejectIfRulePatternMatches() { | |||
DefaultActiveRules activeRules = new DefaultActiveRules(ImmutableSet.of()); | |||
IgnoreIssuesFilter underTest = new IgnoreIssuesFilter(activeRules, analysisWarnings); | |||
WildcardPattern pattern = mock(WildcardPattern.class); | |||
when(pattern.match(ruleKey.toString())).thenReturn(true); | |||
underTest.addRuleExclusionPatternForComponent(component, pattern); | |||
assertThat(underTest.accept(issue, chain)).isFalse(); | |||
verifyNoInteractions(analysisWarnings); | |||
} | |||
@Test | |||
public void shouldRejectIfRulePatternMatchesDeprecatedRule() { | |||
DefaultActiveRules activeRules = new DefaultActiveRules(ImmutableSet.of(new NewActiveRule.Builder() | |||
.setRuleKey(ruleKey) | |||
.setDeprecatedKeys(singleton(RuleKey.of("repo", "rule"))) | |||
.build())); | |||
IgnoreIssuesFilter underTest = new IgnoreIssuesFilter(activeRules, analysisWarnings); | |||
WildcardPattern pattern = WildcardPattern.create("repo:rule"); | |||
underTest.addRuleExclusionPatternForComponent(component, pattern); | |||
assertThat(underTest.accept(issue, chain)).isFalse(); | |||
verify(analysisWarnings).addUnique("The issue multicriteria pattern 'repo:rule' matches a rule key that has been changed. The pattern should be updated to 'foo:bar'"); | |||
assertThat(logTester.logs()) | |||
.contains("The issue multicriteria pattern 'repo:rule' matches a rule key that has been changed. The pattern should be updated to 'foo:bar'"); | |||
} | |||
@Test | |||
public void shouldAcceptIfRulePatternDoesNotMatch() { | |||
DefaultActiveRules activeRules = new DefaultActiveRules(ImmutableSet.of()); | |||
IgnoreIssuesFilter underTest = new IgnoreIssuesFilter(activeRules, analysisWarnings); | |||
WildcardPattern pattern = mock(WildcardPattern.class); | |||
when(pattern.match(ruleKey.toString())).thenReturn(false); | |||
underTest.addRuleExclusionPatternForComponent(component, pattern); |
@@ -57,4 +57,9 @@ public class IssuePatternTest { | |||
assertThat(new IssuePattern("*", "*:Foo*IllegalRegexp").matchRule(rule)).isFalse(); | |||
} | |||
@Test | |||
public void toString_should_include_all_fields() { | |||
assertThat(new IssuePattern("*", "*:Foo*IllegalRegexp").toString()).isEqualTo("IssuePattern{filePattern=*, rulePattern=*:Foo*IllegalRegexp}"); | |||
} | |||
} |
@@ -79,6 +79,8 @@ import org.sonarqube.ws.NewCodePeriods; | |||
import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; | |||
import org.sonarqube.ws.Rules.ListResponse.Rule; | |||
import static java.util.Collections.emptySet; | |||
/** | |||
* Main utility class for writing scanner medium tests. | |||
*/ | |||
@@ -209,6 +211,7 @@ public class ScannerMediumTester extends ExternalResource { | |||
r.setTemplateRuleKey(templateRuleKey); | |||
r.setLanguage(language); | |||
r.setSeverity(severity); | |||
r.setDeprecatedKeys(emptySet()); | |||
activeRules.addActiveRule(r); | |||
return this; |
@@ -30,14 +30,15 @@ import org.apache.commons.io.FileUtils; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.batch.rule.LoadedActiveRule; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | |||
import org.sonar.scanner.mediumtest.AnalysisResult; | |||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | |||
import org.sonar.scanner.protocol.output.ScannerReport.Issue; | |||
import org.sonar.api.batch.rule.LoadedActiveRule; | |||
import org.sonar.xoo.XooPlugin; | |||
import org.sonar.xoo.rule.XooRulesDefinition; | |||
import static java.util.Collections.emptySet; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.tuple; | |||
@@ -98,6 +99,7 @@ public class ChecksMediumTest { | |||
r.setTemplateRuleKey(templateRuleKey); | |||
r.setLanguage(languag); | |||
r.setSeverity(severity); | |||
r.setDeprecatedKeys(emptySet()); | |||
Map<String, String> params = new HashMap<>(); | |||
params.put(paramKey, paramValue); |
@@ -28,19 +28,20 @@ import org.apache.commons.io.FileUtils; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.batch.rule.LoadedActiveRule; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.log.LogTester; | |||
import org.sonar.api.utils.log.LoggerLevel; | |||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | |||
import org.sonar.scanner.mediumtest.AnalysisResult; | |||
import org.sonar.scanner.mediumtest.ScannerMediumTester; | |||
import org.sonar.scanner.protocol.output.ScannerReport.ExternalIssue; | |||
import org.sonar.scanner.protocol.output.ScannerReport.Issue; | |||
import org.sonar.api.batch.rule.LoadedActiveRule; | |||
import org.sonar.xoo.XooPlugin; | |||
import org.sonar.xoo.rule.HasTagSensor; | |||
import org.sonar.xoo.rule.OneExternalIssuePerLineSensor; | |||
import org.sonar.xoo.rule.XooRulesDefinition; | |||
import static java.util.Collections.emptySet; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.tuple; | |||
@@ -385,6 +386,8 @@ public class IssuesMediumTest { | |||
r.setName("TODO"); | |||
r.setLanguage("xoo"); | |||
r.setSeverity("MAJOR"); | |||
r.setDeprecatedKeys(emptySet() | |||
); | |||
r.setParams(ImmutableMap.of("tag", "TODO")); | |||
tester.activateRule(r); | |||
} |
@@ -26,33 +26,24 @@ import java.util.Date; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import org.assertj.core.groups.Tuple; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.mockito.Mock; | |||
import org.mockito.MockitoAnnotations; | |||
import org.sonar.api.batch.rule.ActiveRules; | |||
import org.sonar.api.batch.rule.LoadedActiveRule; | |||
import org.sonar.api.batch.rule.internal.DefaultActiveRules; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.DateUtils; | |||
import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.ArgumentMatchers.eq; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyNoMoreInteractions; | |||
import static org.mockito.Mockito.when; | |||
public class ActiveRulesProviderTest { | |||
private ActiveRulesProvider provider; | |||
@Mock | |||
private DefaultActiveRulesLoader loader; | |||
@Before | |||
public void setUp() { | |||
MockitoAnnotations.initMocks(this); | |||
provider = new ActiveRulesProvider(); | |||
} | |||
private final ActiveRulesProvider provider = new ActiveRulesProvider(); | |||
private final DefaultActiveRulesLoader loader = mock(DefaultActiveRulesLoader.class); | |||
@Test | |||
public void testCombinationOfRules() { | |||
@@ -69,7 +60,7 @@ public class ActiveRulesProviderTest { | |||
when(loader.load(eq("qp3"))).thenReturn(qp3Rules); | |||
QualityProfiles profiles = mockProfiles("qp1", "qp2", "qp3"); | |||
ActiveRules activeRules = provider.provide(loader, profiles); | |||
DefaultActiveRules activeRules = provider.provide(loader, profiles); | |||
assertThat(activeRules.findAll()).hasSize(3); | |||
assertThat(activeRules.findAll()).extracting("ruleKey").containsOnly( | |||
@@ -78,6 +69,8 @@ public class ActiveRulesProviderTest { | |||
verify(loader).load(eq("qp1")); | |||
verify(loader).load(eq("qp2")); | |||
verify(loader).load(eq("qp3")); | |||
assertThat(activeRules.getDeprecatedRuleKeys(RuleKey.of("rule1", "rule1"))).containsOnly("rule1old:rule1old"); | |||
verifyNoMoreInteractions(loader); | |||
} | |||