diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2018-12-14 19:35:50 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2019-01-16 09:43:10 +0100 |
commit | bbff7949d06677e808d7c6f2cf4fd7cfc1f1a079 (patch) | |
tree | 2333a2f05764d9e00833fda00c142d776b2d1387 /sonar-scanner-engine/src | |
parent | 4db8bd9ecf0584443a2e5a9c369970e6ee85f48f (diff) | |
download | sonarqube-bbff7949d06677e808d7c6f2cf4fd7cfc1f1a079.tar.gz sonarqube-bbff7949d06677e808d7c6f2cf4fd7cfc1f1a079.zip |
SONAR-11587 Rework issue exclusions
Diffstat (limited to 'sonar-scanner-engine/src')
31 files changed, 589 insertions, 737 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java index eaf4e0f1edb..bb92a02f31c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java @@ -23,6 +23,7 @@ import java.util.Date; import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.TextRange; import org.sonar.api.batch.fs.internal.DefaultInputProject; import org.sonar.api.batch.fs.internal.DefaultTextPointer; @@ -35,20 +36,20 @@ import org.sonar.scanner.protocol.output.ScannerReport.Issue; @ThreadSafe public class DefaultFilterableIssue implements FilterableIssue { private final Issue rawIssue; + private final InputComponent component; private final ProjectAnalysisInfo projectAnalysisInfo; - private final String componentKey; private DefaultInputProject project; - public DefaultFilterableIssue(DefaultInputProject project, ProjectAnalysisInfo projectAnalysisInfo, Issue rawIssue, String componentKey) { + public DefaultFilterableIssue(DefaultInputProject project, ProjectAnalysisInfo projectAnalysisInfo, Issue rawIssue, InputComponent component) { this.project = project; this.projectAnalysisInfo = projectAnalysisInfo; this.rawIssue = rawIssue; - this.componentKey = componentKey; + this.component = component; } @Override public String componentKey() { - return componentKey; + return component.key(); } @Override @@ -98,6 +99,10 @@ public class DefaultFilterableIssue implements FilterableIssue { return project.key(); } + public InputComponent getComponent() { + return component; + } + @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java index 92a8f758249..b4ade123839 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java @@ -19,6 +19,7 @@ */ package org.sonar.scanner.issue; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.internal.DefaultInputProject; import org.sonar.api.scan.issue.filter.FilterableIssue; import org.sonar.api.scan.issue.filter.IssueFilter; @@ -45,8 +46,8 @@ public class IssueFilters { this(project, projectAnalysisInfo, new IssueFilter[0]); } - public boolean accept(String componentKey, ScannerReport.Issue rawIssue) { - FilterableIssue fIssue = new DefaultFilterableIssue(project, projectAnalysisInfo, rawIssue, componentKey); + public boolean accept(InputComponent component, ScannerReport.Issue rawIssue) { + FilterableIssue fIssue = new DefaultFilterableIssue(project, projectAnalysisInfo, rawIssue, component); return filterChain.accept(fIssue); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java index ca0a8cc0764..f8a652553ea 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java @@ -70,7 +70,7 @@ public class IssuePublisher { ScannerReport.Issue rawIssue = createReportIssue(issue, inputComponent.scannerId(), activeRule.severity()); - if (filters.accept(inputComponent.key(), rawIssue)) { + if (filters.accept(inputComponent, rawIssue)) { write(inputComponent.scannerId(), rawIssue); return true; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java index 3f5b74f2390..0cf870ea06b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java @@ -22,31 +22,31 @@ package org.sonar.scanner.issue.ignore; import java.util.ArrayList; import java.util.Collections; import java.util.List; - -import javax.annotation.CheckForNull; import javax.annotation.concurrent.ThreadSafe; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.InputComponent; -import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.notifications.AnalysisWarnings; 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.scanner.issue.DefaultFilterableIssue; import org.sonar.scanner.issue.ignore.pattern.IssueInclusionPatternInitializer; import org.sonar.scanner.issue.ignore.pattern.IssuePattern; -import org.sonar.scanner.scan.filesystem.InputComponentStore; @ThreadSafe public class EnforceIssuesFilter implements IssueFilter { - private static final Logger LOG = LoggerFactory.getLogger(EnforceIssuesFilter.class); + private static final Logger LOG = Loggers.get(EnforceIssuesFilter.class); private final List<IssuePattern> multicriteriaPatterns; - private final InputComponentStore componentStore; + private final AnalysisWarnings analysisWarnings; + + private boolean warnDeprecatedIssuePatternAlreadyLogged; - public EnforceIssuesFilter(IssueInclusionPatternInitializer patternInitializer, InputComponentStore componentStore) { + public EnforceIssuesFilter(IssueInclusionPatternInitializer patternInitializer, AnalysisWarnings analysisWarnings) { this.multicriteriaPatterns = Collections.unmodifiableList(new ArrayList<>(patternInitializer.getMulticriteriaPatterns())); - this.componentStore = componentStore; + this.analysisWarnings = analysisWarnings; } @Override @@ -56,12 +56,22 @@ public class EnforceIssuesFilter implements IssueFilter { IssuePattern matchingPattern = null; for (IssuePattern pattern : multicriteriaPatterns) { - if (pattern.getRulePattern().match(issue.ruleKey().toString())) { + if (pattern.matchRule(issue.ruleKey())) { atLeastOneRuleMatched = true; - String relativePath = getRelativePath(issue.componentKey()); - if (relativePath != null && pattern.getResourcePattern().match(relativePath)) { - atLeastOnePatternFullyMatched = true; - matchingPattern = pattern; + InputComponent component = ((DefaultFilterableIssue) issue).getComponent(); + if (component.isFile()) { + DefaultInputFile file = (DefaultInputFile) component; + if (pattern.matchFile(file.getProjectRelativePath())) { + atLeastOnePatternFullyMatched = true; + matchingPattern = pattern; + } else if (pattern.matchFile(file.getModuleRelativePath())) { + warnOnceDeprecatedIssuePattern( + "Specifying module-relative paths at project level in property '" + IssueInclusionPatternInitializer.CONFIG_KEY + "' is deprecated. " + + "To continue matching files like '" + file.getProjectRelativePath() + "', update this property so that patterns refer to project-relative paths."); + + atLeastOnePatternFullyMatched = true; + matchingPattern = pattern; + } } } } @@ -76,13 +86,12 @@ public class EnforceIssuesFilter implements IssueFilter { } } - @CheckForNull - private String getRelativePath(String componentKey) { - InputComponent component = componentStore.getByKey(componentKey); - if (component == null || !component.isFile()) { - return null; + private void warnOnceDeprecatedIssuePattern(String msg) { + if (!warnDeprecatedIssuePatternAlreadyLogged) { + LOG.warn(msg); + analysisWarnings.addUnique(msg); + warnDeprecatedIssuePatternAlreadyLogged = true; } - InputFile inputPath = (InputFile) component; - return inputPath.relativePath(); } + } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilter.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilter.java index 723d7eecc4d..666b1732dea 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilter.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilter.java @@ -19,42 +19,55 @@ */ package org.sonar.scanner.issue.ignore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.scan.issue.filter.FilterableIssue; import org.sonar.api.scan.issue.filter.IssueFilter; import org.sonar.api.scan.issue.filter.IssueFilterChain; -import org.sonar.scanner.issue.ignore.pattern.IssuePattern; -import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; +import org.sonar.api.utils.WildcardPattern; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.scanner.issue.DefaultFilterableIssue; public class IgnoreIssuesFilter implements IssueFilter { - private PatternMatcher patternMatcher; + private Multimap<InputComponent, WildcardPattern> rulePatternByComponent = LinkedHashMultimap.create(); - private static final Logger LOG = LoggerFactory.getLogger(IgnoreIssuesFilter.class); - - public IgnoreIssuesFilter(PatternMatcher patternMatcher) { - this.patternMatcher = patternMatcher; - } + private static final Logger LOG = Loggers.get(IgnoreIssuesFilter.class); @Override public boolean accept(FilterableIssue issue, IssueFilterChain chain) { - if (hasMatchFor(issue)) { + InputComponent component = ((DefaultFilterableIssue) issue).getComponent(); + if (component.isFile() && ((DefaultInputFile) component).isIgnoreAllIssues()) { + return false; + } + if (component.isFile() && ((DefaultInputFile) component).isIgnoreAllIssuesOnLine(issue.line())) { + return false; + } + if (hasRuleMatchFor(component, issue)) { return false; } return chain.accept(issue); } - private boolean hasMatchFor(FilterableIssue issue) { - IssuePattern pattern = patternMatcher.getMatchingPattern(issue.componentKey(), issue.ruleKey(), issue.line()); - if (pattern != null) { - logExclusion(issue, pattern); - return true; + public void addRuleExclusionPatternForComponent(DefaultInputFile inputFile, WildcardPattern rulePattern) { + if ("*".equals(rulePattern.toString())) { + inputFile.setIgnoreAllIssues(true); + } else { + rulePatternByComponent.put(inputFile, rulePattern); } - return false; } - private static void logExclusion(FilterableIssue issue, IssuePattern pattern) { - LOG.debug("Issue {} ignored by exclusion pattern {}", issue, pattern); + private boolean hasRuleMatchFor(InputComponent component, FilterableIssue issue) { + for (WildcardPattern pattern : rulePatternByComponent.get(component)) { + if (pattern.match(issue.ruleKey().toString())) { + LOG.debug("Issue {} ignored by exclusion pattern {}", issue, pattern); + return true; + } + } + return false; + } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/AbstractPatternInitializer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/AbstractPatternInitializer.java index 51058404811..4b85daf89e4 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/AbstractPatternInitializer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/AbstractPatternInitializer.java @@ -22,9 +22,9 @@ package org.sonar.scanner.issue.ignore.pattern; import com.google.common.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.List; -import java.util.Set; import org.apache.commons.lang.StringUtils; import org.sonar.api.config.Configuration; +import org.sonar.api.utils.MessageException; import static com.google.common.base.MoreObjects.firstNonNull; @@ -59,13 +59,15 @@ public abstract class AbstractPatternInitializer { multicriteriaPatterns = new ArrayList<>(); for (String id : settings.getStringArray(getMulticriteriaConfigurationKey())) { String propPrefix = getMulticriteriaConfigurationKey() + "." + id + "."; - String resourceKeyPattern = settings.get(propPrefix + "resourceKey").orElse(null); + String filePathPattern = settings.get(propPrefix + "resourceKey").orElse(null); + if (StringUtils.isBlank(filePathPattern)) { + throw MessageException.of("Issue exclusions are misconfigured. File pattern is mandatory for each entry of '" + getMulticriteriaConfigurationKey() + "'"); + } String ruleKeyPattern = settings.get(propPrefix + "ruleKey").orElse(null); - String lineRange = "*"; - String[] fields = new String[] {resourceKeyPattern, ruleKeyPattern, lineRange}; - PatternDecoder.checkRegularLineConstraints(StringUtils.join(fields, ","), fields); - Set<LineRange> rangeOfLines = PatternDecoder.decodeRangeOfLines(firstNonNull(lineRange, "*")); - IssuePattern pattern = new IssuePattern(firstNonNull(resourceKeyPattern, "*"), firstNonNull(ruleKeyPattern, "*"), rangeOfLines); + 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(firstNonNull(filePathPattern, "*"), firstNonNull(ruleKeyPattern, "*")); multicriteriaPatterns.add(pattern); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssueExclusionPatternInitializer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssueExclusionPatternInitializer.java index 01e0ab76b06..16c3163cbf7 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssueExclusionPatternInitializer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssueExclusionPatternInitializer.java @@ -24,12 +24,14 @@ import java.util.Collections; import java.util.List; import org.apache.commons.lang.StringUtils; import org.sonar.api.config.Configuration; +import org.sonar.api.utils.MessageException; import org.sonar.core.config.IssueExclusionProperties; import static com.google.common.base.Strings.nullToEmpty; public class IssueExclusionPatternInitializer extends AbstractPatternInitializer { + public static final String CONFIG_KEY = IssueExclusionProperties.EXCLUSION_KEY_PREFIX + ".multicriteria"; private List<BlockIssuePattern> blockPatterns; private List<String> allFilePatterns; @@ -40,7 +42,7 @@ public class IssueExclusionPatternInitializer extends AbstractPatternInitializer @Override protected String getMulticriteriaConfigurationKey() { - return IssueExclusionProperties.EXCLUSION_KEY_PREFIX + ".multicriteria"; + return CONFIG_KEY; } @Override @@ -54,9 +56,11 @@ public class IssueExclusionPatternInitializer extends AbstractPatternInitializer for (String id : getSettings().getStringArray(IssueExclusionProperties.PATTERNS_BLOCK_KEY)) { String propPrefix = IssueExclusionProperties.PATTERNS_BLOCK_KEY + "." + id + "."; String beginBlockRegexp = getSettings().get(propPrefix + IssueExclusionProperties.BEGIN_BLOCK_REGEXP).orElse(null); + if (StringUtils.isBlank(beginBlockRegexp)) { + throw MessageException.of("Issue exclusions are misconfigured. Start block regexp is mandatory for each entry of '" + IssueExclusionProperties.PATTERNS_BLOCK_KEY + "'"); + } String endBlockRegexp = getSettings().get(propPrefix + IssueExclusionProperties.END_BLOCK_REGEXP).orElse(null); - String[] fields = new String[] {beginBlockRegexp, endBlockRegexp}; - PatternDecoder.checkDoubleRegexpLineConstraints(StringUtils.join(fields, ","), fields); + // As per configuration help, missing second field means: from start regexp to EOF BlockIssuePattern pattern = new BlockIssuePattern(nullToEmpty(beginBlockRegexp), nullToEmpty(endBlockRegexp)); blockPatterns.add(pattern); } @@ -67,7 +71,9 @@ public class IssueExclusionPatternInitializer extends AbstractPatternInitializer for (String id : getSettings().getStringArray(IssueExclusionProperties.PATTERNS_ALLFILE_KEY)) { String propPrefix = IssueExclusionProperties.PATTERNS_ALLFILE_KEY + "." + id + "."; String allFileRegexp = getSettings().get(propPrefix + IssueExclusionProperties.FILE_REGEXP).orElse(null); - PatternDecoder.checkWholeFileRegexp(allFileRegexp); + if (StringUtils.isBlank(allFileRegexp)) { + throw MessageException.of("Issue exclusions are misconfigured. Remove blank entries from '" + IssueExclusionProperties.PATTERNS_ALLFILE_KEY + "'"); + } allFilePatterns.add(nullToEmpty(allFileRegexp)); } allFilePatterns = Collections.unmodifiableList(allFilePatterns); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssueInclusionPatternInitializer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssueInclusionPatternInitializer.java index 0fa72da96db..30987808e9d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssueInclusionPatternInitializer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssueInclusionPatternInitializer.java @@ -24,12 +24,14 @@ import org.sonar.core.config.IssueExclusionProperties; public class IssueInclusionPatternInitializer extends AbstractPatternInitializer { + public static final String CONFIG_KEY = IssueExclusionProperties.INCLUSION_KEY_PREFIX + ".multicriteria"; + public IssueInclusionPatternInitializer(Configuration settings) { super(settings); } @Override protected String getMulticriteriaConfigurationKey() { - return IssueExclusionProperties.INCLUSION_KEY_PREFIX + ".multicriteria"; + return CONFIG_KEY; } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java index 4e76159ab72..9be0fd7c75e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java @@ -19,108 +19,32 @@ */ package org.sonar.scanner.issue.ignore.pattern; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -import org.apache.commons.lang.builder.ToStringBuilder; -import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.WildcardPattern; @Immutable public class IssuePattern { - private final WildcardPattern resourcePattern; + private final WildcardPattern filePattern; private final WildcardPattern rulePattern; - private final Set<Integer> lines; - private final Set<LineRange> lineRanges; - private final boolean checkLines; - public IssuePattern(String resourcePattern, String rulePattern) { - this(resourcePattern, rulePattern, Collections.emptySet()); - } - - public IssuePattern(String resourcePattern, String rulePattern, Set<LineRange> lineRanges) { - this.resourcePattern = WildcardPattern.create(resourcePattern); + public IssuePattern(String filePattern, String rulePattern) { + this.filePattern = WildcardPattern.create(filePattern); this.rulePattern = WildcardPattern.create(rulePattern); - this.checkLines = !lineRanges.isEmpty(); - Set<Integer> modifiableLines = new LinkedHashSet<>(); - Set<LineRange> modifiableLineRanges = new LinkedHashSet<>(); - - for (LineRange range : lineRanges) { - if (range.from() == range.to()) { - modifiableLines.add(range.from()); - } else { - modifiableLineRanges.add(range); - } - } - - this.lines = Collections.unmodifiableSet(modifiableLines); - this.lineRanges = Collections.unmodifiableSet(modifiableLineRanges); - } - - public WildcardPattern getResourcePattern() { - return resourcePattern; } public WildcardPattern getRulePattern() { return rulePattern; } - boolean isCheckLines() { - return checkLines; - } - - Set<Integer> getAllLines() { - Set<Integer> allLines = new LinkedHashSet<>(lines); - for (LineRange lineRange : lineRanges) { - allLines.addAll(lineRange.toLines()); - } - return allLines; + public boolean matchRule(RuleKey rule) { + return rulePattern.match(rule.toString()); } - public boolean match(@Nullable String componentKey, RuleKey ruleKey, @Nullable Integer line) { - if (checkLines) { - if (line == null) { - return false; - } else { - return matchResource(componentKey) && matchRule(ruleKey) && matchLine(line); - } - } - return matchResource(componentKey) && matchRule(ruleKey); + public boolean matchFile(@Nullable String filePath) { + return filePath != null && filePattern.match(filePath); } - boolean matchLine(int lineId) { - if (lines.contains(lineId)) { - return true; - } - - for (LineRange range : lineRanges) { - if (range.in(lineId)) { - return true; - } - } - - return false; - } - - boolean matchRule(RuleKey rule) { - String key = new StringBuilder().append(rule.repository()).append(':').append(rule.rule()).toString(); - return rulePattern.match(key); - } - - public boolean matchResource(@Nullable String resource) { - return resource != null && resourcePattern.match(resource); - } - - public IssuePattern forResource(String resource) { - return new IssuePattern(resource, rulePattern.toString(), lineRanges); - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/PatternDecoder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/PatternDecoder.java deleted file mode 100644 index bb301e3f57b..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/PatternDecoder.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.issue.ignore.pattern; - -import com.google.common.annotations.VisibleForTesting; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import org.apache.commons.lang.StringUtils; - -public class PatternDecoder { - private static final String LINE_RANGE_REGEXP = "\\[((\\d+|\\d+-\\d+),?)*\\]"; - private static final String CONFIG_FORMAT_ERROR_PREFIX = "Exclusions > Issues : Invalid format. "; - - private PatternDecoder() { - // static only - } - - static void checkRegularLineConstraints(String line, String[] fields) { - if (!isResource(fields[0])) { - throw new IllegalStateException(CONFIG_FORMAT_ERROR_PREFIX + "The first field does not define a resource pattern: " + line); - } - if (!isRule(fields[1])) { - throw new IllegalStateException(CONFIG_FORMAT_ERROR_PREFIX + "The second field does not define a rule pattern: " + line); - } - if (!isLinesRange(fields[2])) { - throw new IllegalStateException(CONFIG_FORMAT_ERROR_PREFIX + "The third field does not define a range of lines: " + line); - } - } - - static void checkDoubleRegexpLineConstraints(String line, String[] fields) { - if (!isRegexp(fields[0])) { - throw new IllegalStateException(CONFIG_FORMAT_ERROR_PREFIX + "The first field does not define a regular expression: " + line); - } - // As per configuration help, missing second field means: from start regexp to EOF - } - - static void checkWholeFileRegexp(String regexp) { - if (!isRegexp(regexp)) { - throw new IllegalStateException(CONFIG_FORMAT_ERROR_PREFIX + "The field does not define a regular expression: " + regexp); - } - } - - public static Set<LineRange> decodeRangeOfLines(String field) { - if (StringUtils.equals(field, "*")) { - return Collections.emptySet(); - } else { - Set<LineRange> lineRanges = new HashSet<>(); - - String s = StringUtils.substringBetween(StringUtils.trim(field), "[", "]"); - String[] parts = StringUtils.split(s, ','); - for (String part : parts) { - if (StringUtils.contains(part, '-')) { - String[] range = StringUtils.split(part, '-'); - lineRanges.add(new LineRange(Integer.valueOf(range[0]), Integer.valueOf(range[1]))); - } else { - lineRanges.add(new LineRange(Integer.valueOf(part), Integer.valueOf(part))); - } - } - return lineRanges; - } - } - - @VisibleForTesting - static boolean isLinesRange(String field) { - return StringUtils.equals(field, "*") || java.util.regex.Pattern.matches(LINE_RANGE_REGEXP, field); - } - - @VisibleForTesting - static boolean isResource(String field) { - return StringUtils.isNotBlank(field); - } - - @VisibleForTesting - static boolean isRule(String field) { - return StringUtils.isNotBlank(field); - } - - @VisibleForTesting - static boolean isRegexp(String field) { - return StringUtils.isNotBlank(field); - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/PatternMatcher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/PatternMatcher.java deleted file mode 100644 index a9be53e5aa0..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/PatternMatcher.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.issue.ignore.pattern; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; -import java.util.Collection; -import java.util.Set; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.sonar.api.rule.RuleKey; - -public class PatternMatcher { - - private Multimap<String, IssuePattern> excludePatternByComponent = LinkedHashMultimap.create(); - - @CheckForNull - public IssuePattern getMatchingPattern(String componentKey, RuleKey ruleKey, @Nullable Integer line) { - for (IssuePattern pattern : getPatternsForComponent(componentKey)) { - if (pattern.match(componentKey, ruleKey, line)) { - return pattern; - } - } - return null; - } - - @VisibleForTesting - public Collection<IssuePattern> getPatternsForComponent(String componentKey) { - return excludePatternByComponent.get(componentKey); - } - - public void addPatternForComponent(String componentKey, IssuePattern pattern) { - excludePatternByComponent.put(componentKey, pattern.forResource(componentKey)); - } - - public void addPatternToExcludeResource(String componentKey) { - addPatternForComponent(componentKey, new IssuePattern(componentKey, "*")); - } - - public void addPatternToExcludeLines(String componentKey, Set<LineRange> lineRanges) { - addPatternForComponent(componentKey, new IssuePattern(componentKey, "*", lineRanges)); - } - -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java index 7a3591af10c..6e639385b09 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java @@ -23,22 +23,31 @@ import java.util.ArrayList; import java.util.List; import javax.annotation.CheckForNull; import org.apache.commons.lang.StringUtils; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.charhandler.CharHandler; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.scanner.issue.ignore.IgnoreIssuesFilter; import org.sonar.scanner.issue.ignore.pattern.BlockIssuePattern; import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer; import org.sonar.scanner.issue.ignore.pattern.IssuePattern; -import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; public final class IssueExclusionsLoader { + private static final Logger LOG = Loggers.get(IssueExclusionsLoader.class); + private final List<java.util.regex.Pattern> allFilePatterns; private final List<DoubleRegexpMatcher> blockMatchers; - private final PatternMatcher patternMatcher; + private final IgnoreIssuesFilter ignoreIssuesFilter; + private final AnalysisWarnings analysisWarnings; private final IssueExclusionPatternInitializer patternsInitializer; private final boolean enableCharHandler; + private boolean warnDeprecatedIssuePatternAlreadyLogged; - public IssueExclusionsLoader(IssueExclusionPatternInitializer patternsInitializer, PatternMatcher patternMatcher) { + public IssueExclusionsLoader(IssueExclusionPatternInitializer patternsInitializer, IgnoreIssuesFilter ignoreIssuesFilter, AnalysisWarnings analysisWarnings) { this.patternsInitializer = patternsInitializer; - this.patternMatcher = patternMatcher; + this.ignoreIssuesFilter = ignoreIssuesFilter; + this.analysisWarnings = analysisWarnings; this.allFilePatterns = new ArrayList<>(); this.blockMatchers = new ArrayList<>(); @@ -53,22 +62,31 @@ public final class IssueExclusionsLoader { enableCharHandler = !allFilePatterns.isEmpty() || !blockMatchers.isEmpty(); } - public boolean shouldExecute() { - return patternsInitializer.hasMulticriteriaPatterns(); - } - - public void addMulticriteriaPatterns(String relativePath, String componentKey) { + public void addMulticriteriaPatterns(DefaultInputFile inputFile) { for (IssuePattern pattern : patternsInitializer.getMulticriteriaPatterns()) { - if (pattern.matchResource(relativePath)) { - patternMatcher.addPatternForComponent(componentKey, pattern); + if (pattern.matchFile(inputFile.getProjectRelativePath())) { + ignoreIssuesFilter.addRuleExclusionPatternForComponent(inputFile, pattern.getRulePattern()); + } else if (pattern.matchFile(inputFile.getModuleRelativePath())) { + warnOnceDeprecatedIssuePattern( + "Specifying module-relative paths at project level in property '" + IssueExclusionPatternInitializer.CONFIG_KEY + "' is deprecated. " + + "To continue matching files like '" + inputFile.getProjectRelativePath() + "', update this property so that patterns refer to project-relative paths."); + ignoreIssuesFilter.addRuleExclusionPatternForComponent(inputFile, pattern.getRulePattern()); } } } + private void warnOnceDeprecatedIssuePattern(String msg) { + if (!warnDeprecatedIssuePatternAlreadyLogged) { + LOG.warn(msg); + analysisWarnings.addUnique(msg); + warnDeprecatedIssuePatternAlreadyLogged = true; + } + } + @CheckForNull - public CharHandler createCharHandlerFor(String componentKey) { + public CharHandler createCharHandlerFor(DefaultInputFile inputFile) { if (enableCharHandler) { - return new IssueExclusionsRegexpScanner(componentKey, allFilePatterns, blockMatchers, patternMatcher); + return new IssueExclusionsRegexpScanner(inputFile, allFilePatterns, blockMatchers); } return null; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java index 5daf24c6075..a08ba55a593 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java @@ -24,12 +24,12 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import java.util.stream.Collectors; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.charhandler.CharHandler; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.scanner.issue.ignore.pattern.LineRange; -import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader.DoubleRegexpMatcher; public class IssueExclusionsRegexpScanner extends CharHandler { @@ -38,8 +38,7 @@ public class IssueExclusionsRegexpScanner extends CharHandler { private final StringBuilder sb = new StringBuilder(); private final List<Pattern> allFilePatterns; private final List<DoubleRegexpMatcher> blockMatchers; - private final String componentKey; - private final PatternMatcher patternMatcher; + private final DefaultInputFile inputFile; private int lineIndex = 1; private List<LineExclusion> lineExclusions = new ArrayList<>(); @@ -47,22 +46,26 @@ public class IssueExclusionsRegexpScanner extends CharHandler { private int fileLength = 0; private DoubleRegexpMatcher currentMatcher; - IssueExclusionsRegexpScanner(String componentKey, List<Pattern> allFilePatterns, List<DoubleRegexpMatcher> blockMatchers, PatternMatcher patternMatcher) { + IssueExclusionsRegexpScanner(DefaultInputFile inputFile, List<Pattern> allFilePatterns, List<DoubleRegexpMatcher> blockMatchers) { this.allFilePatterns = allFilePatterns; this.blockMatchers = blockMatchers; - this.patternMatcher = patternMatcher; - this.componentKey = componentKey; - String relativePath = StringUtils.substringAfterLast(componentKey, ":"); - LOG.info("'{}' generating issue exclusions", relativePath); + this.inputFile = inputFile; + LOG.debug("Evaluate issue exclusions for '{}'", inputFile.getProjectRelativePath()); } @Override public void handleIgnoreEoL(char c) { + if (inputFile.isIgnoreAllIssues()) { + return; + } sb.append(c); } @Override public void newLine() { + if (inputFile.isIgnoreAllIssues()) { + return; + } processLine(sb.toString()); sb.setLength(0); lineIndex++; @@ -70,6 +73,9 @@ public class IssueExclusionsRegexpScanner extends CharHandler { @Override public void eof() { + if (inputFile.isIgnoreAllIssues()) { + return; + } processLine(sb.toString()); if (currentMatcher != null && !currentMatcher.hasSecondPattern()) { @@ -81,8 +87,8 @@ public class IssueExclusionsRegexpScanner extends CharHandler { fileLength = lineIndex; if (!lineExclusions.isEmpty()) { Set<LineRange> lineRanges = convertLineExclusionsToLineRanges(); - LOG.debug("- Line exclusions found: {}", lineRanges); - patternMatcher.addPatternToExcludeLines(componentKey, lineRanges); + LOG.debug(" - Line exclusions found: {}", lineRanges.stream().map(LineRange::toString).collect(Collectors.joining(","))); + inputFile.addIgnoreIssuesOnLineRanges(lineRanges.stream().map(r -> new int[] {r.from(), r.to()}).collect(Collectors.toList())); } } @@ -94,9 +100,9 @@ public class IssueExclusionsRegexpScanner extends CharHandler { // first check the single regexp patterns that can be used to totally exclude a file for (Pattern pattern : allFilePatterns) { if (pattern.matcher(line).find()) { - patternMatcher.addPatternToExcludeResource(componentKey); // nothing more to do on this file - LOG.debug("- Exclusion pattern '{}': every issue in this file will be ignored.", pattern); + LOG.debug(" - Exclusion pattern '{}': all issues in this file will be ignored.", pattern); + inputFile.setIgnoreAllIssues(true); return; } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorBuilder.java index 38d0d60733f..7dceaf350a0 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorBuilder.java @@ -20,17 +20,18 @@ package org.sonar.scanner.scan; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Lists; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.text.MessageFormat; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.stream.Stream; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.apache.commons.lang.ArrayUtils; @@ -38,16 +39,21 @@ import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.notifications.AnalysisWarnings; import org.sonar.api.utils.MessageException; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; +import org.sonar.core.config.IssueExclusionProperties; import org.sonar.scanner.bootstrap.ScannerProperties; +import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer; +import org.sonar.scanner.issue.ignore.pattern.IssueInclusionPatternInitializer; import org.sonar.scanner.util.ScannerUtils; +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toList; import static org.sonar.api.config.internal.MultivalueProperty.parseAsCsv; - /** * Class that creates a project definition based on a set of properties. */ @@ -95,17 +101,23 @@ public class ProjectReactorBuilder { */ private static final String[] MANDATORY_PROPERTIES_FOR_CHILD = {MODULE_KEY_PROPERTY}; + private static final Collection<String> UNSUPPORTED_PROPS_FOR_MODULES = asList(IssueExclusionPatternInitializer.CONFIG_KEY, IssueInclusionPatternInitializer.CONFIG_KEY, + IssueExclusionProperties.PATTERNS_BLOCK_KEY, IssueExclusionProperties.PATTERNS_ALLFILE_KEY); + /** * Properties that must not be passed from the parent project to its children. */ - private static final List<String> NON_HERITED_PROPERTIES_FOR_CHILD = Lists.newArrayList(PROPERTY_PROJECT_BASEDIR, CoreProperties.WORKING_DIRECTORY, PROPERTY_MODULES, - CoreProperties.PROJECT_DESCRIPTION_PROPERTY); + private static final List<String> NON_HERITED_PROPERTIES_FOR_CHILD = Stream.concat(Stream.of(PROPERTY_PROJECT_BASEDIR, CoreProperties.WORKING_DIRECTORY, PROPERTY_MODULES, + CoreProperties.PROJECT_DESCRIPTION_PROPERTY), UNSUPPORTED_PROPS_FOR_MODULES.stream()).collect(toList()); private final ScannerProperties scannerProps; + private final AnalysisWarnings analysisWarnings; private File rootProjectWorkDir; + private boolean warnExclusionsAlreadyLogged; - public ProjectReactorBuilder(ScannerProperties props) { + public ProjectReactorBuilder(ScannerProperties props, AnalysisWarnings analysisWarnings) { this.scannerProps = props; + this.analysisWarnings = analysisWarnings; } public ProjectReactor execute() { @@ -121,7 +133,7 @@ public class ProjectReactorBuilder { } private static void extractPropertiesByModule(Map<String, Map<String, String>> propertiesByModuleIdPath, String currentModuleId, String currentModuleIdPath, - Map<String, String> parentProperties) { + Map<String, String> parentProperties) { if (propertiesByModuleIdPath.containsKey(currentModuleIdPath)) { throw MessageException.of(String.format("Two modules have the same id: '%s'. Each module must have a unique id.", currentModuleId)); } @@ -170,6 +182,7 @@ public class ProjectReactorBuilder { workDir = initRootProjectWorkDir(baseDir, moduleProperties); } else { workDir = initModuleWorkDir(baseDir, moduleProperties); + checkUnsupportedIssueExclusions(moduleProperties); } return ProjectDefinition.create().setProperties(moduleProperties) @@ -178,6 +191,23 @@ public class ProjectReactorBuilder { .setBuildDir(initModuleBuildDir(baseDir, moduleProperties)); } + private void checkUnsupportedIssueExclusions(Map<String, String> moduleProperties) { + UNSUPPORTED_PROPS_FOR_MODULES.stream().forEach(p -> { + if (moduleProperties.containsKey(p)) { + warnOnceUnsupportedIssueExclusions( + "Specifying issue exclusions at module level is not supported anymore. Configure the property '" + p + "' and any other issue exclusions at project level."); + } + }); + } + + private void warnOnceUnsupportedIssueExclusions(String msg) { + if (!warnExclusionsAlreadyLogged) { + LOG.warn(msg); + analysisWarnings.addUnique(msg); + warnExclusionsAlreadyLogged = true; + } + } + @VisibleForTesting protected File initRootProjectWorkDir(File baseDir, Map<String, String> rootProperties) { String workDir = rootProperties.get(CoreProperties.WORKING_DIRECTORY); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java index b3d656a55c3..6171c2793de 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java @@ -47,7 +47,6 @@ import org.sonar.scanner.bootstrap.MetricProvider; import org.sonar.scanner.bootstrap.PostJobExtensionDictionnary; import org.sonar.scanner.cpd.CpdExecutor; import org.sonar.scanner.cpd.CpdSettings; -import org.sonar.scanner.cpd.JavaCpdBlockIndexerSensor; import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; import org.sonar.scanner.deprecated.test.TestPlanBuilder; import org.sonar.scanner.deprecated.test.TestableBuilder; @@ -59,7 +58,6 @@ import org.sonar.scanner.issue.ignore.EnforceIssuesFilter; import org.sonar.scanner.issue.ignore.IgnoreIssuesFilter; import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer; import org.sonar.scanner.issue.ignore.pattern.IssueInclusionPatternInitializer; -import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; import org.sonar.scanner.issue.tracking.DefaultServerLineHashesLoader; import org.sonar.scanner.issue.tracking.IssueTransition; @@ -230,7 +228,6 @@ public class ProjectScanContainer extends ComponentContainer { // issue exclusions IssueInclusionPatternInitializer.class, IssueExclusionPatternInitializer.class, - PatternMatcher.class, IssueExclusionsLoader.class, EnforceIssuesFilter.class, IgnoreIssuesFilter.class, diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java index cd9767328a2..aba73c172a4 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java @@ -134,9 +134,7 @@ public class FileIndexer { } checkIfAlreadyIndexed(inputFile); componentStore.put(module.key(), inputFile); - if (issueExclusionsLoader.shouldExecute()) { - issueExclusionsLoader.addMulticriteriaPatterns(inputFile.getProjectRelativePath(), inputFile.key()); - } + issueExclusionsLoader.addMulticriteriaPatterns(inputFile); LOG.debug("'{}' indexed {}with language '{}'", projectRelativePath, type == Type.TEST ? "as test " : "", inputFile.language()); evaluateCoverageExclusions(moduleCoverageExclusions, inputFile); if (properties.preloadFileMetadata()) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/MetadataGenerator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/MetadataGenerator.java index a603edbe6de..cf7833e18fc 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/MetadataGenerator.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/MetadataGenerator.java @@ -64,7 +64,7 @@ public class MetadataGenerator { } InputStream is = charsetDetector.inputStream(); inputFile.setCharset(charset); - Metadata metadata = fileMetadata.readMetadata(is, charset, inputFile.absolutePath(), exclusionsScanner.createCharHandlerFor(inputFile.key())); + Metadata metadata = fileMetadata.readMetadata(is, charset, inputFile.absolutePath(), exclusionsScanner.createCharHandlerFor(inputFile)); inputFile.setMetadata(metadata); inputFile.setStatus(statusDetection.status(moduleKeyWithBranch, inputFile, metadata.hash())); LOG.debug("'{}' generated metadata{} with charset '{}'", inputFile, inputFile.type() == Type.TEST ? " as test " : "", charset); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java index 9b379f021a2..240624f63c3 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/DefaultFilterableIssueTest.java @@ -22,6 +22,7 @@ package org.sonar.scanner.issue; import java.util.Date; import org.junit.Before; import org.junit.Test; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.internal.DefaultInputProject; import org.sonar.scanner.ProjectAnalysisInfo; import org.sonar.scanner.protocol.Constants.Severity; @@ -36,14 +37,15 @@ public class DefaultFilterableIssueTest { private DefaultFilterableIssue issue; private DefaultInputProject mockedProject; private ProjectAnalysisInfo projectAnalysisInfo; - private String componentKey; + private InputComponent component; private Issue rawIssue; @Before public void setUp() { mockedProject = mock(DefaultInputProject.class); projectAnalysisInfo = mock(ProjectAnalysisInfo.class); - componentKey = "component"; + component = mock(InputComponent.class); + when(component.key()).thenReturn("foo"); } private Issue createIssue() { @@ -68,12 +70,12 @@ public class DefaultFilterableIssueTest { @Test public void testRoundTrip() { rawIssue = createIssue(); - issue = new DefaultFilterableIssue(mockedProject, projectAnalysisInfo, rawIssue, componentKey); + issue = new DefaultFilterableIssue(mockedProject, projectAnalysisInfo, rawIssue, component); when(projectAnalysisInfo.analysisDate()).thenReturn(new Date(10_000)); when(mockedProject.key()).thenReturn("projectKey"); - assertThat(issue.componentKey()).isEqualTo(componentKey); + assertThat(issue.componentKey()).isEqualTo(component.key()); assertThat(issue.creationDate()).isEqualTo(new Date(10_000)); assertThat(issue.line()).isEqualTo(30); assertThat(issue.textRange().start().line()).isEqualTo(30); @@ -88,7 +90,7 @@ public class DefaultFilterableIssueTest { @Test public void nullValues() { rawIssue = createIssueWithoutFields(); - issue = new DefaultFilterableIssue(mockedProject, projectAnalysisInfo, rawIssue, componentKey); + issue = new DefaultFilterableIssue(mockedProject, projectAnalysisInfo, rawIssue, component); assertThat(issue.line()).isNull(); assertThat(issue.gap()).isNull(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java index 2c472af2ea0..3e371c728fc 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java @@ -31,6 +31,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputProject; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; @@ -48,7 +49,6 @@ import org.sonar.scanner.report.ReportPublisher; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; @@ -130,7 +130,7 @@ public class IssuePublisherTest { .forRule(SQUID_RULE_KEY) .overrideSeverity(org.sonar.api.batch.rule.Severity.CRITICAL); - when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(true); + when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true); boolean added = moduleIssues.initAndAddIssue(issue); @@ -171,7 +171,7 @@ public class IssuePublisherTest { DefaultIssue issue = new DefaultIssue(project) .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo")) .forRule(SQUID_RULE_KEY); - when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(true); + when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true); moduleIssues.initAndAddIssue(issue); ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class); @@ -193,7 +193,7 @@ public class IssuePublisherTest { .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("")) .forRule(SQUID_RULE_KEY); - when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(false); + when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(false); boolean added = moduleIssues.initAndAddIssue(issue); @@ -240,7 +240,7 @@ public class IssuePublisherTest { .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("")) .forRule(NOSONAR_RULE_KEY); - when(filters.accept(anyString(), any(ScannerReport.Issue.class))).thenReturn(true); + when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true); boolean added = moduleIssues.initAndAddIssue(issue); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilterTest.java index f5c1f1baecf..80ce4f25eb2 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilterTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilterTest.java @@ -20,17 +20,19 @@ package org.sonar.scanner.issue.ignore; import com.google.common.collect.ImmutableList; +import java.io.IOException; import org.junit.Before; +import org.junit.Rule; 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.notifications.AnalysisWarnings; import org.sonar.api.rule.RuleKey; -import org.sonar.api.scan.issue.filter.FilterableIssue; import org.sonar.api.scan.issue.filter.IssueFilterChain; -import org.sonar.api.utils.WildcardPattern; +import org.sonar.scanner.issue.DefaultFilterableIssue; import org.sonar.scanner.issue.ignore.pattern.IssueInclusionPatternInitializer; import org.sonar.scanner.issue.ignore.pattern.IssuePattern; -import org.sonar.scanner.scan.filesystem.InputComponentStore; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -40,24 +42,25 @@ import static org.mockito.Mockito.when; public class EnforceIssuesFilterTest { + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + private IssueInclusionPatternInitializer exclusionPatternInitializer; - private InputComponentStore inputComponentStore; private EnforceIssuesFilter ignoreFilter; - private FilterableIssue issue; + private DefaultFilterableIssue issue; private IssueFilterChain chain; @Before public void init() { - inputComponentStore = mock(InputComponentStore.class); exclusionPatternInitializer = mock(IssueInclusionPatternInitializer.class); - issue = mock(FilterableIssue.class); + issue = mock(DefaultFilterableIssue.class); chain = mock(IssueFilterChain.class); when(chain.accept(issue)).thenReturn(true); } @Test public void shouldPassToChainIfNoConfiguredPatterns() { - ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore); + ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, mock(AnalysisWarnings.class)); assertThat(ignoreFilter.accept(issue, chain)).isTrue(); verify(chain).accept(issue); } @@ -70,12 +73,10 @@ public class EnforceIssuesFilterTest { when(issue.ruleKey()).thenReturn(ruleKey); IssuePattern matching = mock(IssuePattern.class); - WildcardPattern rulePattern = mock(WildcardPattern.class); - when(matching.getRulePattern()).thenReturn(rulePattern); - when(rulePattern.match(rule)).thenReturn(false); + when(matching.matchRule(ruleKey)).thenReturn(false); when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); - ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore); + ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, mock(AnalysisWarnings.class)); assertThat(ignoreFilter.accept(issue, chain)).isTrue(); verify(chain).accept(issue); } @@ -84,23 +85,17 @@ public class EnforceIssuesFilterTest { public void shouldAcceptIssueIfFullyMatched() { String rule = "rule"; String path = "org/sonar/api/Issue.java"; - String componentKey = "org.sonar.api.Issue"; RuleKey ruleKey = mock(RuleKey.class); when(ruleKey.toString()).thenReturn(rule); when(issue.ruleKey()).thenReturn(ruleKey); - when(issue.componentKey()).thenReturn(componentKey); IssuePattern matching = mock(IssuePattern.class); - WildcardPattern rulePattern = mock(WildcardPattern.class); - when(matching.getRulePattern()).thenReturn(rulePattern); - when(rulePattern.match(rule)).thenReturn(true); - WildcardPattern pathPattern = mock(WildcardPattern.class); - when(matching.getResourcePattern()).thenReturn(pathPattern); - when(pathPattern.match(path)).thenReturn(true); + when(matching.matchRule(ruleKey)).thenReturn(true); + when(matching.matchFile(path)).thenReturn(true); when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); - when(inputComponentStore.getByKey(componentKey)).thenReturn(createComponentWithPath(path)); + when(issue.getComponent()).thenReturn(createComponentWithPath(path)); - ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore); + ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, mock(AnalysisWarnings.class)); assertThat(ignoreFilter.accept(issue, chain)).isTrue(); verifyZeroInteractions(chain); } @@ -120,41 +115,32 @@ public class EnforceIssuesFilterTest { when(issue.componentKey()).thenReturn(componentKey); IssuePattern matching = mock(IssuePattern.class); - WildcardPattern rulePattern = mock(WildcardPattern.class); - when(matching.getRulePattern()).thenReturn(rulePattern); - when(rulePattern.match(rule)).thenReturn(true); - WildcardPattern pathPattern = mock(WildcardPattern.class); - when(matching.getResourcePattern()).thenReturn(pathPattern); - when(pathPattern.match(path)).thenReturn(false); + when(matching.matchRule(ruleKey)).thenReturn(true); + when(matching.matchFile(path)).thenReturn(false); when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); - when(inputComponentStore.getByKey(componentKey)).thenReturn(createComponentWithPath(path)); + when(issue.getComponent()).thenReturn(createComponentWithPath(path)); - ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore); + ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, mock(AnalysisWarnings.class)); assertThat(ignoreFilter.accept(issue, chain)).isFalse(); verifyZeroInteractions(chain); } @Test - public void shouldRefuseIssueIfRuleMatchesAndPathUnknown() { + public void shouldRefuseIssueIfRuleMatchesAndNotFile() throws IOException { String rule = "rule"; String path = "org/sonar/api/Issue.java"; String componentKey = "org.sonar.api.Issue"; RuleKey ruleKey = mock(RuleKey.class); when(ruleKey.toString()).thenReturn(rule); when(issue.ruleKey()).thenReturn(ruleKey); - when(issue.componentKey()).thenReturn(componentKey); IssuePattern matching = mock(IssuePattern.class); - WildcardPattern rulePattern = mock(WildcardPattern.class); - when(matching.getRulePattern()).thenReturn(rulePattern); - when(rulePattern.match(rule)).thenReturn(true); - WildcardPattern pathPattern = mock(WildcardPattern.class); - when(matching.getResourcePattern()).thenReturn(pathPattern); - when(pathPattern.match(path)).thenReturn(false); + when(matching.matchRule(ruleKey)).thenReturn(true); + when(matching.matchFile(path)).thenReturn(true); when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching)); - when(inputComponentStore.getByKey(componentKey)).thenReturn(null); + when(issue.getComponent()).thenReturn(TestInputFileBuilder.newDefaultInputProject("foo", tempFolder.newFolder())); - ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore); + ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, mock(AnalysisWarnings.class)); assertThat(ignoreFilter.accept(issue, chain)).isFalse(); verifyZeroInteractions(chain); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilterTest.java index 5f987c98e3a..8a9df4a744a 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilterTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/IgnoreIssuesFilterTest.java @@ -19,42 +19,57 @@ */ package org.sonar.scanner.issue.ignore; +import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.rule.RuleKey; -import org.sonar.api.scan.issue.filter.FilterableIssue; import org.sonar.api.scan.issue.filter.IssueFilterChain; -import org.sonar.scanner.issue.ignore.pattern.IssuePattern; -import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; +import org.sonar.api.utils.WildcardPattern; +import org.sonar.scanner.issue.DefaultFilterableIssue; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class IgnoreIssuesFilterTest { - private PatternMatcher exclusionPatternMatcher = mock(PatternMatcher.class); - private FilterableIssue issue = mock(FilterableIssue.class, Mockito.RETURNS_DEEP_STUBS); + private DefaultFilterableIssue issue = mock(DefaultFilterableIssue.class); private IssueFilterChain chain = mock(IssueFilterChain.class); - private IgnoreIssuesFilter underTest = new IgnoreIssuesFilter(exclusionPatternMatcher); + private IgnoreIssuesFilter underTest = new IgnoreIssuesFilter(); + private DefaultInputFile component; + private RuleKey ruleKey = RuleKey.of("foo", "bar"); + + @Before + public void prepare() { + component = mock(DefaultInputFile.class); + when(issue.getComponent()).thenReturn(component); + when(issue.ruleKey()).thenReturn(ruleKey); + } @Test public void shouldPassToChainIfMatcherHasNoPatternForIssue() { - when(exclusionPatternMatcher.getMatchingPattern(anyString(), any(RuleKey.class), any(Integer.class))) - .thenReturn(null); when(chain.accept(issue)).thenReturn(true); assertThat(underTest.accept(issue, chain)).isTrue(); + verify(chain).accept(any()); } @Test - public void shouldRejectIfPatternMatches() { - IssuePattern pattern = mock(IssuePattern.class); - when(exclusionPatternMatcher.getMatchingPattern(anyString(), any(RuleKey.class), any(Integer.class))) - .thenReturn(pattern); + public void shouldRejectIfRulePatternMatches() { + WildcardPattern pattern = mock(WildcardPattern.class); + when(pattern.match(ruleKey.toString())).thenReturn(true); + underTest.addRuleExclusionPatternForComponent(component, pattern); assertThat(underTest.accept(issue, chain)).isFalse(); } + @Test + public void shouldAcceptIfRulePatternDoesNotMatch() { + WildcardPattern pattern = mock(WildcardPattern.class); + when(pattern.match(ruleKey.toString())).thenReturn(false); + underTest.addRuleExclusionPatternForComponent(component, pattern); + + assertThat(underTest.accept(issue, chain)).isFalse(); + } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java index 0a0f394e210..50096cc117f 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/IssueExclusionPatternInitializerTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.utils.MessageException; import org.sonar.core.config.IssueExclusionProperties; import static org.assertj.core.api.Assertions.assertThat; @@ -43,7 +44,7 @@ public class IssueExclusionPatternInitializerTest { assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0); } - @Test(expected = IllegalStateException.class) + @Test(expected = MessageException.class) public void shouldLogInvalidResourceKey() { settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1"); settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", ""); @@ -51,7 +52,7 @@ public class IssueExclusionPatternInitializerTest { patternsInitializer = new IssueExclusionPatternInitializer(settings.asConfig()); } - @Test(expected = IllegalStateException.class) + @Test(expected = MessageException.class) public void shouldLogInvalidRuleKey() { settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1"); settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "*"); @@ -78,7 +79,7 @@ public class IssueExclusionPatternInitializerTest { assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(0); } - @Test(expected = IllegalStateException.class) + @Test(expected = MessageException.class) public void shouldLogInvalidStartBlockPattern() { settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY, "1"); settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, ""); @@ -101,7 +102,7 @@ public class IssueExclusionPatternInitializerTest { assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(2); } - @Test(expected = IllegalStateException.class) + @Test(expected = MessageException.class) public void shouldLogInvalidAllFilePattern() { settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY, "1"); settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionProperties.FILE_REGEXP, ""); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/IssuePatternTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/IssuePatternTest.java index ee3ca739f7d..ffb26cffeb8 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/IssuePatternTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/IssuePatternTest.java @@ -19,9 +19,6 @@ */ package org.sonar.scanner.issue.ignore.pattern; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; import org.junit.Test; import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; @@ -31,35 +28,19 @@ import static org.assertj.core.api.Assertions.assertThat; public class IssuePatternTest { @Test - public void shouldMatchLines() { - Set<LineRange> lineRanges = new HashSet<>(); - lineRanges.add(new LineRange(12)); - lineRanges.add(new LineRange(15)); - lineRanges.add(new LineRange(20, 25)); - - IssuePattern pattern = new IssuePattern("*", "*", lineRanges); - - assertThat(pattern.matchLine(3)).isFalse(); - assertThat(pattern.matchLine(12)).isTrue(); - assertThat(pattern.matchLine(14)).isFalse(); - assertThat(pattern.matchLine(21)).isTrue(); - assertThat(pattern.matchLine(6599)).isFalse(); - } - - @Test public void shouldMatchJavaFile() { - String javaFile = "org.foo.Bar"; - assertThat(new IssuePattern("org.foo.Bar", "*").matchResource(javaFile)).isTrue(); - assertThat(new IssuePattern("org.foo.*", "*").matchResource(javaFile)).isTrue(); - assertThat(new IssuePattern("*Bar", "*").matchResource(javaFile)).isTrue(); - assertThat(new IssuePattern("*", "*").matchResource(javaFile)).isTrue(); - assertThat(new IssuePattern("org.*.?ar", "*").matchResource(javaFile)).isTrue(); - - assertThat(new IssuePattern("org.other.Hello", "*").matchResource(javaFile)).isFalse(); - assertThat(new IssuePattern("org.foo.Hello", "*").matchResource(javaFile)).isFalse(); - assertThat(new IssuePattern("org.*.??ar", "*").matchResource(javaFile)).isFalse(); - assertThat(new IssuePattern("org.*.??ar", "*").matchResource(null)).isFalse(); - assertThat(new IssuePattern("org.*.??ar", "*").matchResource("plop")).isFalse(); + String javaFile = "org/foo/Bar.java"; + assertThat(new IssuePattern("org/foo/Bar.java", "*").matchFile(javaFile)).isTrue(); + assertThat(new IssuePattern("org/foo/*", "*").matchFile(javaFile)).isTrue(); + assertThat(new IssuePattern("**Bar.java", "*").matchFile(javaFile)).isTrue(); + assertThat(new IssuePattern("**", "*").matchFile(javaFile)).isTrue(); + assertThat(new IssuePattern("org/*/?ar.java", "*").matchFile(javaFile)).isTrue(); + + assertThat(new IssuePattern("org/other/Hello.java", "*").matchFile(javaFile)).isFalse(); + assertThat(new IssuePattern("org/foo/Hello.java", "*").matchFile(javaFile)).isFalse(); + assertThat(new IssuePattern("org/*/??ar.java", "*").matchFile(javaFile)).isFalse(); + assertThat(new IssuePattern("org/*/??ar.java", "*").matchFile(null)).isFalse(); + assertThat(new IssuePattern("org/*/??ar.java", "*").matchFile("plop")).isFalse(); } @Test @@ -76,22 +57,4 @@ public class IssuePatternTest { assertThat(new IssuePattern("*", "*:Foo*IllegalRegexp").matchRule(rule)).isFalse(); } - @Test - public void shouldMatchViolation() { - Rule rule = Rule.create("checkstyle", "IllegalRegexp", ""); - String javaFile = "org.foo.Bar"; - - IssuePattern pattern = new IssuePattern("*", "*", Collections.singleton(new LineRange(12))); - - assertThat(pattern.match(javaFile, rule.ruleKey(), null)).isFalse(); - assertThat(pattern.match(javaFile, rule.ruleKey(), 12)).isTrue(); - assertThat(pattern.match(null, rule.ruleKey(), null)).isFalse(); - } - - @Test - public void shouldPrintPatternToString() { - IssuePattern pattern = new IssuePattern("*", "checkstyle:*"); - - assertThat(pattern.toString()).isEqualTo("IssuePattern[resourcePattern=*,rulePattern=checkstyle:*,lines=[],lineRanges=[],checkLines=false]"); - } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/PatternDecoderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/PatternDecoderTest.java deleted file mode 100644 index 2b4f50eb3ac..00000000000 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/PatternDecoderTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.issue.ignore.pattern; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PatternDecoderTest { - @Test - public void shouldCheckFormatOfResource() { - assertThat(PatternDecoder.isResource("")).isFalse(); - assertThat(PatternDecoder.isResource("*")).isTrue(); - assertThat(PatternDecoder.isResource("com.foo.*")).isTrue(); - } - - @Test - public void shouldCheckFormatOfRule() { - assertThat(PatternDecoder.isRule("")).isFalse(); - assertThat(PatternDecoder.isRule("*")).isTrue(); - assertThat(PatternDecoder.isRule("com.foo.*")).isTrue(); - } - - @Test - public void shouldCheckFormatOfLinesRange() { - assertThat(PatternDecoder.isLinesRange("")).isFalse(); - assertThat(PatternDecoder.isLinesRange(" ")).isFalse(); - assertThat(PatternDecoder.isLinesRange("12")).isFalse(); - assertThat(PatternDecoder.isLinesRange("12,212")).isFalse(); - - assertThat(PatternDecoder.isLinesRange("*")).isTrue(); - assertThat(PatternDecoder.isLinesRange("[]")).isTrue(); - assertThat(PatternDecoder.isLinesRange("[13]")).isTrue(); - assertThat(PatternDecoder.isLinesRange("[13,24]")).isTrue(); - assertThat(PatternDecoder.isLinesRange("[13,24,25-500]")).isTrue(); - assertThat(PatternDecoder.isLinesRange("[24-65]")).isTrue(); - assertThat(PatternDecoder.isLinesRange("[13,24-65,84-89,122]")).isTrue(); - } -} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/PatternMatcherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/PatternMatcherTest.java deleted file mode 100644 index 92797410b02..00000000000 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/pattern/PatternMatcherTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.issue.ignore.pattern; - -import com.google.common.collect.Sets; -import java.util.Collections; -import java.util.Set; -import javax.annotation.Nullable; -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.rules.Rule; - -import static org.assertj.core.api.Assertions.assertThat; - -public class PatternMatcherTest { - - public static final Rule CHECKSTYLE_RULE = Rule.create("checkstyle", "MagicNumber", ""); - public static final String JAVA_FILE = "org.foo.Hello"; - - private PatternMatcher patternMatcher; - - @Before - public void setUp() { - patternMatcher = new PatternMatcher(); - } - - @Test - public void shouldReturnExtraPatternForResource() { - String file = "foo"; - patternMatcher.addPatternToExcludeResource(file); - - IssuePattern extraPattern = patternMatcher.getPatternsForComponent(file).iterator().next(); - assertThat(extraPattern.matchResource(file)).isTrue(); - assertThat(extraPattern.isCheckLines()).isFalse(); - } - - @Test - public void shouldReturnExtraPatternForLinesOfResource() { - String file = "foo"; - Set<LineRange> lineRanges = Sets.newHashSet(); - lineRanges.add(new LineRange(25, 28)); - patternMatcher.addPatternToExcludeLines(file, lineRanges); - - IssuePattern extraPattern = patternMatcher.getPatternsForComponent(file).iterator().next(); - assertThat(extraPattern.matchResource(file)).isTrue(); - assertThat(extraPattern.getAllLines()).isEqualTo(Sets.newHashSet(25, 26, 27, 28)); - } - - @Test - public void shouldHaveNoMatcherIfNoneDefined() { - assertThat(patternMatcher.getMatchingPattern(JAVA_FILE, CHECKSTYLE_RULE.ruleKey(), null)).isNull(); - } - - @Test - public void shouldMatchWithStandardPatterns() { - patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello", "checkstyle:MagicNumber", createRanges(15, 200))); - - assertThat(patternMatcher.getMatchingPattern(JAVA_FILE, CHECKSTYLE_RULE.ruleKey(), 150)).isNotNull(); - } - - @Test - public void shouldNotMatchWithStandardPatterns() { - patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello", "checkstyle:MagicNumber", createRanges(15, 200))); - - assertThat(patternMatcher.getMatchingPattern(JAVA_FILE, CHECKSTYLE_RULE.ruleKey(), 5)).isNull(); - } - - @Test - public void shouldMatchWithExtraPattern() { - patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello", "*", createRanges(15, 200))); - - assertThat(patternMatcher.getMatchingPattern(JAVA_FILE, CHECKSTYLE_RULE.ruleKey(), 150)).isNotNull(); - } - - @Test - public void shouldNotMatchWithExtraPattern() { - patternMatcher.addPatternForComponent(JAVA_FILE, createPattern("org.foo.Hello", "*", createRanges(15, 200))); - - assertThat(patternMatcher.getMatchingPattern(JAVA_FILE, CHECKSTYLE_RULE.ruleKey(), 5)).isNull(); - } - - private IssuePattern createPattern(String resourcePattern, String rulePattern, @Nullable Set<LineRange> lineRanges) { - if (lineRanges != null) { - return new IssuePattern(resourcePattern, rulePattern, lineRanges); - } else { - return new IssuePattern(resourcePattern, rulePattern); - } - } - - private Set<LineRange> createRanges(int from, int to) { - return Collections.singleton(new LineRange(from, to)); - } - -} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoaderTest.java index baa433cef82..b52ad14b4af 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoaderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoaderTest.java @@ -19,7 +19,6 @@ */ package org.sonar.scanner.issue.ignore.scanner; -import java.io.IOException; import java.util.Arrays; import java.util.Collections; import org.junit.Before; @@ -28,9 +27,12 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.scanner.issue.ignore.IgnoreIssuesFilter; import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer; import org.sonar.scanner.issue.ignore.pattern.IssuePattern; -import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -46,15 +48,15 @@ public class IssueExclusionsLoaderTest { @Mock private IssueExclusionPatternInitializer exclusionPatternInitializer; - private PatternMatcher patternMatcher; + private IgnoreIssuesFilter ignoreIssuesFilter; private IssueExclusionsLoader scanner; @Before public void before() throws Exception { - patternMatcher = new PatternMatcher(); + ignoreIssuesFilter = mock(IgnoreIssuesFilter.class); MockitoAnnotations.initMocks(this); - scanner = new IssueExclusionsLoader(exclusionPatternInitializer, patternMatcher); + scanner = new IssueExclusionsLoader(exclusionPatternInitializer, ignoreIssuesFilter, mock(AnalysisWarnings.class)); } @Test @@ -64,54 +66,31 @@ public class IssueExclusionsLoaderTest { @Test public void createComputer() { - assertThat(scanner.createCharHandlerFor("src/main/java/Foo.java")).isNull(); + assertThat(scanner.createCharHandlerFor(TestInputFileBuilder.create("foo", "src/main/java/Foo.java").build())).isNull(); when(exclusionPatternInitializer.getAllFilePatterns()).thenReturn(Collections.singletonList("pattern")); - scanner = new IssueExclusionsLoader(exclusionPatternInitializer, patternMatcher); - assertThat(scanner.createCharHandlerFor("src/main/java/Foo.java")).isNotNull(); - + scanner = new IssueExclusionsLoader(exclusionPatternInitializer, ignoreIssuesFilter, mock(AnalysisWarnings.class)); + assertThat(scanner.createCharHandlerFor(TestInputFileBuilder.create("foo", "src/main/java/Foo.java").build())).isNotNull(); } @Test - public void shouldHavePatternsBasedOnMulticriteriaPattern() { - IssuePattern pattern1 = new IssuePattern("org/foo/Bar.java", "*"); - IssuePattern pattern2 = new IssuePattern("org/foo/Hello.java", "checkstyle:MagicNumber"); + public void populateRuleExclusionPatterns() { + IssuePattern pattern1 = new IssuePattern("org/foo/Bar*.java", "*"); + IssuePattern pattern2 = new IssuePattern("org/foo/Hell?.java", "checkstyle:MagicNumber"); when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(Arrays.asList(new IssuePattern[] {pattern1, pattern2})); - IssueExclusionsLoader loader = new IssueExclusionsLoader(exclusionPatternInitializer, patternMatcher); - loader.addMulticriteriaPatterns("org/foo/Bar.java", "org.foo.Bar"); - loader.addMulticriteriaPatterns("org/foo/Baz.java", "org.foo.Baz"); - loader.addMulticriteriaPatterns("org/foo/Hello.java", "org.foo.Hello"); - - assertThat(patternMatcher.getPatternsForComponent("org.foo.Bar")).hasSize(1); - assertThat(patternMatcher.getPatternsForComponent("org.foo.Baz")).hasSize(0); - assertThat(patternMatcher.getPatternsForComponent("org.foo.Hello")).hasSize(1); - } - - @Test - public void shouldAnalyzeProject() throws IOException { - IssuePattern pattern = new IssuePattern("**", "*"); - when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(Collections.singletonList(pattern)); - when(exclusionPatternInitializer.hasMulticriteriaPatterns()).thenReturn(true); - - PatternMatcher patternMatcher = mock(PatternMatcher.class); - IssueExclusionsLoader loader = new IssueExclusionsLoader(exclusionPatternInitializer, patternMatcher); - assertThat(loader.shouldExecute()).isTrue(); - loader.addMulticriteriaPatterns("src/main/java/Foo.java", "polop:src/main/java/Foo.java"); - loader.addMulticriteriaPatterns("src/test/java/FooTest.java", "polop:src/test/java/FooTest.java"); - - verify(patternMatcher).addPatternForComponent("polop:src/main/java/Foo.java", pattern); - verify(patternMatcher).addPatternForComponent("polop:src/test/java/FooTest.java", pattern); - verifyNoMoreInteractions(patternMatcher); + IssueExclusionsLoader loader = new IssueExclusionsLoader(exclusionPatternInitializer, ignoreIssuesFilter, mock(AnalysisWarnings.class)); + DefaultInputFile file1 = TestInputFileBuilder.create("foo", "org/foo/Bar.java").build(); + loader.addMulticriteriaPatterns(file1); + DefaultInputFile file2 = TestInputFileBuilder.create("foo", "org/foo/Baz.java").build(); + loader.addMulticriteriaPatterns(file2); + DefaultInputFile file3 = TestInputFileBuilder.create("foo", "org/foo/Hello.java").build(); + loader.addMulticriteriaPatterns(file3); + + verify(ignoreIssuesFilter).addRuleExclusionPatternForComponent(file1, pattern1.getRulePattern()); + verify(ignoreIssuesFilter).addRuleExclusionPatternForComponent(file3, pattern2.getRulePattern()); + verifyNoMoreInteractions(ignoreIssuesFilter); } - @Test - public void shouldExecute() { - when(exclusionPatternInitializer.hasMulticriteriaPatterns()).thenReturn(true); - assertThat(scanner.shouldExecute()).isTrue(); - - when(exclusionPatternInitializer.hasMulticriteriaPatterns()).thenReturn(false); - assertThat(scanner.shouldExecute()).isFalse(); - } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScannerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScannerTest.java index f47fdebe959..ab54b184c7d 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScannerTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScannerTest.java @@ -27,32 +27,27 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.regex.Pattern; +import java.util.stream.IntStream; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.FileMetadata; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer; -import org.sonar.scanner.issue.ignore.pattern.LineRange; -import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader.DoubleRegexpMatcher; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; public class IssueExclusionsRegexpScannerTest { - private String javaFile; + private DefaultInputFile javaFile; @Mock private IssueExclusionPatternInitializer patternsInitializer; - @Mock - private PatternMatcher patternMatcher; private List<Pattern> allFilePatterns; private List<DoubleRegexpMatcher> blockPatterns; @@ -69,8 +64,8 @@ public class IssueExclusionsRegexpScannerTest { }); allFilePatterns = Collections.singletonList(Pattern.compile("@SONAR-IGNORE-ALL")); - javaFile = "org.sonar.test.MyFile"; - regexpScanner = new IssueExclusionsRegexpScanner(javaFile, allFilePatterns, blockPatterns, patternMatcher); + javaFile = TestInputFileBuilder.create("foo", "src/Foo.java").build(); + regexpScanner = new IssueExclusionsRegexpScanner(javaFile, allFilePatterns, blockPatterns); } @Test @@ -78,8 +73,7 @@ public class IssueExclusionsRegexpScannerTest { Path filePath = getResource("file-with-single-regexp-last-line.txt"); fileMetadata.readMetadata(Files.newInputStream(filePath), UTF_8, filePath.toString(), regexpScanner); - verify(patternMatcher, times(1)).addPatternToExcludeResource(javaFile); - verifyNoMoreInteractions(patternMatcher); + assertThat(javaFile.isIgnoreAllIssues()).isTrue(); } @Test @@ -87,39 +81,34 @@ public class IssueExclusionsRegexpScannerTest { Path filePath = getResource("file-with-no-regexp.txt"); fileMetadata.readMetadata(Files.newInputStream(filePath), UTF_8, filePath.toString(), regexpScanner); - verifyNoMoreInteractions(patternMatcher); + assertThat(javaFile.isIgnoreAllIssues()).isFalse(); } @Test - public void shouldAddPatternToExcludeFile() throws Exception { + public void shouldExcludeAllIssues() throws Exception { Path filePath = getResource("file-with-single-regexp.txt"); fileMetadata.readMetadata(Files.newInputStream(filePath), UTF_8, filePath.toString(), regexpScanner); - verify(patternMatcher, times(1)).addPatternToExcludeResource(javaFile); - verifyNoMoreInteractions(patternMatcher); + assertThat(javaFile.isIgnoreAllIssues()).isTrue(); } @Test - public void shouldAddPatternToExcludeFileEvenIfAlsoDoubleRegexps() throws Exception { + public void shouldExcludeAllIssuesEvenIfAlsoDoubleRegexps() throws Exception { Path filePath = getResource("file-with-single-regexp-and-double-regexp.txt"); fileMetadata.readMetadata(Files.newInputStream(filePath), UTF_8, filePath.toString(), regexpScanner); - Set<LineRange> lineRanges = new HashSet<>(); - lineRanges.add(new LineRange(5, 26)); - verify(patternMatcher, times(1)).addPatternToExcludeResource(javaFile); - verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges); - verifyNoMoreInteractions(patternMatcher); + assertThat(javaFile.isIgnoreAllIssues()).isTrue(); } @Test - public void shouldAddPatternToExcludeLines() throws Exception { + public void shouldExcludeLines() throws Exception { Path filePath = getResource("file-with-double-regexp.txt"); fileMetadata.readMetadata(Files.newInputStream(filePath), UTF_8, filePath.toString(), regexpScanner); - Set<LineRange> lineRanges = new HashSet<>(); - lineRanges.add(new LineRange(21, 25)); - verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges); - verifyNoMoreInteractions(patternMatcher); + assertThat(javaFile.isIgnoreAllIssues()).isFalse(); + assertThat(IntStream.rangeClosed(1, 20).noneMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); + assertThat(IntStream.rangeClosed(21, 25).allMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); + assertThat(IntStream.rangeClosed(26, 34).noneMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); } @Test @@ -127,10 +116,9 @@ public class IssueExclusionsRegexpScannerTest { Path filePath = getResource("file-with-double-regexp-unfinished.txt"); fileMetadata.readMetadata(Files.newInputStream(filePath), UTF_8, filePath.toString(), regexpScanner); - Set<LineRange> lineRanges = new HashSet<>(); - lineRanges.add(new LineRange(21, 34)); - verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges); - verifyNoMoreInteractions(patternMatcher); + assertThat(javaFile.isIgnoreAllIssues()).isFalse(); + assertThat(IntStream.rangeClosed(1, 20).noneMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); + assertThat(IntStream.rangeClosed(21, 34).allMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); } @Test @@ -138,11 +126,11 @@ public class IssueExclusionsRegexpScannerTest { Path filePath = getResource("file-with-double-regexp-twice.txt"); fileMetadata.readMetadata(Files.newInputStream(filePath), UTF_8, filePath.toString(), regexpScanner); - Set<LineRange> lineRanges = new HashSet<>(); - lineRanges.add(new LineRange(21, 25)); - lineRanges.add(new LineRange(29, 33)); - verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges); - verifyNoMoreInteractions(patternMatcher); + assertThat(javaFile.isIgnoreAllIssues()).isFalse(); + assertThat(IntStream.rangeClosed(1, 20).noneMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); + assertThat(IntStream.rangeClosed(21, 25).allMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); + assertThat(IntStream.rangeClosed(26, 28).noneMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); + assertThat(IntStream.rangeClosed(29, 33).allMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); } @Test @@ -150,10 +138,8 @@ public class IssueExclusionsRegexpScannerTest { Path filePath = getResource("file-with-double-regexp-wrong-order.txt"); fileMetadata.readMetadata(Files.newInputStream(filePath), UTF_8, filePath.toString(), regexpScanner); - Set<LineRange> lineRanges = new HashSet<>(); - lineRanges.add(new LineRange(25, 35)); - verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges); - verifyNoMoreInteractions(patternMatcher); + assertThat(IntStream.rangeClosed(1, 24).noneMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); + assertThat(IntStream.rangeClosed(25, 35).allMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); } @Test @@ -161,10 +147,9 @@ public class IssueExclusionsRegexpScannerTest { Path filePath = getResource("file-with-double-regexp-mess.txt"); fileMetadata.readMetadata(Files.newInputStream(filePath), UTF_8, filePath.toString(), regexpScanner); - Set<LineRange> lineRanges = new HashSet<>(); - lineRanges.add(new LineRange(21, 29)); - verify(patternMatcher, times(1)).addPatternToExcludeLines(javaFile, lineRanges); - verifyNoMoreInteractions(patternMatcher); + assertThat(IntStream.rangeClosed(1, 20).noneMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); + assertThat(IntStream.rangeClosed(21, 29).allMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); + assertThat(IntStream.rangeClosed(30, 37).noneMatch(javaFile::isIgnoreAllIssuesOnLine)).isTrue(); } private Path getResource(String fileName) throws URISyntaxException { diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java index e2103e18828..293ef226b5f 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java @@ -328,8 +328,8 @@ public class FileSystemMediumTest { .execute(); assertThat(logTester.logs()).containsOnlyOnce("'src" + File.separator + "myfile.binary' indexed with language 'null'"); - assertThat(logTester.logs()).doesNotContain("'src/myfile.binary' generating issue exclusions"); - assertThat(logTester.logs()).containsOnlyOnce("'src/sample.xoo' generating issue exclusions"); + assertThat(logTester.logs()).doesNotContain("Evaluate issue exclusions for 'src/myfile.binary'"); + assertThat(logTester.logs()).containsOnlyOnce("Evaluate issue exclusions for 'src/sample.xoo'"); } @Test @@ -354,8 +354,8 @@ public class FileSystemMediumTest { .build()) .execute(); - assertThat(logTester.logs()).containsOnlyOnce("- Exclusion pattern 'pattern': every issue in this file will be ignored."); - assertThat(logTester.logs()).containsOnlyOnce("'src/myfile.binary' generating issue exclusions"); + assertThat(logTester.logs()).containsSequence("Evaluate issue exclusions for 'src/sample.xoo'", + " - Exclusion pattern 'pattern': all issues in this file will be ignored."); } @Test diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java index 5a066cdc700..3fd82bf4afb 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/issues/IssuesMediumTest.java @@ -22,16 +22,22 @@ package org.sonar.scanner.mediumtest.issues; import com.google.common.collect.ImmutableMap; import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.List; import org.apache.commons.io.FileUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +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.protocol.output.ScannerReport.ExternalIssue; import org.sonar.scanner.protocol.output.ScannerReport.Issue; +import org.sonar.scanner.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; @@ -44,6 +50,9 @@ public class IssuesMediumTest { public TemporaryFolder temp = new TemporaryFolder(); @Rule + public LogTester logTester = new LogTester(); + + @Rule public ScannerMediumTester tester = new ScannerMediumTester() .registerPlugin("xoo", new XooPlugin()) .addDefaultQProfile("xoo", "Sonar Way") @@ -113,7 +122,7 @@ public class IssuesMediumTest { } @Test - public void testIssueExclusion() throws Exception { + public void testIssueExclusionByRegexp() throws Exception { File projectDir = new File("test-resources/mediumtest/xoo/sample"); File tmpDir = temp.newFolder(); FileUtils.copyDirectory(projectDir, tmpDir); @@ -129,23 +138,249 @@ public class IssuesMediumTest { } @Test - public void testIssueDetails() throws IOException { + public void testIssueExclusionByBlock() throws Exception { + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "1\nSONAR-OFF 2\n3\n4\n5\nSONAR-ON 6\n7\n8\n9\n10", StandardCharsets.UTF_8); + + AnalysisResult result = tester.newAnalysis() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.sources", "src") + .build()) + .property("sonar.issue.ignore.block", "1") + .property("sonar.issue.ignore.block.1.beginBlockRegexp", "SON.*-OFF") + .property("sonar.issue.ignore.block.1.endBlockRegexp", "SON.*-ON") + .execute(); + List<Issue> issues = result.issuesFor(result.inputFile("src/sample.xoo")); + assertThat(issues).hasSize(5); + assertThat(issues) + .extracting("textRange.startLine") + .containsExactlyInAnyOrder(1, 7, 8, 9, 10); + } + + @Test + public void testIssueExclusionByIgnoreMultiCriteria() throws Exception { + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + activateTODORule(); + + File xooFile1 = new File(srcDir, "sample1.xoo"); + FileUtils.write(xooFile1, "1\n2\n3 TODO\n4\n5\n6 TODO\n7\n8\n9\n10", StandardCharsets.UTF_8); + File xooFile11 = new File(srcDir, "sample11.xoo"); + FileUtils.write(xooFile11, "1\n2\n3 TODO\n4\n5\n6 TODO\n7\n8\n9\n10", StandardCharsets.UTF_8); + + AnalysisResult result = tester.newAnalysis() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.sources", "src") + .build()) + .property("sonar.issue.ignore.multicriteria", "1,2") + .property("sonar.issue.ignore.multicriteria.1.ruleKey", "xoo:HasTag") + .property("sonar.issue.ignore.multicriteria.1.resourceKey", "src/sample11.xoo") + .property("sonar.issue.ignore.multicriteria.2.ruleKey", "xoo:One*") + .property("sonar.issue.ignore.multicriteria.2.resourceKey", "src/sample?.xoo") + .execute(); + + List<Issue> issues = result.issuesFor(result.inputFile("src/sample1.xoo")); + assertThat(issues).hasSize(2); + + issues = result.issuesFor(result.inputFile("src/sample11.xoo")); + assertThat(issues).hasSize(10); + } + + @Test + public void warn_user_for_outdated_IssueExclusionByIgnoreMultiCriteria() throws Exception { + File baseDir = temp.getRoot(); + File baseDirModuleA = new File(baseDir, "moduleA"); + File baseDirModuleB = new File(baseDir, "moduleB"); + File srcDirA = new File(baseDirModuleA, "src"); + srcDirA.mkdirs(); + File srcDirB = new File(baseDirModuleB, "src"); + srcDirB.mkdirs(); + + File xooFileA = new File(srcDirA, "sampleA.xoo"); + FileUtils.write(xooFileA, "1\n2\n3\n4\n5\n6 TODO\n7\n8\n9\n10", StandardCharsets.UTF_8); + File xooFileB = new File(srcDirB, "sampleB.xoo"); + FileUtils.write(xooFileB, "1\n2\n3\n4\n5\n6 TODO\n7\n8\n9\n10", StandardCharsets.UTF_8); + + tester + .addProjectServerSettings("sonar.issue.ignore.multicriteria", "1") + .addProjectServerSettings("sonar.issue.ignore.multicriteria.1.ruleKey", "*") + .addProjectServerSettings("sonar.issue.ignore.multicriteria.1.resourceKey", "src/sampleA.xoo"); + + AnalysisResult result = tester.newAnalysis() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.modules", "moduleA,moduleB") + .put("sonar.sources", "src") + .build()) + .execute(); + + assertThat(logTester.logs(LoggerLevel.WARN)).contains("Specifying module-relative paths at project level in property 'sonar.issue.ignore.multicriteria' is deprecated. To continue matching files like 'moduleA/src/sampleA.xoo', update this property so that patterns refer to project-relative paths."); + + List<Issue> issues = result.issuesFor(result.inputFile("moduleA/src/sampleA.xoo")); + assertThat(issues).hasSize(0); + + issues = result.issuesFor(result.inputFile("moduleB/src/sampleB.xoo")); + assertThat(issues).hasSize(10); + } + + @Test + public void warn_user_for_unsupported_module_level_IssueExclusion() throws Exception { + File baseDir = temp.getRoot(); + File baseDirModuleA = new File(baseDir, "moduleA"); + File baseDirModuleB = new File(baseDir, "moduleB"); + File srcDirA = new File(baseDirModuleA, "src"); + srcDirA.mkdirs(); + File srcDirB = new File(baseDirModuleB, "src"); + srcDirB.mkdirs(); + + File xooFileA = new File(srcDirA, "sampleA.xoo"); + FileUtils.write(xooFileA, "1\n2\n3\n4\n5\n6 TODO\n7\n8\n9\n10", StandardCharsets.UTF_8); + File xooFileB = new File(srcDirB, "sampleB.xoo"); + FileUtils.write(xooFileB, "1\n2\n3\n4\n5\n6 TODO\n7\n8\n9\n10", StandardCharsets.UTF_8); + + AnalysisResult result = tester.newAnalysis() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.modules", "moduleA,moduleB") + .put("sonar.sources", "src") + .put("sonar.scm.disabled", "true") + .put("sonar.issue.ignore.multicriteria", "1") + .put("sonar.issue.ignore.multicriteria.1.ruleKey", "*") + .put("sonar.issue.ignore.multicriteria.1.resourceKey", "*") + .build()) + .execute(); + + assertThat(logTester.logs(LoggerLevel.WARN)).isEmpty(); + + result = tester.newAnalysis() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.modules", "moduleA,moduleB") + .put("sonar.sources", "src") + .put("sonar.scm.disabled", "true") + .put("moduleA.sonar.issue.ignore.multicriteria", "1") + .put("moduleA.sonar.issue.ignore.multicriteria.1.ruleKey", "*") + .put("moduleA.sonar.issue.ignore.multicriteria.1.resourceKey", "*") + .build()) + .execute(); + + assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly("Specifying issue exclusions at module level is not supported anymore. Configure the property 'sonar.issue.ignore.multicriteria' and any other issue exclusions at project level."); + + List<Issue> issues = result.issuesFor(result.inputFile("moduleA/src/sampleA.xoo")); + assertThat(issues).hasSize(10); + + issues = result.issuesFor(result.inputFile("moduleB/src/sampleB.xoo")); + assertThat(issues).hasSize(10); + } + + @Test + public void testIssueExclusionByEnforceMultiCriteria() throws Exception { + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + activateTODORule(); + + File xooFile1 = new File(srcDir, "sample1.xoo"); + FileUtils.write(xooFile1, "1\n2\n3 TODO\n4\n5\n6 TODO\n7\n8\n9\n10", StandardCharsets.UTF_8); + File xooFile11 = new File(srcDir, "sample11.xoo"); + FileUtils.write(xooFile11, "1\n2\n3 TODO\n4\n5\n6 TODO\n7\n8\n9\n10", StandardCharsets.UTF_8); + + AnalysisResult result = tester.newAnalysis() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.sources", "src") + .build()) + .property("sonar.issue.enforce.multicriteria", "1,2") + .property("sonar.issue.enforce.multicriteria.1.ruleKey", "xoo:HasTag") + .property("sonar.issue.enforce.multicriteria.1.resourceKey", "src/sample11.xoo") + .property("sonar.issue.enforce.multicriteria.2.ruleKey", "xoo:One*") + .property("sonar.issue.enforce.multicriteria.2.resourceKey", "src/sample?.xoo") + .execute(); + + List<Issue> issues = result.issuesFor(result.inputFile("src/sample1.xoo")); + assertThat(issues).hasSize(10); + + issues = result.issuesFor(result.inputFile("src/sample11.xoo")); + assertThat(issues).hasSize(2); + } + + @Test + public void warn_user_for_outdated_IssueExclusionByEnforceMultiCriteria() throws Exception { + File baseDir = temp.getRoot(); + File baseDirModuleA = new File(baseDir, "moduleA"); + File baseDirModuleB = new File(baseDir, "moduleB"); + File srcDirA = new File(baseDirModuleA, "src"); + srcDirA.mkdirs(); + File srcDirB = new File(baseDirModuleB, "src"); + srcDirB.mkdirs(); + + File xooFileA = new File(srcDirA, "sampleA.xoo"); + FileUtils.write(xooFileA, "1\n2\n3\n4\n5\n6 TODO\n7\n8\n9\n10", StandardCharsets.UTF_8); + File xooFileB = new File(srcDirB, "sampleB.xoo"); + FileUtils.write(xooFileB, "1\n2\n3\n4\n5\n6 TODO\n7\n8\n9\n10", StandardCharsets.UTF_8); + + tester + .addProjectServerSettings("sonar.issue.enforce.multicriteria", "1") + .addProjectServerSettings("sonar.issue.enforce.multicriteria.1.ruleKey", "*") + .addProjectServerSettings("sonar.issue.enforce.multicriteria.1.resourceKey", "src/sampleA.xoo"); + + AnalysisResult result = tester.newAnalysis() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.modules", "moduleA,moduleB") + .put("sonar.sources", "src") + .build()) + .execute(); + + assertThat(logTester.logs(LoggerLevel.WARN)).contains("Specifying module-relative paths at project level in property 'sonar.issue.enforce.multicriteria' is deprecated. To continue matching files like 'moduleA/src/sampleA.xoo', update this property so that patterns refer to project-relative paths."); + + List<Issue> issues = result.issuesFor(result.inputFile("moduleA/src/sampleA.xoo")); + assertThat(issues).hasSize(10); + + issues = result.issuesFor(result.inputFile("moduleB/src/sampleB.xoo")); + assertThat(issues).hasSize(0); + } + + private void activateTODORule() { + LoadedActiveRule r = new LoadedActiveRule(); + r.setRuleKey(RuleKey.of("xoo", HasTagSensor.RULE_KEY)); + r.setName("TODO"); + r.setLanguage("xoo"); + r.setSeverity("MAJOR"); + r.setParams(ImmutableMap.of("tag", "TODO")); + tester.activateRule(r); + } + + @Test + public void testIssueDetails() throws IOException { File baseDir = temp.newFolder(); File srcDir = new File(baseDir, "src"); srcDir.mkdir(); File xooFile = new File(srcDir, "sample.xoo"); - FileUtils.write(xooFile, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10"); + FileUtils.write(xooFile, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10", StandardCharsets.UTF_8); AnalysisResult result = tester.newAnalysis() .properties(ImmutableMap.<String, String>builder() - .put("sonar.task", "scan") .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) .put("sonar.projectKey", "com.foo.project") - .put("sonar.projectName", "Foo Project") - .put("sonar.projectVersion", "1.0-SNAPSHOT") - .put("sonar.projectDescription", "Description of Foo Project") .put("sonar.sources", "src") .build()) .execute(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorBuilderTest.java index 1db1cd92e04..ead72172d9c 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorBuilderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorBuilderTest.java @@ -35,11 +35,13 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.notifications.AnalysisWarnings; import org.sonar.api.utils.MessageException; import org.sonar.api.utils.log.LogTester; import org.sonar.scanner.bootstrap.ScannerProperties; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; public class ProjectReactorBuilderTest { @@ -399,7 +401,7 @@ public class ProjectReactorBuilderTest { @Test public void shouldInitRootWorkDir() { - ProjectReactorBuilder builder = new ProjectReactorBuilder(new ScannerProperties(Maps.<String, String>newHashMap())); + ProjectReactorBuilder builder = new ProjectReactorBuilder(new ScannerProperties(Maps.<String, String>newHashMap()), mock(AnalysisWarnings.class)); File baseDir = new File("target/tmp/baseDir"); File workDir = builder.initRootProjectWorkDir(baseDir, Maps.<String, String>newHashMap()); @@ -411,7 +413,7 @@ public class ProjectReactorBuilderTest { public void shouldInitWorkDirWithCustomRelativeFolder() { Map<String, String> props = Maps.<String, String>newHashMap(); props.put("sonar.working.directory", ".foo"); - ProjectReactorBuilder builder = new ProjectReactorBuilder(new ScannerProperties(props)); + ProjectReactorBuilder builder = new ProjectReactorBuilder(new ScannerProperties(props), mock(AnalysisWarnings.class)); File baseDir = new File("target/tmp/baseDir"); File workDir = builder.initRootProjectWorkDir(baseDir, props); @@ -423,7 +425,7 @@ public class ProjectReactorBuilderTest { public void shouldInitRootWorkDirWithCustomAbsoluteFolder() { Map<String, String> props = Maps.<String, String>newHashMap(); props.put("sonar.working.directory", new File("src").getAbsolutePath()); - ProjectReactorBuilder builder = new ProjectReactorBuilder(new ScannerProperties(props)); + ProjectReactorBuilder builder = new ProjectReactorBuilder(new ScannerProperties(props), mock(AnalysisWarnings.class)); File baseDir = new File("target/tmp/baseDir"); File workDir = builder.initRootProjectWorkDir(baseDir, props); @@ -482,7 +484,7 @@ public class ProjectReactorBuilderTest { private ProjectDefinition loadProjectDefinition(String projectFolder) { Map<String, String> props = loadProps(projectFolder); ScannerProperties bootstrapProps = new ScannerProperties(props); - ProjectReactor projectReactor = new ProjectReactorBuilder(bootstrapProps).execute(); + ProjectReactor projectReactor = new ProjectReactorBuilder(bootstrapProps, mock(AnalysisWarnings.class)).execute(); return projectReactor.getRoot(); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MetadataGeneratorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MetadataGeneratorTest.java index b46d174783b..cc4b6bf2485 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MetadataGeneratorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/MetadataGeneratorTest.java @@ -35,9 +35,10 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.notifications.AnalysisWarnings; import org.sonar.api.utils.PathUtils; +import org.sonar.scanner.issue.ignore.IgnoreIssuesFilter; import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer; -import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; import static org.apache.commons.codec.digest.DigestUtils.md5Hex; @@ -61,7 +62,8 @@ public class MetadataGeneratorTest { public void setUp() throws IOException { MockitoAnnotations.initMocks(this); metadata = new FileMetadata(); - IssueExclusionsLoader issueExclusionsLoader = new IssueExclusionsLoader(mock(IssueExclusionPatternInitializer.class), mock(PatternMatcher.class)); + IssueExclusionsLoader issueExclusionsLoader = new IssueExclusionsLoader(mock(IssueExclusionPatternInitializer.class), mock(IgnoreIssuesFilter.class), + mock(AnalysisWarnings.class)); generator = new MetadataGenerator(statusDetection, metadata, issueExclusionsLoader); } |