From: Jean-Baptiste Lievremont Date: Thu, 5 Sep 2013 09:49:20 +0000 (+0200) Subject: SONAR-3644 Save changes to new API before revert X-Git-Tag: 4.0~500^2~6 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c6999241df2f34d82c8ba476489a469ca6b61c5a;p=sonarqube.git SONAR-3644 Save changes to new API before revert Copy resources from Switch Off Violations plugin to core Remove references to deprecated APIs --- diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java index 7ee64903dc7..3f25e46fb09 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java @@ -47,6 +47,7 @@ import org.sonar.plugins.core.issue.IssueTracking; import org.sonar.plugins.core.issue.IssueTrackingDecorator; import org.sonar.plugins.core.issue.IssuesDensityDecorator; import org.sonar.plugins.core.issue.WeightedIssuesDecorator; +import org.sonar.plugins.core.issue.ignore.IgnoreIssuesPlugin; import org.sonar.plugins.core.issue.notification.ChangesOnMyIssueNotificationDispatcher; import org.sonar.plugins.core.issue.notification.IssueChangesEmailTemplate; import org.sonar.plugins.core.issue.notification.NewFalsePositiveNotificationDispatcher; @@ -406,9 +407,11 @@ import java.util.List; }) public final class CorePlugin extends SonarPlugin { - @SuppressWarnings("unchecked") + @SuppressWarnings("rawtypes") public List getExtensions() { - return ImmutableList.of( + ImmutableList.Builder extensions = ImmutableList.builder(); + + extensions.add( DefaultResourceTypes.class, UserManagedMetrics.class, Periods.class, @@ -524,5 +527,9 @@ public final class CorePlugin extends SonarPlugin { // Notify alerts on my favourite projects NewAlerts.class, NewAlerts.newMetadata()); + + extensions.addAll(IgnoreIssuesPlugin.getExtensions()); + + return extensions.build(); } } diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/Constants.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/Constants.java new file mode 100644 index 00000000000..632d038d1bd --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/Constants.java @@ -0,0 +1,47 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore; + +public interface Constants { + // New Properties + String SUB_CATEGORY_IGNORE_ISSUES = "Ignore Issues"; + + String CORE_KEY_PREFIX = "sonar.issue.ignore"; + String DEPRECATED_PLUGIN_KEY_PREFIX = "sonar.switchoffviolations"; + + String MULTICRITERIA_SUFFIX = ".multicriteria"; + String PATTERNS_MULTICRITERIA_KEY = CORE_KEY_PREFIX + MULTICRITERIA_SUFFIX; + String DEPRECATED_MULTICRITERIA_KEY = DEPRECATED_PLUGIN_KEY_PREFIX + MULTICRITERIA_SUFFIX; + String RESOURCE_KEY = "resourceKey"; + String RULE_KEY = "ruleKey"; + String LINE_RANGE_KEY = "lineRange"; + + String BLOCK_SUFFIX = ".block"; + String PATTERNS_BLOCK_KEY = CORE_KEY_PREFIX + BLOCK_SUFFIX; + String DEPRECATED_BLOCK_KEY = DEPRECATED_PLUGIN_KEY_PREFIX + BLOCK_SUFFIX; + String BEGIN_BLOCK_REGEXP = "beginBlockRegexp"; + String END_BLOCK_REGEXP = "endBlockRegexp"; + + String ALLFILE_SUFFIX = ".allfile"; + String PATTERNS_ALLFILE_KEY = CORE_KEY_PREFIX + ALLFILE_SUFFIX; + String DEPRECATED_ALLFILE_KEY = DEPRECATED_PLUGIN_KEY_PREFIX + ALLFILE_SUFFIX; + String FILE_REGEXP = "fileRegexp"; +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesConfiguration.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesConfiguration.java new file mode 100644 index 00000000000..045425c5d76 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesConfiguration.java @@ -0,0 +1,109 @@ +// POLOP_IS_GREAT +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore; + +import org.sonar.api.PropertyType; + +import org.sonar.api.config.PropertyFieldDefinition; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.CoreProperties; +import com.google.common.collect.ImmutableList; +import org.sonar.api.config.PropertyDefinition; + +import java.util.List; + +public class IgnoreIssuesConfiguration { + + + static final int LARGE_SIZE = 20; + static final int SMALL_SIZE = 10; + + public static List getPropertyDefinitions() { + return ImmutableList.of( + PropertyDefinition.builder(Constants.PATTERNS_MULTICRITERIA_KEY) + .category(CoreProperties.CATEGORY_EXCLUSIONS) + .subCategory(Constants.SUB_CATEGORY_IGNORE_ISSUES) + .deprecatedKey(Constants.DEPRECATED_MULTICRITERIA_KEY) + .name("Resource Key Pattern") + .description("Patterns used to identify which violations to switch off.
" + + "More information on the Project Administration page.
") + .onQualifiers(Qualifiers.PROJECT) + .fields( + PropertyFieldDefinition.build(Constants.RESOURCE_KEY) + .name("Resource Key Pattern") + .description("Pattern used to match resources which should be ignored") + .type(PropertyType.STRING) + .indicativeSize(LARGE_SIZE) + .build(), + PropertyFieldDefinition.build(Constants.RULE_KEY) + .name("Rule Key Pattern") + .description("Pattern used to match rules which should be ignored") + .type(PropertyType.STRING) + .indicativeSize(LARGE_SIZE) + .build(), + PropertyFieldDefinition.build(Constants.LINE_RANGE_KEY) + .name("Line Range") + .description("Range of lines that should be ignored.") + .type(PropertyType.STRING) + .indicativeSize(SMALL_SIZE) + .build()) + .build(), + PropertyDefinition.builder(Constants.PATTERNS_BLOCK_KEY) + .category(CoreProperties.CATEGORY_EXCLUSIONS) + .subCategory(Constants.SUB_CATEGORY_IGNORE_ISSUES) + .deprecatedKey(Constants.DEPRECATED_BLOCK_KEY) + .name("Block exclusion patterns") + .description("Patterns used to identify blocks in which violations are switched off.
" + + "More information on the Project Administration page.
") + .onQualifiers(Qualifiers.PROJECT) + .fields( + PropertyFieldDefinition.build(Constants.BEGIN_BLOCK_REGEXP) + .name("Regular expression for start of block") + .description("If this regular expression is found in a resource, then following lines are ignored until end of block.") + .type(PropertyType.STRING) + .indicativeSize(LARGE_SIZE) + .build(), + PropertyFieldDefinition.build(Constants.END_BLOCK_REGEXP) + .name("Regular expression for end of block") + .description("If specified, this regular expression is used to determine the end of code blocks to ignore. If not, then block ends at the end of file.") + .type(PropertyType.STRING) + .indicativeSize(LARGE_SIZE) + .build()) + .build(), + PropertyDefinition.builder(Constants.PATTERNS_ALLFILE_KEY) + .category(CoreProperties.CATEGORY_EXCLUSIONS) + .subCategory(Constants.SUB_CATEGORY_IGNORE_ISSUES) + .deprecatedKey(Constants.DEPRECATED_ALLFILE_KEY) + .name("File exclusion patterns") + .description("Patterns used to identify files in which violations are switched off.
" + + "More information on the Project Administration page.
") + .onQualifiers(Qualifiers.PROJECT) + .fields( + PropertyFieldDefinition.build(Constants.FILE_REGEXP) + .name("Regular expression") + .description("If this regular expression is found in a resource, then following lines are ignored.") + .type(PropertyType.STRING) + .indicativeSize(LARGE_SIZE) + .build()) + .build()); + } +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilter.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilter.java new file mode 100644 index 00000000000..c60b9b71672 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilter.java @@ -0,0 +1,64 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueFilter; +import org.sonar.plugins.core.issue.ignore.pattern.Pattern; +import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer; + +import java.util.List; + +public final class IgnoreIssuesFilter implements IssueFilter { + + private static final Logger LOG = LoggerFactory.getLogger(IgnoreIssuesFilter.class); + + private PatternsInitializer patternsInitializer; + + public IgnoreIssuesFilter(PatternsInitializer patternsInitializer) { + this.patternsInitializer = patternsInitializer; + } + + public boolean accept(Issue issue) { + Pattern extraPattern = patternsInitializer.getExtraPattern(issue.componentKey()); + LOG.debug("Extra pattern for resource {}: {}", issue.componentKey(), extraPattern); + if (extraPattern != null && extraPattern.match(issue)) { + logExclusion(issue, extraPattern); + return false; + } + + List patterns = patternsInitializer.getMulticriteriaPatterns(); + for (Pattern pattern : patterns) { + if (pattern.match(issue)) { + logExclusion(issue, pattern); + return false; + } + } + return true; + } + + private void logExclusion(Issue issue, Pattern pattern) { + LOG.debug("Issue {} ignored by {}", issue, pattern); + } + +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPlugin.java new file mode 100644 index 00000000000..29d0048c50a --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPlugin.java @@ -0,0 +1,45 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore; + +import com.google.common.collect.ImmutableList; +import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer; +import org.sonar.plugins.core.issue.ignore.scanner.RegexpScanner; +import org.sonar.plugins.core.issue.ignore.scanner.SourceScanner; + +import java.util.List; + +public final class IgnoreIssuesPlugin { + + public static List getExtensions() { + ImmutableList.Builder extensions = ImmutableList.builder(); + + extensions.addAll(IgnoreIssuesConfiguration.getPropertyDefinitions()); + extensions.add( + PatternsInitializer.class, + RegexpScanner.class, + SourceScanner.class, + IgnoreIssuesFilter.class); + + return extensions.build(); + } + +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/LineRange.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/LineRange.java new file mode 100644 index 00000000000..743533d05f3 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/LineRange.java @@ -0,0 +1,91 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.pattern; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; + +import java.util.Set; + +public class LineRange { + private int from; + private int to; + + public LineRange(int from, int to) { + Preconditions.checkArgument(from <= to, "Line range is not valid: %s must be greater than %s", from, to); + + this.from = from; + this.to = to; + } + + public boolean in(int lineId) { + return from <= lineId && lineId <= to; + } + + public Set toLines() { + Set lines = Sets.newLinkedHashSet(); + for (int index = from; index <= to; index++) { + lines.add(index); + } + return lines; + } + + @Override + public String toString() { + return "[" + from + "-" + to + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + from; + result = prime * result + to; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + if (fieldsDiffer((LineRange) obj)) { + return false; + } + return true; + } + + private boolean fieldsDiffer(LineRange other) { + if (from != other.from) { + return true; + } + if (to != other.to) { + return true; + } + return false; + } +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/Pattern.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/Pattern.java new file mode 100644 index 00000000000..ae3a8ebc91b --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/Pattern.java @@ -0,0 +1,159 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.pattern; + +import com.google.common.collect.Sets; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.sonar.api.issue.Issue; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.WildcardPattern; + +import java.util.Set; + +public class Pattern { + + private WildcardPattern resourcePattern; + private WildcardPattern rulePattern; + private Set lines = Sets.newLinkedHashSet(); + private Set lineRanges = Sets.newLinkedHashSet(); + private String beginBlockRegexp; + private String endBlockRegexp; + private String allFileRegexp; + private boolean checkLines = true; + + public Pattern() { + } + + public Pattern(String resourcePattern, String rulePattern) { + this.resourcePattern = WildcardPattern.create(resourcePattern); + this.rulePattern = WildcardPattern.create(rulePattern); + } + + public Pattern(String resourcePattern, String rulePattern, Set lineRanges) { + this(resourcePattern, rulePattern); + this.lineRanges = lineRanges; + } + + public WildcardPattern getResourcePattern() { + return resourcePattern; + } + + public WildcardPattern getRulePattern() { + return rulePattern; + } + + public String getBeginBlockRegexp() { + return beginBlockRegexp; + } + + public String getEndBlockRegexp() { + return endBlockRegexp; + } + + public String getAllFileRegexp() { + return allFileRegexp; + } + + Pattern addLineRange(int fromLineId, int toLineId) { + lineRanges.add(new LineRange(fromLineId, toLineId)); + return this; + } + + Pattern addLine(int lineId) { + lines.add(lineId); + return this; + } + + boolean isCheckLines() { + return checkLines; + } + + Pattern setCheckLines(boolean b) { + this.checkLines = b; + return this; + } + + Pattern setBeginBlockRegexp(String beginBlockRegexp) { + this.beginBlockRegexp = beginBlockRegexp; + return this; + } + + Pattern setEndBlockRegexp(String endBlockRegexp) { + this.endBlockRegexp = endBlockRegexp; + return this; + } + + Pattern setAllFileRegexp(String allFileRegexp) { + this.allFileRegexp = allFileRegexp; + return this; + } + + Set getAllLines() { + Set allLines = Sets.newLinkedHashSet(lines); + for (LineRange lineRange : lineRanges) { + allLines.addAll(lineRange.toLines()); + } + return allLines; + } + + public boolean match(Issue violation) { + boolean match = matchResource(violation.componentKey()) && matchRule(violation.ruleKey()); + if (checkLines && violation.line() != null) { + match = match && matchLine(violation.line()); + } + return match; + } + + 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) { + System.out.printf("Matching rule {} against pattern {}", rule, rulePattern); + if (rule == null) { + return false; + } + + String key = new StringBuilder().append(rule.repository()).append(':').append(rule.rule()).toString(); + return rulePattern.match(key); + } + + boolean matchResource(String resource) { + System.out.printf("Matching resource {} against pattern {}", resource, resourcePattern); + return resource != null && resourcePattern.match(resource); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoder.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoder.java new file mode 100644 index 00000000000..d40388b9a28 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoder.java @@ -0,0 +1,158 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.pattern; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.SonarException; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +public class PatternDecoder { + + private static final int THREE_FIELDS_PER_LINE = 3; + private static final String LINE_RANGE_REGEXP = "\\[((\\d+|\\d+-\\d+),?)*\\]"; + + public List decode(String patternsList) { + List patterns = Lists.newLinkedList(); + String[] patternsLines = StringUtils.split(patternsList, "\n"); + for (String patternLine : patternsLines) { + Pattern pattern = decodeLine(patternLine.trim()); + if (pattern != null) { + patterns.add(pattern); + } + } + return patterns; + } + + public List decode(File file) { + try { + List lines = FileUtils.readLines(file); + List patterns = Lists.newLinkedList(); + for (String line : lines) { + Pattern pattern = decodeLine(line); + if (pattern != null) { + patterns.add(pattern); + } + } + return patterns; + + } catch (IOException e) { + throw new SonarException("Fail to load the file: " + file.getAbsolutePath(), e); + } + } + + /** + * Main method that decodes a line which defines a pattern + */ + public Pattern decodeLine(String line) { + if (isBlankOrComment(line)) { + return null; + } + + String[] fields = StringUtils.splitPreserveAllTokens(line, ';'); + if (fields.length > THREE_FIELDS_PER_LINE) { + throw new SonarException("Invalid format. The following line has more than 3 fields separated by comma: " + line); + } + + Pattern pattern; + if (fields.length == THREE_FIELDS_PER_LINE) { + checkRegularLineConstraints(line, fields); + pattern = new Pattern(StringUtils.trim(fields[0]), StringUtils.trim(fields[1])); + decodeRangeOfLines(pattern, fields[2]); + } else if (fields.length == 2) { + checkDoubleRegexpLineConstraints(line, fields); + pattern = new Pattern().setBeginBlockRegexp(fields[0]).setEndBlockRegexp(fields[1]); + } else { + pattern = new Pattern().setAllFileRegexp(fields[0]); + } + + return pattern; + } + + private void checkRegularLineConstraints(String line, String[] fields) { + if (!isResource(fields[0])) { + throw new SonarException("Invalid format. The first field does not define a resource pattern: " + line); + } + if (!isRule(fields[1])) { + throw new SonarException("Invalid format. The second field does not define a rule pattern: " + line); + } + if (!isLinesRange(fields[2])) { + throw new SonarException("Invalid format. The third field does not define a range of lines: " + line); + } + } + + private void checkDoubleRegexpLineConstraints(String line, String[] fields) { + if (!isRegexp(fields[0])) { + throw new SonarException("Invalid format. The first field does not define a regular expression: " + line); + } + if (!isRegexp(fields[1])) { + throw new SonarException("Invalid format. The second field does not define a regular expression: " + line); + } + } + + public static void decodeRangeOfLines(Pattern pattern, String field) { + if (StringUtils.equals(field, "*")) { + pattern.setCheckLines(false); + } else { + pattern.setCheckLines(true); + 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, '-'); + pattern.addLineRange(Integer.valueOf(range[0]), Integer.valueOf(range[1])); + } else { + pattern.addLine(Integer.valueOf(part)); + } + } + } + } + + @VisibleForTesting + boolean isLinesRange(String field) { + return StringUtils.equals(field, "*") || java.util.regex.Pattern.matches(LINE_RANGE_REGEXP, field); + } + + @VisibleForTesting + boolean isBlankOrComment(String line) { + return StringUtils.isBlank(line) || StringUtils.startsWith(line, "#"); + } + + @VisibleForTesting + boolean isResource(String field) { + return StringUtils.isNotBlank(field); + } + + @VisibleForTesting + boolean isRule(String field) { + return StringUtils.isNotBlank(field); + } + + @VisibleForTesting + boolean isRegexp(String field) { + return StringUtils.isNotBlank(field); + } +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializer.java new file mode 100644 index 00000000000..bfc3bdbe377 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializer.java @@ -0,0 +1,118 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.pattern; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.BatchExtension; +import org.sonar.api.config.Settings; +import org.sonar.plugins.core.issue.ignore.Constants; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.google.common.base.Objects.firstNonNull; +import static com.google.common.base.Strings.nullToEmpty; + +public class PatternsInitializer implements BatchExtension { + + private final Settings settings; + + private List multicriteriaPatterns; + private List blockPatterns; + private List allFilePatterns; + private Map extraPatternByResource = Maps.newHashMap(); + + public PatternsInitializer(Settings settings) { + this.settings = settings; + initPatterns(); + } + + public List getMulticriteriaPatterns() { + return multicriteriaPatterns; + } + + public List getBlockPatterns() { + return blockPatterns; + } + + public List getAllFilePatterns() { + return allFilePatterns; + } + + public Pattern getExtraPattern(String resource) { + return extraPatternByResource.get(resource.substring(resource.lastIndexOf(":") + 1)); + } + + @VisibleForTesting + protected final void initPatterns() { + multicriteriaPatterns = Lists.newArrayList(); + blockPatterns = Lists.newArrayList(); + allFilePatterns = Lists.newArrayList(); + + loadPatternsFromNewProperties(); + } + + private void loadPatternsFromNewProperties() { + // Patterns Multicriteria + String patternConf = StringUtils.defaultIfBlank(settings.getString(Constants.PATTERNS_MULTICRITERIA_KEY), ""); + for (String id : StringUtils.split(patternConf, ',')) { + String propPrefix = Constants.PATTERNS_MULTICRITERIA_KEY + "." + id + "."; + String resourceKeyPattern = settings.getString(propPrefix + Constants.RESOURCE_KEY); + String ruleKeyPattern = settings.getString(propPrefix + Constants.RULE_KEY); + Pattern pattern = new Pattern(firstNonNull(resourceKeyPattern, "*"), firstNonNull(ruleKeyPattern, "*")); + String lineRange = settings.getString(propPrefix + Constants.LINE_RANGE_KEY); + PatternDecoder.decodeRangeOfLines(pattern, firstNonNull(lineRange, "*")); + multicriteriaPatterns.add(pattern); + } + + // Patterns Block + patternConf = StringUtils.defaultIfBlank(settings.getString(Constants.PATTERNS_BLOCK_KEY), ""); + for (String id : StringUtils.split(patternConf, ',')) { + String propPrefix = Constants.PATTERNS_BLOCK_KEY + "." + id + "."; + String beginBlockRegexp = settings.getString(propPrefix + Constants.BEGIN_BLOCK_REGEXP); + String endBlockRegexp = settings.getString(propPrefix + Constants.END_BLOCK_REGEXP); + Pattern pattern = new Pattern().setBeginBlockRegexp(nullToEmpty(beginBlockRegexp)).setEndBlockRegexp(nullToEmpty(endBlockRegexp)); + blockPatterns.add(pattern); + } + + // Patterns All File + patternConf = StringUtils.defaultIfBlank(settings.getString(Constants.PATTERNS_ALLFILE_KEY), ""); + for (String id : StringUtils.split(patternConf, ',')) { + String propPrefix = Constants.PATTERNS_ALLFILE_KEY + "." + id + "."; + String allFileRegexp = settings.getString(propPrefix + Constants.FILE_REGEXP); + Pattern pattern = new Pattern().setAllFileRegexp(nullToEmpty(allFileRegexp)); + allFilePatterns.add(pattern); + } + } + + public void addPatternToExcludeResource(String resource) { + extraPatternByResource.put(resource, new Pattern(resource, "*").setCheckLines(false)); + } + + public void addPatternToExcludeLines(String resource, Set lineRanges) { + extraPatternByResource.put(resource, new Pattern(resource, "*", lineRanges)); + } + +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScanner.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScanner.java new file mode 100644 index 00000000000..e25ec67b047 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScanner.java @@ -0,0 +1,189 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.scanner; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.BatchExtension; +import org.sonar.plugins.core.issue.ignore.pattern.LineRange; +import org.sonar.plugins.core.issue.ignore.pattern.Pattern; +import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Set; + +public class RegexpScanner implements BatchExtension { + + private static final Logger LOG = LoggerFactory.getLogger(RegexpScanner.class); + + private PatternsInitializer patternsInitializer; + private List allFilePatterns; + private List blockMatchers; + + // fields to be reset at every new scan + private DoubleRegexpMatcher currentMatcher; + private int fileLength; + private List lineExclusions; + private LineExclusion currentLineExclusion; + + public RegexpScanner(PatternsInitializer patternsInitializer) { + this.patternsInitializer = patternsInitializer; + + lineExclusions = Lists.newArrayList(); + allFilePatterns = Lists.newArrayList(); + blockMatchers = Lists.newArrayList(); + + for (Pattern pattern : this.patternsInitializer.getAllFilePatterns()) { + allFilePatterns.add(java.util.regex.Pattern.compile(pattern.getAllFileRegexp())); + } + for (Pattern pattern : this.patternsInitializer.getBlockPatterns()) { + blockMatchers.add(new DoubleRegexpMatcher( + java.util.regex.Pattern.compile(pattern.getBeginBlockRegexp()), + java.util.regex.Pattern.compile(pattern.getEndBlockRegexp()))); + } + + init(); + } + + private void init() { + currentMatcher = null; + fileLength = 0; + lineExclusions.clear(); + currentLineExclusion = null; + } + + public void scan(String resource, File file, Charset sourcesEncoding) throws IOException { + LOG.debug("Scanning {}", resource); + init(); + + List lines = FileUtils.readLines(file, sourcesEncoding.name()); + int lineIndex = 0; + for (String line : lines) { + lineIndex++; + if (line.trim().length() == 0) { + continue; + } + + // first check the single regexp patterns that can be used to totally exclude a file + for (java.util.regex.Pattern pattern : allFilePatterns) { + if (pattern.matcher(line).find()) { + patternsInitializer.addPatternToExcludeResource(resource); + // nothing more to do on this file + LOG.debug("- Exclusion pattern '{}': every violation in this file will be ignored.", pattern); + return; + } + } + + // then check the double regexps if we're still here + checkDoubleRegexps(line, lineIndex); + } + + // now create the new line-based pattern for this file if there are exclusions + fileLength = lineIndex; + if (!lineExclusions.isEmpty()) { + Set lineRanges = convertLineExclusionsToLineRanges(); + LOG.debug("- Line exclusions found: {}", lineRanges); + patternsInitializer.addPatternToExcludeLines(resource, lineRanges); + } + } + + private Set convertLineExclusionsToLineRanges() { + Set lineRanges = Sets.newHashSet(); + for (LineExclusion lineExclusion : lineExclusions) { + lineRanges.add(lineExclusion.toLineRange()); + } + return lineRanges; + } + + private void checkDoubleRegexps(String line, int lineIndex) { + if (currentMatcher == null) { + for (DoubleRegexpMatcher matcher : blockMatchers) { + if (matcher.matchesFirstPattern(line)) { + startExclusion(lineIndex); + currentMatcher = matcher; + break; + } + } + } else { + if (currentMatcher.matchesSecondPattern(line)) { + endExclusion(lineIndex); + currentMatcher = null; + } + } + } + + private void startExclusion(int lineIndex) { + currentLineExclusion = new LineExclusion(lineIndex); + lineExclusions.add(currentLineExclusion); + } + + private void endExclusion(int lineIndex) { + currentLineExclusion.setEnd(lineIndex); + currentLineExclusion = null; + } + + private class LineExclusion { + + private int start; + private int end; + + LineExclusion(int start) { + this.start = start; + this.end = -1; + } + + void setEnd(int end) { + this.end = end; + } + + public LineRange toLineRange() { + return new LineRange(start, (end == -1 ? fileLength : end)); + } + + } + + private static class DoubleRegexpMatcher { + + private java.util.regex.Pattern firstPattern; + private java.util.regex.Pattern secondPattern; + + DoubleRegexpMatcher(java.util.regex.Pattern firstPattern, java.util.regex.Pattern secondPattern) { + this.firstPattern = firstPattern; + this.secondPattern = secondPattern; + } + + boolean matchesFirstPattern(String line) { + return firstPattern.matcher(line).find(); + } + + boolean matchesSecondPattern(String line) { + return secondPattern.matcher(line).find(); + } + + } + +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScanner.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScanner.java new file mode 100644 index 00000000000..55fec5877c9 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScanner.java @@ -0,0 +1,112 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.scanner; + +import org.sonar.api.batch.Phase; +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.JavaFile; +import org.sonar.api.resources.Project; +import org.sonar.api.scan.filesystem.FileQuery; +import org.sonar.api.scan.filesystem.ModuleFileSystem; +import org.sonar.api.utils.SonarException; +import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer; + +import java.io.File; +import java.nio.charset.Charset; +import java.util.List; + +@Phase(name = Phase.Name.PRE) +public final class SourceScanner implements Sensor { + + private final RegexpScanner regexpScanner; + private final PatternsInitializer patternsInitializer; + private final ModuleFileSystem fileSystem; + + public SourceScanner(RegexpScanner regexpScanner, PatternsInitializer patternsInitializer, ModuleFileSystem fileSystem) { + this.regexpScanner = regexpScanner; + this.patternsInitializer = patternsInitializer; + this.fileSystem = fileSystem; + } + + public boolean shouldExecuteOnProject(Project project) { + return patternsInitializer.getAllFilePatterns().size() > 0 || patternsInitializer.getBlockPatterns().size() > 0; + } + + /** + * {@inheritDoc} + */ + public void analyse(Project project, SensorContext context) { + parseDirs(project, false); + parseDirs(project, true); + } + + protected void parseDirs(Project project, boolean isTest) { + Charset sourcesEncoding = fileSystem.sourceCharset(); + + List files; + if (isTest) { + files = fileSystem.files(FileQuery.onTest().onLanguage(project.getLanguageKey())); + } else { + files = fileSystem.files(FileQuery.onSource().onLanguage(project.getLanguageKey())); + } + + for (File inputFile : files) { + try { + String resource = defineResource(inputFile, fileSystem, project, isTest); + if (resource != null) { + regexpScanner.scan(resource, inputFile, sourcesEncoding); + } + } catch (Exception e) { + throw new SonarException("Unable to read the source file : '" + inputFile.getAbsolutePath() + "' with the charset : '" + + sourcesEncoding.name() + "'.", e); + } + } + } + + /* + * This method is necessary because Java resources are not treated as every other resource... + */ + private String defineResource(File inputFile, ModuleFileSystem fileSystem, Project project, boolean isTest) { + if (Java.KEY.equals(project.getLanguageKey()) && Java.isJavaFile(inputFile)) { + List sourceDirs = null; + if (isTest) { + sourceDirs = fileSystem.testDirs(); + } else { + sourceDirs = fileSystem.sourceDirs(); + } + JavaFile file = JavaFile.fromIOFile(inputFile, sourceDirs, isTest); + if (file == null) { + return null; + } else { + return file.getKey(); + } + } + return inputFile.getPath(); + } + + @Override + public String toString() { + return "Ignore Issues - Source Scanner"; + } + +} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilterTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilterTest.java new file mode 100644 index 00000000000..0a37fc7132c --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilterTest.java @@ -0,0 +1,104 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.Rule; +import org.sonar.plugins.core.issue.ignore.pattern.Pattern; +import org.sonar.plugins.core.issue.ignore.pattern.PatternDecoder; +import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class IgnoreIssuesFilterTest { + + public static final Rule CHECKSTYLE_RULE = Rule.create("checkstyle", "MagicNumber", ""); + public static final String JAVA_FILE = "org.foo.Hello"; + + private PatternsInitializer patternsInitializer; + private IgnoreIssuesFilter filter; + + @Before + public void init() { + patternsInitializer = mock(PatternsInitializer.class); + when(patternsInitializer.getMulticriteriaPatterns()).thenReturn(Collections. emptyList()); + + filter = new IgnoreIssuesFilter(patternsInitializer); + } + + @Test + public void shouldBeDeactivatedWhenPropertyIsMissing() { + assertThat(filter.accept(create(CHECKSTYLE_RULE, JAVA_FILE, null))).isTrue(); + } + + @Test + public void shouldBeIgnoredWithStandardPatterns() throws IOException { + when(patternsInitializer.getMulticriteriaPatterns()).thenReturn(createPatterns("org.foo.Bar;*;*\norg.foo.Hello;checkstyle:MagicNumber;[15-200]")); + + assertThat(filter.accept(create(CHECKSTYLE_RULE, JAVA_FILE, 150))).isFalse(); + } + + @Test + public void shouldNotBeIgnoredWithStandardPatterns() throws IOException { + when(patternsInitializer.getMulticriteriaPatterns()).thenReturn(createPatterns("org.foo.Bar;*;*\norg.foo.Hello;checkstyle:MagicNumber;[15-200]")); + + assertThat(filter.accept(create(CHECKSTYLE_RULE, JAVA_FILE, 5))).isTrue(); + } + + @Test + public void shouldBeIgnoredWithExtraPattern() throws IOException { + when(patternsInitializer.getExtraPattern(JAVA_FILE)).thenReturn(createPatterns("org.foo.Hello;*;[15-200]").get(0)); + + assertThat(filter.accept(create(CHECKSTYLE_RULE, JAVA_FILE, 150))).isFalse(); + } + + @Test + public void shouldNotBeIgnoredWithExtraPattern() throws IOException { + when(patternsInitializer.getExtraPattern(JAVA_FILE)).thenReturn(createPatterns("org.foo.Hello;*;[15-200]").get(0)); + + assertThat(filter.accept(create(CHECKSTYLE_RULE, JAVA_FILE, 5))).isTrue(); + } + + private Issue create(Rule rule, String component, Integer line) { + Issue mockIssue = mock(Issue.class); + RuleKey ruleKey = null; + if (rule != null) { + ruleKey = rule.ruleKey(); + } + when(mockIssue.ruleKey()).thenReturn(ruleKey); + when(mockIssue.componentKey()).thenReturn(component); + when(mockIssue.line()).thenReturn(line); + return mockIssue; + } + + private List createPatterns(String line) { + return new PatternDecoder().decode(line); + } +} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPluginTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPluginTest.java new file mode 100644 index 00000000000..c478e714efe --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPluginTest.java @@ -0,0 +1,32 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore; + +import org.junit.Test; + +import static org.fest.assertions.Assertions.assertThat; + +public class IgnoreIssuesPluginTest { + @Test + public void justForCoverage() { + assertThat(new IgnoreIssuesPlugin().getExtensions()).hasSize(3 /* properties */ + 4 /* extensions */); + } +} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/LineRangeTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/LineRangeTest.java new file mode 100644 index 00000000000..3c4c02aedf4 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/LineRangeTest.java @@ -0,0 +1,73 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.pattern; + +import org.junit.Test; + +import static org.fest.assertions.Assertions.assertThat; + +public class LineRangeTest { + + @Test(expected = IllegalArgumentException.class) + public void lineRangeShouldBeOrdered() { + new LineRange(25, 12); + } + + @Test + public void shouldConvertLineRangeToLines() { + LineRange range = new LineRange(12, 15); + + assertThat(range.toLines()).containsOnly(12, 13, 14, 15); + } + + @Test + public void shouldTestInclusionInRangeOfLines() { + LineRange range = new LineRange(12, 15); + + assertThat(range.in(3)).isFalse(); + assertThat(range.in(12)).isTrue(); + assertThat(range.in(13)).isTrue(); + assertThat(range.in(14)).isTrue(); + assertThat(range.in(15)).isTrue(); + assertThat(range.in(16)).isFalse(); + } + + @Test + public void testToString() throws Exception { + assertThat(new LineRange(12, 15).toString()).isEqualTo("[12-15]"); + } + + @Test + public void testEquals() throws Exception { + LineRange range = new LineRange(12, 15); + assertThat(range).isEqualTo(range); + assertThat(range).isEqualTo(new LineRange(12, 15)); + assertThat(range).isNotEqualTo(new LineRange(12, 2000)); + assertThat(range).isNotEqualTo(new LineRange(1000, 2000)); + assertThat(range).isNotEqualTo(null); + assertThat(range).isNotEqualTo(new StringBuffer()); + } + + @Test + public void testHashCode() throws Exception { + assertThat(new LineRange(12, 15).hashCode()).isEqualTo(new LineRange(12, 15).hashCode()); + } +} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoderTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoderTest.java new file mode 100644 index 00000000000..3d904b43ec5 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoderTest.java @@ -0,0 +1,196 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.pattern; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.SonarException; + +import java.io.File; +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; + +public class PatternDecoderTest { + + private PatternDecoder decoder = new PatternDecoder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void shouldReadString() { + String patternsList = "# a comment followed by a blank line\n\n" + + "# suppress all violations\n" + + "*;*;*\n\n" + + "# exclude a Java file\n" + + "com.foo.Bar;*;*\n\n" + + "# exclude a Java package\n" + + "com.foo.*;*;*\n\n" + + "# exclude a specific rule\n" + + "*;checkstyle:IllegalRegexp;*\n\n" + + "# exclude a specific rule on a specific file\n" + + "com.foo.Bar;checkstyle:IllegalRegexp;*\n"; + List patterns = decoder.decode(patternsList); + + assertThat(patterns).hasSize(5); + } + + @Test + public void shouldCheckFormatOfResource() { + assertThat(decoder.isResource("")).isFalse(); + assertThat(decoder.isResource("*")).isTrue(); + assertThat(decoder.isResource("com.foo.*")).isTrue(); + } + + @Test + public void shouldCheckFormatOfRule() { + assertThat(decoder.isRule("")).isFalse(); + assertThat(decoder.isRule("*")).isTrue(); + assertThat(decoder.isRule("com.foo.*")).isTrue(); + } + + @Test + public void shouldCheckFormatOfLinesRange() { + assertThat(decoder.isLinesRange("")).isFalse(); + assertThat(decoder.isLinesRange(" ")).isFalse(); + assertThat(decoder.isLinesRange("12")).isFalse(); + assertThat(decoder.isLinesRange("12,212")).isFalse(); + + assertThat(decoder.isLinesRange("*")).isTrue(); + assertThat(decoder.isLinesRange("[]")).isTrue(); + assertThat(decoder.isLinesRange("[13]")).isTrue(); + assertThat(decoder.isLinesRange("[13,24]")).isTrue(); + assertThat(decoder.isLinesRange("[13,24,25-500]")).isTrue(); + assertThat(decoder.isLinesRange("[24-65]")).isTrue(); + assertThat(decoder.isLinesRange("[13,24-65,84-89,122]")).isTrue(); + } + + @Test + public void shouldReadStarPatterns() { + Pattern pattern = decoder.decodeLine("*;*;*"); + + assertThat(pattern.getResourcePattern().toString()).isEqualTo("*"); + assertThat(pattern.getRulePattern().toString()).isEqualTo("*"); + assertThat(pattern.isCheckLines()).isFalse(); + } + + @Test + public void shouldReadLineIds() { + Pattern pattern = decoder.decodeLine("*;*;[10,25,98]"); + + assertThat(pattern.isCheckLines()).isTrue(); + assertThat(pattern.getAllLines()).containsOnly(10, 25, 98); + } + + @Test + public void shouldReadRangeOfLineIds() { + Pattern pattern = decoder.decodeLine("*;*;[10-12,25,97-100]"); + + assertThat(pattern.isCheckLines()).isTrue(); + assertThat(pattern.getAllLines()).containsOnly(10, 11, 12, 25, 97, 98, 99, 100); + } + + @Test + public void shouldNotExcludeLines() { + // [] is different than * + // - all violations are excluded on * + // * no violations are excluded on [] + Pattern pattern = decoder.decodeLine("*;*;[]"); + + assertThat(pattern.isCheckLines()).isTrue(); + assertThat(pattern.getAllLines()).isEmpty(); + } + + @Test + public void shouldReadBlockPattern() { + Pattern pattern = decoder.decodeLine("SONAR-OFF;SONAR-ON"); + + assertThat(pattern.getResourcePattern()).isNull(); + assertThat(pattern.getBeginBlockRegexp()).isEqualTo("SONAR-OFF"); + assertThat(pattern.getEndBlockRegexp()).isEqualTo("SONAR-ON"); + } + + @Test + public void shouldReadAllFilePattern() { + Pattern pattern = decoder.decodeLine("SONAR-ALL-OFF"); + + assertThat(pattern.getResourcePattern()).isNull(); + assertThat(pattern.getAllFileRegexp()).isEqualTo("SONAR-ALL-OFF"); + } + + @Test + public void shouldFailIfUnexistingFile() { + thrown.expect(SonarException.class); + thrown.expectMessage("Fail to load the file"); + + decoder.decode(new File("foo")); + } + + @Test + public void shouldFailToReadUncorrectLine1() { + thrown.expect(SonarException.class); + thrown.expectMessage("Invalid format. The following line has more than 3 fields separated by comma"); + + decoder.decode(";;;;"); + } + + @Test + public void shouldFailToReadUncorrectLine3() { + thrown.expect(SonarException.class); + thrown.expectMessage("Invalid format. The first field does not define a resource pattern"); + + decoder.decode(";*;*"); + } + + @Test + public void shouldFailToReadUncorrectLine4() { + thrown.expect(SonarException.class); + thrown.expectMessage("Invalid format. The second field does not define a rule pattern"); + + decoder.decode("*;;*"); + } + + @Test + public void shouldFailToReadUncorrectLine5() { + thrown.expect(SonarException.class); + thrown.expectMessage("Invalid format. The third field does not define a range of lines"); + + decoder.decode("*;*;blabla"); + } + + @Test + public void shouldFailToReadUncorrectLine6() { + thrown.expect(SonarException.class); + thrown.expectMessage("Invalid format. The first field does not define a regular expression"); + + decoder.decode(";ON"); + } + + @Test + public void shouldFailToReadUncorrectLine7() { + thrown.expect(SonarException.class); + thrown.expectMessage("Invalid format. The second field does not define a regular expression"); + + decoder.decode("OFF;"); + } +} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternTest.java new file mode 100644 index 00000000000..da10e8545a3 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternTest.java @@ -0,0 +1,114 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.pattern; + +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.Rule; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class PatternTest { + + @Test + public void shouldMatchLines() { + Pattern pattern = new Pattern("*", "*"); + pattern.addLine(12).addLine(15).addLineRange(20, 25); + + 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 Pattern("org.foo.Bar", "*").matchResource(javaFile)).isTrue(); + assertThat(new Pattern("org.foo.*", "*").matchResource(javaFile)).isTrue(); + assertThat(new Pattern("*Bar", "*").matchResource(javaFile)).isTrue(); + assertThat(new Pattern("*", "*").matchResource(javaFile)).isTrue(); + assertThat(new Pattern("org.*.?ar", "*").matchResource(javaFile)).isTrue(); + + assertThat(new Pattern("org.other.Hello", "*").matchResource(javaFile)).isFalse(); + assertThat(new Pattern("org.foo.Hello", "*").matchResource(javaFile)).isFalse(); + assertThat(new Pattern("org.*.??ar", "*").matchResource(javaFile)).isFalse(); + assertThat(new Pattern("org.*.??ar", "*").matchResource(null)).isFalse(); + assertThat(new Pattern("org.*.??ar", "*").matchResource("plop")).isFalse(); + } + + @Test + public void shouldMatchRule() { + RuleKey rule = Rule.create("checkstyle", "IllegalRegexp", "").ruleKey(); + assertThat(new Pattern("*", "*").matchRule(rule)).isTrue(); + assertThat(new Pattern("*", "checkstyle:*").matchRule(rule)).isTrue(); + assertThat(new Pattern("*", "checkstyle:IllegalRegexp").matchRule(rule)).isTrue(); + assertThat(new Pattern("*", "checkstyle:Illegal*").matchRule(rule)).isTrue(); + assertThat(new Pattern("*", "*:*Illegal*").matchRule(rule)).isTrue(); + + assertThat(new Pattern("*", "pmd:IllegalRegexp").matchRule(rule)).isFalse(); + assertThat(new Pattern("*", "pmd:*").matchRule(rule)).isFalse(); + assertThat(new Pattern("*", "*:Foo*IllegalRegexp").matchRule(rule)).isFalse(); + } + + @Test + public void shouldMatchViolation() { + Rule rule = Rule.create("checkstyle", "IllegalRegexp", ""); + String javaFile = "org.foo.Bar"; + + Pattern pattern = new Pattern("*", "*"); + pattern.addLine(12); + + assertThat(pattern.match(create(rule, javaFile, null))).isTrue(); + assertThat(pattern.match(create(rule, javaFile, 12))).isTrue(); + assertThat(pattern.match(create((Rule) null, javaFile, 5))).isFalse(); + assertThat(pattern.match(create(rule, null, null))).isFalse(); + assertThat(pattern.match(create((Rule) null, null, null))).isFalse(); + } + + private Issue create(Rule rule, String component, Integer line) { + Issue mockIssue = mock(Issue.class); + RuleKey ruleKey = null; + if (rule != null) { + ruleKey = rule.ruleKey(); + } + when(mockIssue.ruleKey()).thenReturn(ruleKey); + when(mockIssue.componentKey()).thenReturn(component); + when(mockIssue.line()).thenReturn(line); + return mockIssue; + } + + @Test + public void shouldNotMatchNullRule() { + assertThat(new Pattern("*", "*").matchRule(null)).isFalse(); + } + + @Test + public void shouldPrintPatternToString() { + Pattern pattern = new Pattern("*", "checkstyle:*"); + + assertThat(pattern.toString()).isEqualTo("Pattern[resourcePattern=*,rulePattern=checkstyle:*,lines=[],lineRanges=[],beginBlockRegexp=,endBlockRegexp=,allFileRegexp=,checkLines=true]"); + } +} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializerTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializerTest.java new file mode 100644 index 00000000000..ec6a6529255 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializerTest.java @@ -0,0 +1,116 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.pattern; + +import com.google.common.collect.Sets; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.Settings; +import org.sonar.plugins.core.issue.ignore.Constants; +import org.sonar.plugins.core.issue.ignore.IgnoreIssuesPlugin; + +import java.util.Set; + +import static org.fest.assertions.Assertions.assertThat; + +public class PatternsInitializerTest { + + private PatternsInitializer patternsInitializer; + + private Settings settings; + + @Before + public void init() { + settings = new Settings(new PropertyDefinitions(new IgnoreIssuesPlugin())); + patternsInitializer = new PatternsInitializer(settings); + } + + @Test + public void testNoConfiguration() { + patternsInitializer.initPatterns(); + assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0); + } + + @Test + public void shouldReturnExtraPatternForResource() { + String file = "foo"; + patternsInitializer.addPatternToExcludeResource(file); + + Pattern extraPattern = patternsInitializer.getExtraPattern(file); + assertThat(extraPattern.matchResource(file)).isTrue(); + assertThat(extraPattern.isCheckLines()).isFalse(); + } + + @Test + public void shouldReturnExtraPatternForLinesOfResource() { + String file = "foo"; + Set lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(25, 28)); + patternsInitializer.addPatternToExcludeLines(file, lineRanges); + + Pattern extraPattern = patternsInitializer.getExtraPattern(file); + assertThat(extraPattern.matchResource(file)).isTrue(); + assertThat(extraPattern.getAllLines()).isEqualTo(Sets.newHashSet(25, 26, 27, 28)); + } + + @Test + public void shouldReturnMulticriteriaPattern() { + settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY, "1,2"); + settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".1." + Constants.RESOURCE_KEY, "org.foo.Bar"); + settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".1." + Constants.RULE_KEY, "*"); + settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".1." + Constants.RESOURCE_KEY, "*"); + settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".2." + Constants.LINE_RANGE_KEY, "org.foo.Hello"); + settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".2." + Constants.RULE_KEY, "checkstyle:MagicNumber"); + settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".2." + Constants.LINE_RANGE_KEY, "[15-200]"); + patternsInitializer.initPatterns(); + + assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(2); + assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(0); + assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(0); + } + + @Test + public void shouldReturnBlockPattern() { + settings.setProperty(Constants.PATTERNS_BLOCK_KEY, "1,2"); + settings.setProperty(Constants.PATTERNS_BLOCK_KEY + ".1." + Constants.BEGIN_BLOCK_REGEXP, "// SONAR-OFF"); + settings.setProperty(Constants.PATTERNS_BLOCK_KEY + ".1." + Constants.END_BLOCK_REGEXP, "// SONAR-ON"); + settings.setProperty(Constants.PATTERNS_BLOCK_KEY + ".2." + Constants.BEGIN_BLOCK_REGEXP, "// FOO-OFF"); + settings.setProperty(Constants.PATTERNS_BLOCK_KEY + ".2." + Constants.END_BLOCK_REGEXP, "// FOO-ON"); + patternsInitializer.initPatterns(); + + assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0); + assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(2); + assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(0); + } + + @Test + public void shouldReturnAllFilePattern() { + settings.setProperty(Constants.PATTERNS_ALLFILE_KEY, "1,2"); + settings.setProperty(Constants.PATTERNS_ALLFILE_KEY + ".1." + Constants.FILE_REGEXP, "@SONAR-IGNORE-ALL"); + settings.setProperty(Constants.PATTERNS_ALLFILE_KEY + ".2." + Constants.FILE_REGEXP, "//FOO-IGNORE-ALL"); + patternsInitializer.initPatterns(); + + assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0); + assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(0); + assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(2); + } +} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest.java new file mode 100644 index 00000000000..9454ac52e84 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest.java @@ -0,0 +1,150 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.scanner; + +import com.google.common.collect.Sets; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.plugins.core.issue.ignore.pattern.LineRange; +import org.sonar.plugins.core.issue.ignore.pattern.Pattern; +import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer; +import org.sonar.test.TestUtils; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Set; + +import static com.google.common.base.Charsets.UTF_8; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class RegexpScannerTest { + + private RegexpScanner regexpScanner; + + private String javaFile; + @Mock + private PatternsInitializer patternsInitializer; + @Mock + private Pattern allFilePattern; + @Mock + private Pattern blockPattern1; + @Mock + private Pattern blockPattern2; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + + when(allFilePattern.getAllFileRegexp()).thenReturn("@SONAR-IGNORE-ALL"); + when(blockPattern1.getBeginBlockRegexp()).thenReturn("// SONAR-OFF"); + when(blockPattern1.getEndBlockRegexp()).thenReturn("// SONAR-ON"); + when(blockPattern2.getBeginBlockRegexp()).thenReturn("// FOO-OFF"); + when(blockPattern2.getEndBlockRegexp()).thenReturn("// FOO-ON"); + when(patternsInitializer.getAllFilePatterns()).thenReturn(Arrays.asList(allFilePattern)); + when(patternsInitializer.getBlockPatterns()).thenReturn(Arrays.asList(blockPattern1, blockPattern2)); + + regexpScanner = new RegexpScanner(patternsInitializer); + verify(patternsInitializer, times(1)).getAllFilePatterns(); + verify(patternsInitializer, times(1)).getBlockPatterns(); + + javaFile = "org.sonar.test.MyFile"; + } + + @Test + public void shouldDoNothing() throws IOException { + regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-no-regexp.txt"), UTF_8); + + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeFile() throws IOException { + regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-single-regexp.txt"), UTF_8); + + verify(patternsInitializer, times(1)).addPatternToExcludeResource(javaFile); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeFileEvenIfAlsoDoubleRegexps() throws IOException { + regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-single-regexp-and-double-regexp.txt"), UTF_8); + + verify(patternsInitializer, times(1)).addPatternToExcludeResource(javaFile); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeLines() throws IOException { + regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-double-regexp.txt"), UTF_8); + + Set lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(21, 25)); + verify(patternsInitializer, times(1)).addPatternToExcludeLines(javaFile, lineRanges); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeLinesTillTheEnd() throws IOException { + regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-double-regexp-unfinished.txt"), UTF_8); + + Set lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(21, 34)); + verify(patternsInitializer, times(1)).addPatternToExcludeLines(javaFile, lineRanges); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeSeveralLineRanges() throws IOException { + regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-double-regexp-twice.txt"), UTF_8); + + Set lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(21, 25)); + lineRanges.add(new LineRange(29, 33)); + verify(patternsInitializer, times(1)).addPatternToExcludeLines(javaFile, lineRanges); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeLinesWithWrongOrder() throws IOException { + regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-double-regexp-wrong-order.txt"), UTF_8); + + Set lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(25, 35)); + verify(patternsInitializer, times(1)).addPatternToExcludeLines(javaFile, lineRanges); + verifyNoMoreInteractions(patternsInitializer); + } + + @Test + public void shouldAddPatternToExcludeLinesWithMess() throws IOException { + regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-double-regexp-mess.txt"), UTF_8); + + Set lineRanges = Sets.newHashSet(); + lineRanges.add(new LineRange(21, 29)); + verify(patternsInitializer, times(1)).addPatternToExcludeLines(javaFile, lineRanges); + verifyNoMoreInteractions(patternsInitializer); + } + +} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScannerTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScannerTest.java new file mode 100644 index 00000000000..954512edf8c --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScannerTest.java @@ -0,0 +1,178 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.issue.ignore.scanner; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.sonar.api.resources.Project; +import org.sonar.api.scan.filesystem.FileQuery; +import org.sonar.api.scan.filesystem.ModuleFileSystem; +import org.sonar.api.utils.SonarException; +import org.sonar.plugins.core.issue.ignore.pattern.Pattern; +import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import static com.google.common.base.Charsets.UTF_8; +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class SourceScannerTest { + + private SourceScanner scanner; + + @Mock + private RegexpScanner regexpScanner; + @Mock + private PatternsInitializer patternsInitializer; + @Mock + private Project project; + @Mock + private ModuleFileSystem fileSystem; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + + when(fileSystem.sourceCharset()).thenReturn(UTF_8); + + scanner = new SourceScanner(regexpScanner, patternsInitializer, fileSystem); + } + + @Test + public void testToString() throws Exception { + assertThat(scanner.toString()).isEqualTo("Ignore Issues - Source Scanner"); + } + + @Test + public void shouldExecute() throws IOException { + when(patternsInitializer.getAllFilePatterns()).thenReturn(Arrays.asList(new Pattern(), new Pattern())); + assertThat(scanner.shouldExecuteOnProject(null)).isTrue(); + + when(patternsInitializer.getAllFilePatterns()).thenReturn(Collections.emptyList()); + when(patternsInitializer.getBlockPatterns()).thenReturn(Arrays.asList(new Pattern(), new Pattern())); + assertThat(scanner.shouldExecuteOnProject(null)).isTrue(); + + when(patternsInitializer.getAllFilePatterns()).thenReturn(Collections.emptyList()); + when(patternsInitializer.getBlockPatterns()).thenReturn(Collections.emptyList()); + assertThat(scanner.shouldExecuteOnProject(null)).isFalse(); + } + + @Test + public void shouldAnalyseJavaProject() throws IOException { + File sourceFile = new File("src/main/java/Foo.java"); + File testFile = new File("src/test/java/FooTest.java"); + + when(project.getLanguageKey()).thenReturn("java"); + when(fileSystem.files(Mockito.isA(FileQuery.class))) + .thenReturn(Arrays.asList(sourceFile)) + .thenReturn(Arrays.asList(testFile)); + when(fileSystem.sourceDirs()).thenReturn(Arrays.asList(new File("src/main/java"))); + when(fileSystem.testDirs()).thenReturn(Arrays.asList(new File("src/test/java"))); + + scanner.analyse(project, null); + + verify(regexpScanner).scan("[default].Foo", sourceFile, UTF_8); + verify(regexpScanner).scan("[default].FooTest", testFile, UTF_8); + } + + @Test + public void shouldAnalyseOtherProject() throws IOException { + File sourceFile = new File("Foo.php"); + File testFile = new File("FooTest.php"); + + when(project.getLanguageKey()).thenReturn("php"); + when(fileSystem.files(Mockito.isA(FileQuery.class))) + .thenReturn(Arrays.asList(sourceFile)) + .thenReturn(Arrays.asList(testFile)); + + scanner.analyse(project, null); + + verify(regexpScanner).scan("Foo.php", sourceFile, UTF_8); + verify(regexpScanner).scan("FooTest.php", testFile, UTF_8); + } + + @Test + public void shouldAnalyseJavaProjectWithNonJavaFile() throws IOException { + File sourceFile = new File("src/main/java/Foo.java"); + File otherFile = new File("other.js"); + + when(project.getLanguageKey()).thenReturn("java"); + List empty = Collections.emptyList(); + when(fileSystem.files(Mockito.isA(FileQuery.class))) + .thenReturn(Arrays.asList(sourceFile, otherFile)) + .thenReturn(empty); + when(fileSystem.sourceDirs()).thenReturn(Arrays.asList(new File("src/main/java"))); + when(fileSystem.testDirs()).thenReturn(Arrays.asList(new File("src/test/java"))); + + scanner.analyse(project, null); + + verify(regexpScanner, never()).scan("other.js", sourceFile, UTF_8); + } + + @Test + public void shouldIgnoreInvalidFile() throws IOException { + File sourceFile = new File("invalid.java"); + + when(project.getLanguageKey()).thenReturn("java"); + List empty = Collections.emptyList(); + when(fileSystem.files(Mockito.isA(FileQuery.class))) + .thenReturn(Arrays.asList(sourceFile)) + .thenReturn(empty); + when(fileSystem.sourceDirs()).thenReturn(Arrays.asList(new File("src/main/java"))); + when(fileSystem.testDirs()).thenReturn(Arrays.asList(new File("src/test/java"))); + + scanner.analyse(project, null); + + verifyNoMoreInteractions(regexpScanner); + } + + @Test + public void shouldReportFailure() throws IOException { + File sourceFile = new File("Foo.php"); + + when(project.getLanguageKey()).thenReturn("php"); + when(fileSystem.files(Mockito.isA(FileQuery.class))).thenReturn(Arrays.asList(sourceFile)); + doThrow(new IOException("BUG")).when(regexpScanner).scan("Foo.php", sourceFile, UTF_8); + + thrown.expect(SonarException.class); + thrown.expectMessage("Unable to read the source file"); + + scanner.analyse(project, null); + } +} diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-mess.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-mess.txt new file mode 100644 index 00000000000..48d30c92f97 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-mess.txt @@ -0,0 +1,37 @@ +package org.sonar.plugins.switchoffviolations.pattern; + +import com.google.common.collect.Sets; + +import java.util.Set; + +/** + * + */ +public class LineRange { + int from, to; + + public LineRange(int from, int to) { + if (to < from) { + throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to); + } + this.from = from; + this.to = to; + } + + // SONAR-OFF + public boolean in(int lineId) { + return from <= lineId && lineId <= to; + } + // FOO-OFF + + public Set toLines() { + Set lines = Sets.newLinkedHashSet(); + // SONAR-ON + for (int index = from; index <= to; index++) { + lines.add(index); + } + // FOO-ON + return lines; + } + +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-twice.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-twice.txt new file mode 100644 index 00000000000..9ae63dc57f9 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-twice.txt @@ -0,0 +1,37 @@ +package org.sonar.plugins.switchoffviolations.pattern; + +import com.google.common.collect.Sets; + +import java.util.Set; + +/** + * + */ +public class LineRange { + int from, to; + + public LineRange(int from, int to) { + if (to < from) { + throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to); + } + this.from = from; + this.to = to; + } + + // SONAR-OFF + public boolean in(int lineId) { + return from <= lineId && lineId <= to; + } + // SONAR-ON + + public Set toLines() { + Set lines = Sets.newLinkedHashSet(); + // FOO-OFF + for (int index = from; index <= to; index++) { + lines.add(index); + } + // FOO-ON + return lines; + } + +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-unfinished.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-unfinished.txt new file mode 100644 index 00000000000..dd7656180ab --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-unfinished.txt @@ -0,0 +1,34 @@ +package org.sonar.plugins.switchoffviolations.pattern; + +import com.google.common.collect.Sets; + +import java.util.Set; + +/** + * + */ +public class LineRange { + int from, to; + + public LineRange(int from, int to) { + if (to < from) { + throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to); + } + this.from = from; + this.to = to; + } + + // SONAR-OFF + public boolean in(int lineId) { + return from <= lineId && lineId <= to; + } + + public Set toLines() { + Set lines = Sets.newLinkedHashSet(); + for (int index = from; index <= to; index++) { + lines.add(index); + } + return lines; + } + +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-wrong-order.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-wrong-order.txt new file mode 100644 index 00000000000..7cac0b98aed --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-wrong-order.txt @@ -0,0 +1,35 @@ +package org.sonar.plugins.switchoffviolations.pattern; + +import com.google.common.collect.Sets; + +import java.util.Set; + +/** + * + */ +public class LineRange { + int from, to; + + public LineRange(int from, int to) { + if (to < from) { + throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to); + } + this.from = from; + this.to = to; + } + + // SONAR-ON + public boolean in(int lineId) { + return from <= lineId && lineId <= to; + } + // SONAR-OFF + + public Set toLines() { + Set lines = Sets.newLinkedHashSet(); + for (int index = from; index <= to; index++) { + lines.add(index); + } + return lines; + } + +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp.txt new file mode 100644 index 00000000000..002169fe031 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp.txt @@ -0,0 +1,35 @@ +package org.sonar.plugins.switchoffviolations.pattern; + +import com.google.common.collect.Sets; + +import java.util.Set; + +/** + * + */ +public class LineRange { + int from, to; + + public LineRange(int from, int to) { + if (to < from) { + throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to); + } + this.from = from; + this.to = to; + } + + // SONAR-OFF + public boolean in(int lineId) { + return from <= lineId && lineId <= to; + } + // SONAR-ON + + public Set toLines() { + Set lines = Sets.newLinkedHashSet(); + for (int index = from; index <= to; index++) { + lines.add(index); + } + return lines; + } + +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-no-regexp.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-no-regexp.txt new file mode 100644 index 00000000000..f18fa5b90ad --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-no-regexp.txt @@ -0,0 +1,33 @@ +package org.sonar.plugins.switchoffviolations.pattern; + +import com.google.common.collect.Sets; + +import java.util.Set; + +/** + * + */ +public class LineRange { + int from, to; + + public LineRange(int from, int to) { + if (to < from) { + throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to); + } + this.from = from; + this.to = to; + } + + public boolean in(int lineId) { + return from <= lineId && lineId <= to; + } + + public Set toLines() { + Set lines = Sets.newLinkedHashSet(); + for (int index = from; index <= to; index++) { + lines.add(index); + } + return lines; + } + +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp-and-double-regexp.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp-and-double-regexp.txt new file mode 100644 index 00000000000..e09ecd7a323 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp-and-double-regexp.txt @@ -0,0 +1,36 @@ +package org.sonar.plugins.switchoffviolations.pattern; + +import com.google.common.collect.Sets; + + // SONAR-OFF + +import java.util.Set; + +/** + * @SONAR-IGNORE-ALL + */ +public class LineRange { + int from, to; + + public LineRange(int from, int to) { + if (to < from) { + throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to); + } + this.from = from; + this.to = to; + } + + public boolean in(int lineId) { + return from <= lineId && lineId <= to; + } + // SONAR-ON + + public Set toLines() { + Set lines = Sets.newLinkedHashSet(); + for (int index = from; index <= to; index++) { + lines.add(index); + } + return lines; + } + +} \ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp.txt new file mode 100644 index 00000000000..ef135ebc50c --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp.txt @@ -0,0 +1,33 @@ +package org.sonar.plugins.switchoffviolations.pattern; + +import com.google.common.collect.Sets; + +import java.util.Set; + +/** + * @SONAR-IGNORE-ALL + */ +public class LineRange { + int from, to; + + public LineRange(int from, int to) { + if (to < from) { + throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to); + } + this.from = from; + this.to = to; + } + + public boolean in(int lineId) { + return from <= lineId && lineId <= to; + } + + public Set toLines() { + Set lines = Sets.newLinkedHashSet(); + for (int index = from; index <= to; index++) { + lines.add(index); + } + return lines; + } + +} \ No newline at end of file