Browse Source

SONAR-14192 Deprecating rule keys may break users' exclusion settings

tags/8.8.0.42792
Duarte Meneses 3 years ago
parent
commit
398550aef2
17 changed files with 287 additions and 81 deletions
  1. 1
    2
      sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilder.java
  2. 9
    0
      sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRule.java
  3. 16
    0
      sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRules.java
  4. 10
    0
      sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java
  5. 63
    0
      sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/rule/internal/DefaultActiveRulesTest.java
  6. 24
    4
      sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java
  7. 26
    3
      sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilter.java
  8. 2
    2
      sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/AbstractPatternInitializer.java
  9. 9
    0
      sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java
  10. 9
    8
      sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java
  11. 51
    39
      sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilterTest.java
  12. 43
    5
      sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilterTest.java
  13. 5
    0
      sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/IssuePatternTest.java
  14. 3
    0
      sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java
  15. 4
    2
      sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/ChecksMediumTest.java
  16. 5
    2
      sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java
  17. 7
    14
      sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java

+ 1
- 2
sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilder.java View File

@@ -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());
}
}

+ 9
- 0
sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRule.java View File

@@ -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;
}
}

+ 16
- 0
sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRules.java View File

@@ -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())

+ 10
- 0
sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java View File

@@ -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);
}

+ 63
- 0
sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/rule/internal/DefaultActiveRulesTest.java View File

@@ -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();
}
}

+ 24
- 4
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java View File

@@ -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);

+ 26
- 3
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilter.java View File

@@ -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;
}
}

+ 2
- 2
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/AbstractPatternInitializer.java View File

@@ -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);
}

+ 9
- 0
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java View File

@@ -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();
}

}

+ 9
- 8
sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java View File

@@ -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()) {

+ 51
- 39
sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilterTest.java View File

@@ -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);
}
}

+ 43
- 5
sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilterTest.java View File

@@ -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);

+ 5
- 0
sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/IssuePatternTest.java View File

@@ -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}");
}

}

+ 3
- 0
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java View File

@@ -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;

+ 4
- 2
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/ChecksMediumTest.java View File

@@ -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);

+ 5
- 2
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java View File

@@ -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);
}

+ 7
- 14
sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java View File

@@ -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);
}


Loading…
Cancel
Save