import org.sonar.api.checks.NoSonarFilter;
import org.sonar.api.notifications.NotificationDispatcherMetadata;
import org.sonar.api.resources.Java;
+import org.sonar.batch.issue.IssuesDecorator;
+import org.sonar.batch.issue.IssuesDensityDecorator;
+import org.sonar.batch.issue.WeightedIssuesDecorator;
import org.sonar.core.timemachine.Periods;
import org.sonar.plugins.core.batch.IndexProjectPostJob;
import org.sonar.plugins.core.charts.DistributionAreaChart;
CheckAlertThresholds.class,
GenerateAlertEvents.class,
ViolationsDecorator.class,
+ IssuesDecorator.class,
WeightedViolationsDecorator.class,
+ WeightedIssuesDecorator.class,
ViolationsDensityDecorator.class,
+ IssuesDensityDecorator.class,
LineCoverageDecorator.class,
CoverageDecorator.class,
BranchCoverageDecorator.class,
if (source != null && resource != null && hasLastScan) {
String referenceSource = lastSnapshots.getSource(resource);
if (referenceSource != null) {
- HashedSequence<StringText> hashedReference = HashedSequence.wrap(new StringText(referenceSource), StringTextComparator.IGNORE_WHITESPACE);
- HashedSequence<StringText> hashedSource = HashedSequence.wrap(new StringText(source), StringTextComparator.IGNORE_WHITESPACE);
- HashedSequenceComparator<StringText> hashedComparator = new HashedSequenceComparator<StringText>(StringTextComparator.IGNORE_WHITESPACE);
-
- ViolationTrackingBlocksRecognizer rec = new ViolationTrackingBlocksRecognizer(hashedReference, hashedSource, hashedComparator);
-
- Multimap<Integer, DefaultIssue> newIssuesByLines = newIssuesByLines(newIssues, rec);
- Multimap<Integer, IssueDto> lastIssuesByLines = lastIssuesByLines(unmappedLastIssues, rec);
-
- RollingHashSequence<HashedSequence<StringText>> a = RollingHashSequence.wrap(hashedReference, hashedComparator, 5);
- RollingHashSequence<HashedSequence<StringText>> b = RollingHashSequence.wrap(hashedSource, hashedComparator, 5);
- RollingHashSequenceComparator<HashedSequence<StringText>> cmp = new RollingHashSequenceComparator<HashedSequence<StringText>>(hashedComparator);
-
- Map<Integer, HashOccurrence> map = Maps.newHashMap();
-
- for (Integer line : lastIssuesByLines.keySet()) {
- int hash = cmp.hash(a, line - 1);
- HashOccurrence hashOccurrence = map.get(hash);
- if (hashOccurrence == null) {
- // first occurrence in A
- hashOccurrence = new HashOccurrence();
- hashOccurrence.lineA = line;
- hashOccurrence.countA = 1;
- map.put(hash, hashOccurrence);
- } else {
- hashOccurrence.countA++;
- }
- }
-
- for (Integer line : newIssuesByLines.keySet()) {
- int hash = cmp.hash(b, line - 1);
- HashOccurrence hashOccurrence = map.get(hash);
- if (hashOccurrence != null) {
- hashOccurrence.lineB = line;
- hashOccurrence.countB++;
- }
- }
-
- for (HashOccurrence hashOccurrence : map.values()) {
- if (hashOccurrence.countA == 1 && hashOccurrence.countB == 1) {
- // Guaranteed that lineA has been moved to lineB, so we can map all issues on lineA to all issues on lineB
- map(newIssuesByLines.get(hashOccurrence.lineB), lastIssuesByLines.get(hashOccurrence.lineA), lastIssuesByRule);
- lastIssuesByLines.removeAll(hashOccurrence.lineA);
- newIssuesByLines.removeAll(hashOccurrence.lineB);
- }
- }
-
- // Check if remaining number of lines exceeds threshold
- if (lastIssuesByLines.keySet().size() * newIssuesByLines.keySet().size() < 250000) {
- List<LinePair> possibleLinePairs = Lists.newArrayList();
- for (Integer oldLine : lastIssuesByLines.keySet()) {
- for (Integer newLine : newIssuesByLines.keySet()) {
- int weight = rec.computeLengthOfMaximalBlock(oldLine - 1, newLine - 1);
- possibleLinePairs.add(new LinePair(oldLine, newLine, weight));
- }
- }
- Collections.sort(possibleLinePairs, LINE_PAIR_COMPARATOR);
- for (LinePair linePair : possibleLinePairs) {
- // High probability that lineA has been moved to lineB, so we can map all Issues on lineA to all Issues on lineB
- map(newIssuesByLines.get(linePair.lineB), lastIssuesByLines.get(linePair.lineA), lastIssuesByRule);
- }
- }
- }
- }
-
- // Try then to match issues on same rule with same message and with same checksum
- for (DefaultIssue newIssue : newIssues) {
- if (isNotAlreadyMapped(newIssue)) {
- mapIssue(newIssue,
- findLastIssueWithSameChecksumAndMessage(newIssue, lastIssuesByRule.get(getRule(newIssue))),
- lastIssuesByRule, referenceIssuesMap);
- }
- }
-
- // Try then to match issues on same rule with same line and with same message
- for (DefaultIssue newIssue : newIssues) {
- if (isNotAlreadyMapped(newIssue)) {
- mapIssue(newIssue,
- findLastIssueWithSameLineAndMessage(newIssue, lastIssuesByRule.get(getRule(newIssue))),
- lastIssuesByRule, referenceIssuesMap);
- }
- }
-
- // Last check: match issue if same rule and same checksum but different line and different message
- // See SONAR-2812
- for (DefaultIssue newIssue : newIssues) {
- if (isNotAlreadyMapped(newIssue)) {
- mapIssue(newIssue,
- findLastIssueWithSameChecksum(newIssue, lastIssuesByRule.get(getRule(newIssue))),
- lastIssuesByRule, referenceIssuesMap);
+ mapNewissues(referenceSource, newIssues, lastIssuesByRule, source);
}
}
+ mapIssuesOnSameRule(newIssues, lastIssuesByRule);
}
unmappedLastIssues.clear();
}
}
+ private void mapNewissues(String referenceSource, List<DefaultIssue> newIssues, Multimap<Integer, IssueDto> lastIssuesByRule, String source){
+ HashedSequence<StringText> hashedReference = HashedSequence.wrap(new StringText(referenceSource), StringTextComparator.IGNORE_WHITESPACE);
+ HashedSequence<StringText> hashedSource = HashedSequence.wrap(new StringText(source), StringTextComparator.IGNORE_WHITESPACE);
+ HashedSequenceComparator<StringText> hashedComparator = new HashedSequenceComparator<StringText>(StringTextComparator.IGNORE_WHITESPACE);
+
+ ViolationTrackingBlocksRecognizer rec = new ViolationTrackingBlocksRecognizer(hashedReference, hashedSource, hashedComparator);
+
+ Multimap<Integer, DefaultIssue> newIssuesByLines = newIssuesByLines(newIssues, rec);
+ Multimap<Integer, IssueDto> lastIssuesByLines = lastIssuesByLines(unmappedLastIssues, rec);
+
+ RollingHashSequence<HashedSequence<StringText>> a = RollingHashSequence.wrap(hashedReference, hashedComparator, 5);
+ RollingHashSequence<HashedSequence<StringText>> b = RollingHashSequence.wrap(hashedSource, hashedComparator, 5);
+ RollingHashSequenceComparator<HashedSequence<StringText>> cmp = new RollingHashSequenceComparator<HashedSequence<StringText>>(hashedComparator);
+
+ Map<Integer, HashOccurrence> map = Maps.newHashMap();
+
+ for (Integer line : lastIssuesByLines.keySet()) {
+ int hash = cmp.hash(a, line - 1);
+ HashOccurrence hashOccurrence = map.get(hash);
+ if (hashOccurrence == null) {
+ // first occurrence in A
+ hashOccurrence = new HashOccurrence();
+ hashOccurrence.lineA = line;
+ hashOccurrence.countA = 1;
+ map.put(hash, hashOccurrence);
+ } else {
+ hashOccurrence.countA++;
+ }
+ }
+
+ for (Integer line : newIssuesByLines.keySet()) {
+ int hash = cmp.hash(b, line - 1);
+ HashOccurrence hashOccurrence = map.get(hash);
+ if (hashOccurrence != null) {
+ hashOccurrence.lineB = line;
+ hashOccurrence.countB++;
+ }
+ }
+
+ for (HashOccurrence hashOccurrence : map.values()) {
+ if (hashOccurrence.countA == 1 && hashOccurrence.countB == 1) {
+ // Guaranteed that lineA has been moved to lineB, so we can map all issues on lineA to all issues on lineB
+ map(newIssuesByLines.get(hashOccurrence.lineB), lastIssuesByLines.get(hashOccurrence.lineA), lastIssuesByRule);
+ lastIssuesByLines.removeAll(hashOccurrence.lineA);
+ newIssuesByLines.removeAll(hashOccurrence.lineB);
+ }
+ }
+
+ // Check if remaining number of lines exceeds threshold
+ if (lastIssuesByLines.keySet().size() * newIssuesByLines.keySet().size() < 250000) {
+ List<LinePair> possibleLinePairs = Lists.newArrayList();
+ for (Integer oldLine : lastIssuesByLines.keySet()) {
+ for (Integer newLine : newIssuesByLines.keySet()) {
+ int weight = rec.computeLengthOfMaximalBlock(oldLine - 1, newLine - 1);
+ possibleLinePairs.add(new LinePair(oldLine, newLine, weight));
+ }
+ }
+ Collections.sort(possibleLinePairs, LINE_PAIR_COMPARATOR);
+ for (LinePair linePair : possibleLinePairs) {
+ // High probability that lineA has been moved to lineB, so we can map all Issues on lineA to all Issues on lineB
+ map(newIssuesByLines.get(linePair.lineB), lastIssuesByLines.get(linePair.lineA), lastIssuesByRule);
+ }
+ }
+ }
+
+ private void mapIssuesOnSameRule(List<DefaultIssue> newIssues, Multimap<Integer, IssueDto> lastIssuesByRule){
+ // Try then to match issues on same rule with same message and with same checksum
+ for (DefaultIssue newIssue : newIssues) {
+ if (isNotAlreadyMapped(newIssue)) {
+ mapIssue(newIssue,
+ findLastIssueWithSameChecksumAndMessage(newIssue, lastIssuesByRule.get(getRule(newIssue))),
+ lastIssuesByRule, referenceIssuesMap);
+ }
+ }
+
+ // Try then to match issues on same rule with same line and with same message
+ for (DefaultIssue newIssue : newIssues) {
+ if (isNotAlreadyMapped(newIssue)) {
+ mapIssue(newIssue,
+ findLastIssueWithSameLineAndMessage(newIssue, lastIssuesByRule.get(getRule(newIssue))),
+ lastIssuesByRule, referenceIssuesMap);
+ }
+ }
+
+ // Last check: match issue if same rule and same checksum but different line and different message
+ // See SONAR-2812
+ for (DefaultIssue newIssue : newIssues) {
+ if (isNotAlreadyMapped(newIssue)) {
+ mapIssue(newIssue,
+ findLastIssueWithSameChecksum(newIssue, lastIssuesByRule.get(getRule(newIssue))),
+ lastIssuesByRule, referenceIssuesMap);
+ }
+ }
+ }
+
@VisibleForTesting
IssueDto getReferenceIssue(DefaultIssue issue) {
return referenceIssuesMap.get(issue);
private void mapIssue(DefaultIssue newIssue, IssueDto pastIssue, Multimap<Integer, IssueDto> lastIssuesByRule, Map<DefaultIssue, IssueDto> issueMap) {
if (pastIssue != null) {
+ newIssue.setKey(pastIssue.getUuid());
+ if (pastIssue.isManualSeverity()) {
+ newIssue.setSeverity(pastIssue.getSeverity());
+ }
+
newIssue.setCreatedAt(pastIssue.getCreatedAt());
newIssue.setUpdatedAt(project.getAnalysisDate());
- newIssue.setKey(pastIssue.getUuid());
+ newIssue.setNew(false);
+
// TODO
// newIssue.setPersonId(pastIssue.getPersonId());
- newIssue.setNew(false);
lastIssuesByRule.remove(getRule(newIssue), pastIssue);
issueMap.put(newIssue, pastIssue);
import com.google.common.collect.Multiset;
import com.google.common.collect.TreeMultiset;
import org.sonar.api.CoreProperties;
-import org.sonar.api.Properties;
-import org.sonar.api.Property;
import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.DependedUpon;
import java.util.List;
import java.util.Map;
-@Properties(
- @Property(
- key = CoreProperties.CORE_RULE_WEIGHTS_PROPERTY,
- defaultValue = CoreProperties.CORE_RULE_WEIGHTS_DEFAULT_VALUE,
- name = "Rules weight",
- description = "A weight is associated to each severity to calculate the Rules Compliance Index.",
- project = false,
- global = true,
- category = CoreProperties.CATEGORY_GENERAL)
-)
public class WeightedViolationsDecorator implements Decorator {
private Settings settings;
assertThat(newIssue.isNew()).isTrue();
}
+ @Test
+ public void should_set_severity_if_severity_has_been_changed_by_user() {
+ DefaultIssue newIssue = newDefaultIssue("message", 1, "repoKey", "ruleKey", "checksum").setSeverity("MAJOR");
+ IssueDto referenceIssue = newReferenceIssue("message", 1, 1, "checksum").setSeverity("MINOR").setManualSeverity(true);
+
+ Map<DefaultIssue, IssueDto> mapping = decorator.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue));
+ assertThat(newIssue.severity()).isEqualTo("MINOR");
+ }
+
@Test
public void should_copy_date_when_not_new() {
DefaultIssue newIssue = newDefaultIssue("message", 1, "repoKey", "ruleKey", "checksum");
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.issue;
+
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multiset;
+import org.sonar.api.batch.*;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.issue.Issuable;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.measures.*;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RulePriority;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+@DependsUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING)
+public class IssuesDecorator implements Decorator {
+
+ private final ResourcePerspectives perspectives;
+ private final RuleFinder rulefinder;
+
+ public IssuesDecorator(ResourcePerspectives perspectives, RuleFinder rulefinder) {
+ this.perspectives = perspectives;
+ this.rulefinder = rulefinder;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependedUpon
+ public List<Metric> generatesIssuesMetrics() {
+ return Arrays.asList(CoreMetrics.ISSUES,
+ CoreMetrics.BLOCKER_ISSUES,
+ CoreMetrics.CRITICAL_ISSUES,
+ CoreMetrics.MAJOR_ISSUES,
+ CoreMetrics.MINOR_ISSUES,
+ CoreMetrics.INFO_ISSUES);
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ Issuable issuable = perspectives.as(Issuable.class, context.getResource());
+ Collection<Issue> issues = issuable.issues();
+ computeTotalIssues(context, issues);
+ computeIssuesPerSeverities(context, issues);
+ computeIssuesPerRules(context, issues);
+ }
+
+ private void computeTotalIssues(DecoratorContext context, Collection<Issue> issues) {
+ if (context.getMeasure(CoreMetrics.ISSUES) == null) {
+ Collection<Measure> childrenIssues = context.getChildrenMeasures(CoreMetrics.ISSUES);
+ Double sum = MeasureUtils.sum(true, childrenIssues);
+ context.saveMeasure(CoreMetrics.ISSUES, sum + issues.size());
+ }
+ }
+
+ private void computeIssuesPerSeverities(DecoratorContext context, Collection<Issue> issues) {
+ Multiset<RulePriority> severitiesBag = HashMultiset.create();
+ for (Issue issue : issues) {
+ severitiesBag.add(RulePriority.valueOf(issue.severity()));
+ }
+
+ for (RulePriority ruleSeverity : RulePriority.values()) {
+ Metric metric = SeverityUtils.severityToIssueMetric(ruleSeverity);
+ if (context.getMeasure(metric) == null) {
+ Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.metric(metric));
+ int sum = MeasureUtils.sum(true, children).intValue() + severitiesBag.count(ruleSeverity);
+ context.saveMeasure(metric, (double) sum);
+ }
+ }
+ }
+
+ private void computeIssuesPerRules(DecoratorContext context, Collection<Issue> issues) {
+ Map<RulePriority, Multiset<Rule>> rulesPerSeverity = Maps.newHashMap();
+ for (Issue issue : issues) {
+ Multiset<Rule> rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity()));
+ rulesBag.add(rulefinder.findByKey(issue.ruleRepositoryKey(), issue.ruleKey()));
+ }
+
+ for (RulePriority severity : RulePriority.values()) {
+ Metric metric = SeverityUtils.severityToIssueMetric(severity);
+
+ Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
+ for (Measure child : children) {
+ RuleMeasure childRuleMeasure = (RuleMeasure) child;
+ Rule rule = childRuleMeasure.getRule();
+ if (rule != null && MeasureUtils.hasValue(childRuleMeasure)) {
+ Multiset<Rule> rulesBag = initRules(rulesPerSeverity, severity);
+ rulesBag.add(rule, childRuleMeasure.getIntValue());
+ }
+ }
+
+ Multiset<Rule> rulesBag = rulesPerSeverity.get(severity);
+ if (rulesBag != null) {
+ for (Multiset.Entry<Rule> entry : rulesBag.entrySet()) {
+ RuleMeasure measure = RuleMeasure.createForRule(metric, entry.getElement(), (double) entry.getCount());
+ measure.setSeverity(severity);
+ context.saveMeasure(measure);
+ }
+ }
+ }
+ }
+
+ private Multiset<Rule> initRules(Map<RulePriority, Multiset<Rule>> rulesPerSeverity, RulePriority severity) {
+ Multiset<Rule> rulesBag = rulesPerSeverity.get(severity);
+ if (rulesBag == null) {
+ rulesBag = HashMultiset.create();
+ rulesPerSeverity.put(severity, rulesBag);
+ }
+ return rulesBag;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.issue;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class IssuesDensityDecorator implements Decorator {
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependsUpon
+ public List<Metric> dependsUponWeightedIissuesAndNcloc() {
+ return Arrays.asList(CoreMetrics.WEIGHTED_ISSUES, CoreMetrics.NCLOC);
+ }
+
+ @DependedUpon
+ public Metric generatesIssuesDensity() {
+ return CoreMetrics.ISSUES_DENSITY;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (shouldDecorateResource(context)) {
+ decorateDensity(context);
+ }
+ }
+
+ protected boolean shouldDecorateResource(DecoratorContext context) {
+ return context.getMeasure(CoreMetrics.ISSUES_DENSITY) == null;
+ }
+
+ private void decorateDensity(DecoratorContext context) {
+ Measure ncloc = context.getMeasure(CoreMetrics.NCLOC);
+ if (MeasureUtils.hasValue(ncloc) && ncloc.getValue() > 0.0) {
+ saveDensity(context, ncloc.getValue().intValue());
+ }
+ }
+
+ private void saveDensity(DecoratorContext context, int ncloc) {
+ Measure debt = context.getMeasure(CoreMetrics.WEIGHTED_ISSUES);
+ Integer debtValue = 0;
+ if (MeasureUtils.hasValue(debt)) {
+ debtValue = debt.getValue().intValue();
+ }
+ double density = calculate(debtValue, ncloc);
+ context.saveMeasure(CoreMetrics.ISSUES_DENSITY, density);
+ }
+
+ protected static double calculate(int debt, int ncloc) {
+ double rci = (1.0 - ((double) debt / (double) ncloc)) * 100.0;
+ rci = Math.max(rci, 0.0);
+ return rci;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.issue;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.rules.RulePriority;
+
+final class SeverityUtils {
+ private SeverityUtils() {
+ // only static methods
+ }
+
+ static Metric severityToIssueMetric(RulePriority severity) {
+ Metric metric;
+ if (severity.equals(RulePriority.BLOCKER)) {
+ metric = CoreMetrics.BLOCKER_ISSUES;
+ } else if (severity.equals(RulePriority.CRITICAL)) {
+ metric = CoreMetrics.CRITICAL_ISSUES;
+ } else if (severity.equals(RulePriority.MAJOR)) {
+ metric = CoreMetrics.MAJOR_ISSUES;
+ } else if (severity.equals(RulePriority.MINOR)) {
+ metric = CoreMetrics.MINOR_ISSUES;
+ } else if (severity.equals(RulePriority.INFO)) {
+ metric = CoreMetrics.INFO_ISSUES;
+ } else {
+ throw new IllegalArgumentException("Unsupported severity: " + severity);
+ }
+ return metric;
+ }
+}
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.issue;
+
+import com.google.common.collect.Multiset;
+import com.google.common.collect.TreeMultiset;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.Properties;
+import org.sonar.api.Property;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.config.Settings;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.utils.KeyValueFormat;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+@Properties(
+ @Property(
+ key = CoreProperties.CORE_RULE_WEIGHTS_PROPERTY,
+ defaultValue = CoreProperties.CORE_RULE_WEIGHTS_DEFAULT_VALUE,
+ name = "Rules weight",
+ description = "A weight is associated to each severity to calculate the Rules Compliance Index.",
+ project = false,
+ global = true,
+ category = CoreProperties.CATEGORY_GENERAL)
+)
+public class WeightedIssuesDecorator implements Decorator {
+
+ private Settings settings;
+ private Map<RulePriority, Integer> weightsBySeverity;
+
+ public WeightedIssuesDecorator(Settings settings) {
+ this.settings = settings;
+ }
+
+ @DependsUpon
+ public List<Metric> dependsUponIssues() {
+ return Arrays.asList(CoreMetrics.BLOCKER_ISSUES, CoreMetrics.CRITICAL_ISSUES,
+ CoreMetrics.MAJOR_ISSUES, CoreMetrics.MINOR_ISSUES, CoreMetrics.INFO_ISSUES);
+ }
+
+ @DependedUpon
+ public Metric generatesWeightedIssues() {
+ return CoreMetrics.WEIGHTED_ISSUES;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void start() {
+ weightsBySeverity = getWeights(settings);
+ }
+
+ Map<RulePriority, Integer> getWeightsBySeverity() {
+ return weightsBySeverity;
+ }
+
+ static Map<RulePriority, Integer> getWeights(final Settings settings) {
+ String value = settings.getString(CoreProperties.CORE_RULE_WEIGHTS_PROPERTY);
+
+ Map<RulePriority, Integer> weights = KeyValueFormat.parse(value, KeyValueFormat.newPriorityConverter(), KeyValueFormat.newIntegerConverter());
+
+ for (RulePriority priority : RulePriority.values()) {
+ if (!weights.containsKey(priority)) {
+ weights.put(priority, 1);
+ }
+ }
+ return weights;
+ }
+
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ decorate(context);
+ }
+
+ void decorate(DecoratorContext context) {
+ double debt = 0.0;
+ Multiset<RulePriority> distribution = TreeMultiset.create();
+
+ for (RulePriority severity : RulePriority.values()) {
+ Measure measure = context.getMeasure(SeverityUtils.severityToIssueMetric(severity));
+ if (measure != null && MeasureUtils.hasValue(measure)) {
+ distribution.add(severity, measure.getIntValue());
+ double add = weightsBySeverity.get(severity) * measure.getIntValue();
+ debt += add;
+ }
+ }
+
+ Measure debtMeasure = new Measure(CoreMetrics.WEIGHTED_ISSUES, debt, KeyValueFormat.format(distribution));
+ context.saveMeasure(debtMeasure);
+ }
+
+}
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.issue;
+
+import com.google.common.collect.Lists;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.issue.Issuable;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilter;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.test.IsRuleMeasure;
+import org.sonar.core.issue.DefaultIssue;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyDouble;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+
+public class IssuesDecoratorTest {
+
+ private Rule ruleA1;
+ private Rule ruleA2;
+ private Rule ruleB1;
+ private IssuesDecorator decorator;
+ private Resource resource;
+ private DecoratorContext context;
+ private Issuable issuable;
+ private RuleFinder rulefinder;
+
+ @Before
+ public void before() {
+ ruleA1 = Rule.create().setRepositoryKey("ruleA1").setKey("ruleA1").setName("nameA1");
+ ruleA2 = Rule.create().setRepositoryKey("ruleA2").setKey("ruleA2").setName("nameA2");
+ ruleB1 = Rule.create().setRepositoryKey("ruleB1").setKey("ruleB1").setName("nameB1");
+
+ rulefinder = mock(RuleFinder.class);
+ when(rulefinder.findByKey(ruleA1.getRepositoryKey(), ruleA1.getKey())).thenReturn(ruleA1);
+ when(rulefinder.findByKey(ruleA2.getRepositoryKey(), ruleA2.getKey())).thenReturn(ruleA2);
+ when(rulefinder.findByKey(ruleB1.getRepositoryKey(), ruleB1.getKey())).thenReturn(ruleB1);
+
+ resource = mock(Resource.class);
+ context = mock(DecoratorContext.class);
+ when(context.getResource()).thenReturn(resource);
+
+ issuable = mock(Issuable.class);
+ ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
+ when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
+ decorator = new IssuesDecorator(perspectives, rulefinder);
+ }
+
+ @Test
+ public void should_count_issues() {
+ when(resource.getScope()).thenReturn(Scopes.PROJECT);
+ when(issuable.issues()).thenReturn(createIssues());
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList());
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.ISSUES, 4.0);
+ }
+
+ /**
+ * See http://jira.codehaus.org/browse/SONAR-1729
+ */
+ @Test
+ public void should_not_count_issues_if_measure_already_exists() {
+ when(resource.getScope()).thenReturn(Scopes.PROJECT);
+ when(issuable.issues()).thenReturn(createIssues());
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList());
+ when(context.getMeasure(CoreMetrics.ISSUES)).thenReturn(new Measure(CoreMetrics.ISSUES, 3000.0));
+ when(context.getMeasure(CoreMetrics.MAJOR_ISSUES)).thenReturn(new Measure(CoreMetrics.MAJOR_ISSUES, 500.0));
+
+ decorator.decorate(resource, context);
+
+ verify(context, never()).saveMeasure(eq(CoreMetrics.ISSUES), anyDouble());// not changed
+ verify(context, never()).saveMeasure(eq(CoreMetrics.MAJOR_ISSUES), anyDouble());// not changed
+ verify(context, times(1)).saveMeasure(eq(CoreMetrics.CRITICAL_ISSUES), anyDouble());// did not exist
+ }
+
+ @Test
+ public void should_save_zero_on_projects() {
+ when(resource.getScope()).thenReturn(Scopes.PROJECT);
+ when(issuable.issues()).thenReturn(Lists.<Issue>newArrayList());
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList());
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.ISSUES, 0.0);
+ }
+
+ @Test
+ public void should_save_zero_on_directories() {
+ when(resource.getScope()).thenReturn(Scopes.DIRECTORY);
+ when(issuable.issues()).thenReturn(Lists.<Issue>newArrayList());
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList());
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.ISSUES, 0.0);
+ }
+
+ @Test
+ public void should_count_issues_by_severity() {
+ when(resource.getScope()).thenReturn(Scopes.PROJECT);
+ when(issuable.issues()).thenReturn(createIssues());
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList());
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.BLOCKER_ISSUES, 0.0);
+ verify(context).saveMeasure(CoreMetrics.CRITICAL_ISSUES, 2.0);
+ verify(context).saveMeasure(CoreMetrics.MAJOR_ISSUES, 1.0);
+ verify(context).saveMeasure(CoreMetrics.MINOR_ISSUES, 1.0);
+ verify(context).saveMeasure(CoreMetrics.INFO_ISSUES, 0.0);
+ }
+
+ @Test
+ public void should_count_issues_per_rule() {
+ List<Issue> issues = newArrayList();
+ issues.add(new DefaultIssue().setRuleRepositoryKey(ruleA1.getRepositoryKey()).setRuleKey(ruleA1.getKey()).setSeverity(RulePriority.CRITICAL.name()));
+ issues.add(new DefaultIssue().setRuleRepositoryKey(ruleA1.getRepositoryKey()).setRuleKey(ruleA1.getKey()).setSeverity(RulePriority.CRITICAL.name()));
+ issues.add(new DefaultIssue().setRuleRepositoryKey(ruleA2.getRepositoryKey()).setRuleKey(ruleA2.getKey()).setSeverity(RulePriority.MAJOR.name()));
+ when(issuable.issues()).thenReturn(issues);
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.CRITICAL_ISSUES, ruleA1, 2.0)));
+ verify(context, never()).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MAJOR_ISSUES, ruleA1, 0.0)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MAJOR_ISSUES, ruleA2, 1.0)));
+ }
+
+ @Test
+ public void same_rule_should_have_different_severities() {
+ List<Issue> issues = newArrayList();
+ issues.add(new DefaultIssue().setRuleRepositoryKey(ruleA1.getRepositoryKey()).setRuleKey(ruleA1.getKey()).setSeverity(RulePriority.CRITICAL.name()));
+ issues.add(new DefaultIssue().setRuleRepositoryKey(ruleA1.getRepositoryKey()).setRuleKey(ruleA1.getKey()).setSeverity(RulePriority.CRITICAL.name()));
+ issues.add(new DefaultIssue().setRuleRepositoryKey(ruleA1.getRepositoryKey()).setRuleKey(ruleA1.getKey()).setSeverity(RulePriority.MINOR.name()));
+ when(issuable.issues()).thenReturn(issues);
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.CRITICAL_ISSUES, ruleA1, 2.0)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MINOR_ISSUES, ruleA1, 1.0)));
+ }
+
+ private List<Issue> createIssues() {
+ List<Issue> issues = newArrayList();
+ issues.add(new DefaultIssue().setRuleRepositoryKey(ruleA1.getRepositoryKey()).setRuleKey(ruleA1.getKey()).setSeverity(RulePriority.CRITICAL.name()));
+ issues.add(new DefaultIssue().setRuleRepositoryKey(ruleA1.getRepositoryKey()).setRuleKey(ruleA1.getKey()).setSeverity(RulePriority.CRITICAL.name()));
+ issues.add(new DefaultIssue().setRuleRepositoryKey(ruleA2.getRepositoryKey()).setRuleKey(ruleA2.getKey()).setSeverity(RulePriority.MAJOR.name()));
+ issues.add(new DefaultIssue().setRuleRepositoryKey(ruleB1.getRepositoryKey()).setRuleKey(ruleB1.getKey()).setSeverity(RulePriority.MINOR.name()));
+ return issues;
+ }
+}
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.issue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.Scopes;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+
+public class IssuesDensityDecoratorTest {
+
+ private IssuesDensityDecorator decorator;
+ private Resource resource;
+
+ @Before
+ public void before() {
+ resource = mock(Resource.class);
+ when(resource.getScope()).thenReturn(Scopes.PROJECT);
+ decorator = new IssuesDensityDecorator();
+ }
+
+ @Test
+ public void calculate_density() {
+ assertThat(IssuesDensityDecorator.calculate(4000, 200)).isEqualTo(0.0);
+ assertThat(IssuesDensityDecorator.calculate(200, 200)).isEqualTo(0.0);
+ assertThat(IssuesDensityDecorator.calculate(50, 200)).isEqualTo(75.0);
+ assertThat(IssuesDensityDecorator.calculate(0, 200)).isEqualTo(100.0);
+ }
+
+ @Test
+ public void decorate_density() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 200.0));
+ when(context.getMeasure(CoreMetrics.WEIGHTED_ISSUES)).thenReturn(new Measure(CoreMetrics.WEIGHTED_ISSUES, 50.0));
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.ISSUES_DENSITY, 75.0);
+ }
+
+ @Test
+ public void no_density_if_no_ncloc() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 0.0));
+ when(context.getMeasure(CoreMetrics.WEIGHTED_ISSUES)).thenReturn(new Measure(CoreMetrics.WEIGHTED_ISSUES, 50.0));
+
+ decorator.decorate(resource, context);
+
+ verify(context, never()).saveMeasure(eq(CoreMetrics.ISSUES_DENSITY), anyDouble());
+ }
+
+ @Test
+ public void save_density_if_value_is_zero() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 200.0));
+ when(context.getMeasure(CoreMetrics.WEIGHTED_ISSUES)).thenReturn(new Measure(CoreMetrics.WEIGHTED_ISSUES, 5000.0));
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.ISSUES_DENSITY, 0.0);
+ }
+
+ @Test
+ public void density_is_hundred_when_no_debt() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 200.0));
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.ISSUES_DENSITY, 100.0);
+ }
+
+ @Test
+ public void density_is_hundred_when_debt_is_zero() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 200.0));
+ when(context.getMeasure(CoreMetrics.WEIGHTED_ISSUES)).thenReturn(new Measure(CoreMetrics.WEIGHTED_ISSUES, 0.0));
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.ISSUES_DENSITY, 100.0);
+ }
+}
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.issue;
+
+import org.hamcrest.core.Is;
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.config.Settings;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.test.IsMeasure;
+
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.*;
+
+public class WeightedIssuesDecoratorTest {
+
+ @Test
+ public void test_weighted_issues() {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.CORE_RULE_WEIGHTS_PROPERTY, "BLOCKER=10;CRITICAL=5;MAJOR=2;MINOR=1;INFO=0");
+ WeightedIssuesDecorator decorator = new WeightedIssuesDecorator(settings);
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.INFO_ISSUES)).thenReturn(new Measure(CoreMetrics.INFO_ISSUES, 50.0));
+ when(context.getMeasure(CoreMetrics.CRITICAL_ISSUES)).thenReturn(new Measure(CoreMetrics.CRITICAL_ISSUES, 80.0));
+ when(context.getMeasure(CoreMetrics.BLOCKER_ISSUES)).thenReturn(new Measure(CoreMetrics.BLOCKER_ISSUES, 100.0));
+
+ decorator.start();
+ decorator.decorate(context);
+
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.WEIGHTED_ISSUES, (double) (100 * 10 + 80 * 5 + 50 * 0))));
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.WEIGHTED_ISSUES, "INFO=50;CRITICAL=80;BLOCKER=100")));
+ }
+
+ // SONAR-3092
+ @Test
+ public void do_save_zero() {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.CORE_RULE_WEIGHTS_PROPERTY, "BLOCKER=10;CRITICAL=5;MAJOR=2;MINOR=1;INFO=0");
+ DecoratorContext context = mock(DecoratorContext.class);
+
+ WeightedIssuesDecorator decorator = new WeightedIssuesDecorator(settings);
+ decorator.start();
+ decorator.decorate(context);
+
+ verify(context).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void should_load_severity_weights_at_startup() {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.CORE_RULE_WEIGHTS_PROPERTY, "BLOCKER=2;CRITICAL=1;MAJOR=0;MINOR=0;INFO=0");
+
+ WeightedIssuesDecorator decorator = new WeightedIssuesDecorator(settings);
+ decorator.start();
+
+ assertThat(decorator.getWeightsBySeverity().get(RulePriority.BLOCKER), Is.is(2));
+ assertThat(decorator.getWeightsBySeverity().get(RulePriority.CRITICAL), Is.is(1));
+ assertThat(decorator.getWeightsBySeverity().get(RulePriority.MAJOR), Is.is(0));
+ }
+
+ @Test
+ public void weights_setting_should_be_optional() {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.CORE_RULE_WEIGHTS_PROPERTY, "BLOCKER=2");
+
+ WeightedIssuesDecorator decorator = new WeightedIssuesDecorator(settings);
+ decorator.start();
+
+ assertThat(decorator.getWeightsBySeverity().get(RulePriority.MAJOR), Is.is(1));
+ }
+}
\ No newline at end of file
.setDeleteHistoricalData(true)
.create();
+ // --------------------------------------------------------------------------------------------------------------------
+ //
+ // ISSUES
+ //
+ // --------------------------------------------------------------------------------------------------------------------
+
+ public static final String ISSUES_KEY = "issues";
+ public static final Metric ISSUES = new Metric.Builder(ISSUES_KEY, "Issues", Metric.ValueType.INT)
+ .setDescription("Issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .create();
+
+ public static final String WEIGHTED_ISSUES_KEY = "weighted_issues";
+ public static final Metric WEIGHTED_ISSUES = new Metric.Builder(WEIGHTED_ISSUES_KEY, "Weighted issues", Metric.ValueType.INT)
+ .setDescription("Weighted issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .create();
+
+ public static final String ISSUES_DENSITY_KEY = "issues_density";
+ public static final Metric ISSUES_DENSITY = new Metric.Builder(ISSUES_DENSITY_KEY, "Rules compliance", Metric.ValueType.PERCENT)
+ .setDescription("Rules compliance")
+ .setDirection(Metric.DIRECTION_BETTER)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .create();
+
+ public static final String BLOCKER_ISSUES_KEY = "blocker_issues";
+ public static final Metric BLOCKER_ISSUES = new Metric.Builder(BLOCKER_ISSUES_KEY, "Blocker issues", Metric.ValueType.INT)
+ .setDescription("Blocker issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .create();
+
+ public static final String CRITICAL_ISSUES_KEY = "critical_issues";
+ public static final Metric CRITICAL_ISSUES = new Metric.Builder(CRITICAL_ISSUES_KEY, "Critical issues", Metric.ValueType.INT)
+ .setDescription("Critical issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .create();
+
+ public static final String MAJOR_ISSUES_KEY = "major_issues";
+ public static final Metric MAJOR_ISSUES = new Metric.Builder(MAJOR_ISSUES_KEY, "Major issues", Metric.ValueType.INT)
+ .setDescription("Major issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .create();
+
+ public static final String MINOR_ISSUES_KEY = "minor_issues";
+ public static final Metric MINOR_ISSUES = new Metric.Builder(MINOR_ISSUES_KEY, "Minor issues", Metric.ValueType.INT)
+ .setDescription("Minor issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .create();
+
+ public static final String INFO_ISSUES_KEY = "info_issues";
+ public static final Metric INFO_ISSUES = new Metric.Builder(INFO_ISSUES_KEY, "Info issues", Metric.ValueType.INT)
+ .setDescription("Info issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .create();
+
+ public static final String NEW_ISSUES_KEY = "new_issues";
+ public static final Metric NEW_ISSUES = new Metric.Builder(NEW_ISSUES_KEY, "New issues", Metric.ValueType.INT)
+ .setDescription("New issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .setDeleteHistoricalData(true)
+ .create();
+
+ public static final String NEW_BLOCKER_ISSUES_KEY = "new_blocker_issues";
+ public static final Metric NEW_BLOCKER_ISSUES = new Metric.Builder(NEW_BLOCKER_ISSUES_KEY, "New Blocker issues", Metric.ValueType.INT)
+ .setDescription("New Blocker issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .setDeleteHistoricalData(true)
+ .create();
+
+ public static final String NEW_CRITICAL_ISSUES_KEY = "new_critical_issues";
+ public static final Metric NEW_CRITICAL_ISSUES = new Metric.Builder(NEW_CRITICAL_ISSUES_KEY, "New Critical issues", Metric.ValueType.INT)
+ .setDescription("New Critical issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .setDeleteHistoricalData(true)
+ .create();
+
+ public static final String NEW_MAJOR_ISSUES_KEY = "new_major_issues";
+ public static final Metric NEW_MAJOR_ISSUES = new Metric.Builder(NEW_MAJOR_ISSUES_KEY, "New Major issues", Metric.ValueType.INT)
+ .setDescription("New Major issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .setDeleteHistoricalData(true)
+ .create();
+
+ public static final String NEW_MINOR_ISSUES_KEY = "new_minor_issues";
+ public static final Metric NEW_MINOR_ISSUES = new Metric.Builder(NEW_MINOR_ISSUES_KEY, "New Minor issues", Metric.ValueType.INT)
+ .setDescription("New Minor issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .setDeleteHistoricalData(true)
+ .create();
+
+ public static final String NEW_INFO_ISSUES_KEY = "new_info_issues";
+ public static final Metric NEW_INFO_ISSUES = new Metric.Builder(NEW_INFO_ISSUES_KEY, "New Info issues", Metric.ValueType.INT)
+ .setDescription("New Info issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_RULES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .setDeleteHistoricalData(true)
+ .create();
+
// --------------------------------------------------------------------------------------------------------------------
//
// DESIGN
.append(key)
.toString();
}
+
+
}
return new Issue()
.setKey(utils.getString(json, "key"))
.setComponentKey(utils.getString(json, "component"))
- .setRuleKey(utils.getString(json, "ruleKey"))
- .setRuleRepositoryKey(utils.getString(json, "ruleRepositoryKey"))
+ .setRuleKey(utils.getString(json, "rule"))
+ .setRuleRepositoryKey(utils.getString(json, "ruleRepository"))
.setSeverity(utils.getString(json, "severity"))
.setTitle(utils.getString(json, "title"))
.setMessage(utils.getString(json, "message"))
{
"key": "029d283a-072b-4ef8-bdda-e4b212aa39e3",
"component": "com.sonarsource.it.samples:simple-sample:sample",
- "ruleKey": "NM_FIELD_NAMING_CONVENTION",
- "ruleRepositoryKey": "findbugs",
+ "rule": "NM_FIELD_NAMING_CONVENTION",
+ "ruleRepository": "findbugs",
"severity": "MAJOR",
"title": "title",
"message": "message",