aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java89
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/IssuesDecorator.java231
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/IssuesDensityDecorator.java9
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/NewIssuesDecorator.java235
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/SeverityUtils.java18
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/IssuesDecoratorTest.java255
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/NewIssuesDecoratorTest.java323
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java198
8 files changed, 633 insertions, 725 deletions
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 2f72a4ac5fa..878eb4e66c9 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
@@ -21,31 +21,18 @@
package org.sonar.plugins.core;
import com.google.common.collect.ImmutableList;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.Properties;
-import org.sonar.api.Property;
-import org.sonar.api.PropertyType;
-import org.sonar.api.SonarPlugin;
+import org.sonar.api.*;
import org.sonar.api.checks.NoSonarFilter;
import org.sonar.api.notifications.NotificationDispatcherMetadata;
import org.sonar.api.resources.Java;
-import org.sonar.batch.issue.InitialOpenIssuesSensor;
-import org.sonar.batch.issue.InitialOpenIssuesStack;
-import org.sonar.batch.issue.IssuesDecorator;
-import org.sonar.batch.issue.IssuesDensityDecorator;
-import org.sonar.batch.issue.NewIssuesDecorator;
-import org.sonar.batch.issue.WeightedIssuesDecorator;
+import org.sonar.batch.issue.*;
import org.sonar.core.timemachine.Periods;
import org.sonar.plugins.core.batch.IndexProjectPostJob;
import org.sonar.plugins.core.charts.DistributionAreaChart;
import org.sonar.plugins.core.charts.DistributionBarChart;
import org.sonar.plugins.core.charts.XradarChart;
import org.sonar.plugins.core.colorizers.JavaColorizerFormat;
-import org.sonar.plugins.core.dashboards.GlobalDefaultDashboard;
-import org.sonar.plugins.core.dashboards.ProjectDefaultDashboard;
-import org.sonar.plugins.core.dashboards.ProjectHotspotDashboard;
-import org.sonar.plugins.core.dashboards.ProjectReviewsDashboard;
-import org.sonar.plugins.core.dashboards.ProjectTimeMachineDashboard;
+import org.sonar.plugins.core.dashboards.*;
import org.sonar.plugins.core.issue.IssueTracking;
import org.sonar.plugins.core.issue.IssuesWorkflowDecorator;
import org.sonar.plugins.core.measurefilters.MyFavouritesFilter;
@@ -55,73 +42,12 @@ import org.sonar.plugins.core.notifications.reviews.ChangesInReviewAssignedToMeO
import org.sonar.plugins.core.notifications.reviews.NewFalsePositiveReview;
import org.sonar.plugins.core.notifications.violations.NewViolationsOnFirstDifferentialPeriod;
import org.sonar.plugins.core.security.ApplyProjectRolesDecorator;
-import org.sonar.plugins.core.sensors.BranchCoverageDecorator;
-import org.sonar.plugins.core.sensors.CheckAlertThresholds;
-import org.sonar.plugins.core.sensors.CommentDensityDecorator;
-import org.sonar.plugins.core.sensors.CoverageDecorator;
-import org.sonar.plugins.core.sensors.DirectoriesDecorator;
-import org.sonar.plugins.core.sensors.FilesDecorator;
-import org.sonar.plugins.core.sensors.GenerateAlertEvents;
-import org.sonar.plugins.core.sensors.ItBranchCoverageDecorator;
-import org.sonar.plugins.core.sensors.ItCoverageDecorator;
-import org.sonar.plugins.core.sensors.ItLineCoverageDecorator;
-import org.sonar.plugins.core.sensors.LineCoverageDecorator;
-import org.sonar.plugins.core.sensors.ManualMeasureDecorator;
-import org.sonar.plugins.core.sensors.ManualViolationInjector;
-import org.sonar.plugins.core.sensors.OverallBranchCoverageDecorator;
-import org.sonar.plugins.core.sensors.OverallCoverageDecorator;
-import org.sonar.plugins.core.sensors.OverallLineCoverageDecorator;
-import org.sonar.plugins.core.sensors.ProfileEventsSensor;
-import org.sonar.plugins.core.sensors.ProfileSensor;
-import org.sonar.plugins.core.sensors.ProjectLinksSensor;
-import org.sonar.plugins.core.sensors.ReviewNotifications;
-import org.sonar.plugins.core.sensors.ReviewWorkflowDecorator;
-import org.sonar.plugins.core.sensors.ReviewsMeasuresDecorator;
-import org.sonar.plugins.core.sensors.UnitTestDecorator;
-import org.sonar.plugins.core.sensors.VersionEventsSensor;
-import org.sonar.plugins.core.sensors.ViolationSeverityUpdater;
-import org.sonar.plugins.core.sensors.ViolationsDecorator;
-import org.sonar.plugins.core.sensors.ViolationsDensityDecorator;
-import org.sonar.plugins.core.sensors.WeightedViolationsDecorator;
-import org.sonar.plugins.core.timemachine.NewCoverageAggregator;
-import org.sonar.plugins.core.timemachine.NewCoverageFileAnalyzer;
-import org.sonar.plugins.core.timemachine.NewItCoverageFileAnalyzer;
-import org.sonar.plugins.core.timemachine.NewOverallCoverageFileAnalyzer;
-import org.sonar.plugins.core.timemachine.NewViolationsDecorator;
-import org.sonar.plugins.core.timemachine.TendencyDecorator;
-import org.sonar.plugins.core.timemachine.TimeMachineConfigurationPersister;
-import org.sonar.plugins.core.timemachine.VariationDecorator;
-import org.sonar.plugins.core.timemachine.ViolationPersisterDecorator;
-import org.sonar.plugins.core.timemachine.ViolationTrackingDecorator;
+import org.sonar.plugins.core.sensors.*;
+import org.sonar.plugins.core.timemachine.*;
import org.sonar.plugins.core.web.Lcom4Viewer;
import org.sonar.plugins.core.web.TestsViewer;
-import org.sonar.plugins.core.widgets.ActionPlansWidget;
-import org.sonar.plugins.core.widgets.AlertsWidget;
-import org.sonar.plugins.core.widgets.CommentsDuplicationsWidget;
-import org.sonar.plugins.core.widgets.ComplexityWidget;
-import org.sonar.plugins.core.widgets.CoverageWidget;
-import org.sonar.plugins.core.widgets.CustomMeasuresWidget;
-import org.sonar.plugins.core.widgets.DescriptionWidget;
-import org.sonar.plugins.core.widgets.EventsWidget;
-import org.sonar.plugins.core.widgets.HotspotMetricWidget;
-import org.sonar.plugins.core.widgets.HotspotMostViolatedResourcesWidget;
-import org.sonar.plugins.core.widgets.HotspotMostViolatedRulesWidget;
-import org.sonar.plugins.core.widgets.ItCoverageWidget;
-import org.sonar.plugins.core.widgets.MeasureFilterListWidget;
-import org.sonar.plugins.core.widgets.MeasureFilterTreemapWidget;
-import org.sonar.plugins.core.widgets.RulesWidget;
-import org.sonar.plugins.core.widgets.SizeWidget;
-import org.sonar.plugins.core.widgets.TimeMachineWidget;
-import org.sonar.plugins.core.widgets.TimelineWidget;
-import org.sonar.plugins.core.widgets.TreemapWidget;
-import org.sonar.plugins.core.widgets.WelcomeWidget;
-import org.sonar.plugins.core.widgets.reviews.FalsePositiveReviewsWidget;
-import org.sonar.plugins.core.widgets.reviews.MyReviewsWidget;
-import org.sonar.plugins.core.widgets.reviews.PlannedReviewsWidget;
-import org.sonar.plugins.core.widgets.reviews.ProjectReviewsWidget;
-import org.sonar.plugins.core.widgets.reviews.ReviewsMetricsWidget;
-import org.sonar.plugins.core.widgets.reviews.ReviewsPerDeveloperWidget;
-import org.sonar.plugins.core.widgets.reviews.UnplannedReviewsWidget;
+import org.sonar.plugins.core.widgets.*;
+import org.sonar.plugins.core.widgets.reviews.*;
import java.util.List;
@@ -515,7 +441,6 @@ public final class CorePlugin extends SonarPlugin {
IssueTracking.class,
ViolationPersisterDecorator.class,
NewViolationsDecorator.class,
- NewIssuesDecorator.class,
TimeMachineConfigurationPersister.class,
NewCoverageFileAnalyzer.class,
NewItCoverageFileAnalyzer.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/IssuesDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/issue/IssuesDecorator.java
index 803ed44381e..65286c6d124 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/IssuesDecorator.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/IssuesDecorator.java
@@ -19,9 +19,9 @@
*/
package org.sonar.batch.issue;
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multiset;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Predicate;
+import com.google.common.collect.*;
import org.sonar.api.batch.*;
import org.sonar.api.component.ResourcePerspectives;
import org.sonar.api.issue.Issuable;
@@ -29,24 +29,31 @@ 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.resources.ResourceUtils;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleFinder;
import org.sonar.api.rules.RulePriority;
+import org.sonar.batch.components.PastSnapshot;
+import org.sonar.batch.components.TimeMachineConfiguration;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * @since 3.6
+ */
@DependsUpon(DecoratorBarriers.END_OF_ISSUES_UPDATES)
public class IssuesDecorator implements Decorator {
private final ResourcePerspectives perspectives;
private final RuleFinder rulefinder;
+ private final TimeMachineConfiguration timeMachineConfiguration;
- public IssuesDecorator(ResourcePerspectives perspectives, RuleFinder rulefinder) {
+ public IssuesDecorator(ResourcePerspectives perspectives, RuleFinder rulefinder, TimeMachineConfiguration timeMachineConfiguration) {
this.perspectives = perspectives;
this.rulefinder = rulefinder;
+ this.timeMachineConfiguration = timeMachineConfiguration;
}
public boolean shouldExecuteOnProject(Project project) {
@@ -55,25 +62,65 @@ public class IssuesDecorator implements Decorator {
@DependedUpon
public List<Metric> generatesIssuesMetrics() {
- return Arrays.asList(CoreMetrics.ISSUES,
+ return newArrayList(
+ CoreMetrics.ISSUES,
CoreMetrics.BLOCKER_ISSUES,
CoreMetrics.CRITICAL_ISSUES,
CoreMetrics.MAJOR_ISSUES,
CoreMetrics.MINOR_ISSUES,
- CoreMetrics.INFO_ISSUES);
+ CoreMetrics.INFO_ISSUES,
+ CoreMetrics.NEW_ISSUES,
+ CoreMetrics.NEW_BLOCKER_ISSUES,
+ CoreMetrics.NEW_CRITICAL_ISSUES,
+ CoreMetrics.NEW_MAJOR_ISSUES,
+ CoreMetrics.NEW_MINOR_ISSUES,
+ CoreMetrics.NEW_INFO_ISSUES,
+ CoreMetrics.FALSE_POSITIVE_ISSUES,
+ CoreMetrics.UNASSIGNED_ISSUES
+ );
}
public void decorate(Resource resource, DecoratorContext context) {
Issuable issuable = perspectives.as(Issuable.class, resource);
if (issuable != null) {
- Collection<Issue> issues = issuable.issues();
- computeTotalIssues(context, issues);
- computeIssuesPerSeverities(context, issues);
- computeIssuesPerRules(context, issues);
+ Collection<Issue> issues = getOpenIssues(issuable.issues());
+ boolean shouldSaveNewMetrics = shouldSaveNewMetrics(context);
+
+ Multiset<RulePriority> severitiesBag = HashMultiset.create();
+ Map<RulePriority, Multiset<Rule>> rulesPerSeverity = Maps.newHashMap();
+ ListMultimap<RulePriority, Issue> issuesPerSeverities = ArrayListMultimap.create();
+ int countUnassigned = 0;
+ int falsePositives = 0;
+
+ for (Issue issue : issues) {
+ severitiesBag.add(RulePriority.valueOf(issue.severity()));
+ Multiset<Rule> rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity()));
+ rulesBag.add(rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule()));
+ issuesPerSeverities.put(RulePriority.valueOf(issue.severity()), issue);
+
+ if (issue.assigneeLogin() == null) {
+ countUnassigned++;
+ }
+ if (Issue.RESOLUTION_FALSE_POSITIVE.equals(issue.resolution())) {
+ falsePositives++;
+ }
+ }
+
+ for (RulePriority ruleSeverity : RulePriority.values()) {
+ saveIssuesForSeverity(context, ruleSeverity, severitiesBag);
+ saveIssuesPerRules(context, ruleSeverity, rulesPerSeverity);
+ saveNewIssuesForSeverity(context, ruleSeverity, issuesPerSeverities, shouldSaveNewMetrics);
+ saveNewIssuesPerRule(context, ruleSeverity, issues, shouldSaveNewMetrics);
+ }
+
+ saveTotalIssues(context, issues);
+ saveNewIssues(context, issues, shouldSaveNewMetrics);
+ saveUnassignedIssues(context, countUnassigned);
+ saveFalsePositiveIssues(context, falsePositives);
}
}
- private void computeTotalIssues(DecoratorContext context, Collection<Issue> issues) {
+ private void saveTotalIssues(DecoratorContext context, Collection<Issue> issues) {
if (context.getMeasure(CoreMetrics.ISSUES) == null) {
Collection<Measure> childrenIssues = context.getChildrenMeasures(CoreMetrics.ISSUES);
Double sum = MeasureUtils.sum(true, childrenIssues);
@@ -81,53 +128,119 @@ public class IssuesDecorator implements Decorator {
}
}
- private void computeIssuesPerSeverities(DecoratorContext context, Collection<Issue> issues) {
- Multiset<RulePriority> severitiesBag = HashMultiset.create();
- for (Issue issue : issues) {
- severitiesBag.add(RulePriority.valueOf(issue.severity()));
+ private void saveNewIssues(DecoratorContext context, Collection<Issue> issues, boolean shouldSaveNewMetrics) {
+ if (shouldSaveNewMetrics) {
+ Measure measure = new Measure(CoreMetrics.NEW_ISSUES);
+ saveNewIssues(context, measure, issues);
+ }
+ }
+
+ private void saveIssuesForSeverity(DecoratorContext context, RulePriority ruleSeverity, Multiset<RulePriority> severitiesBag) {
+ 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);
}
+ }
- 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 saveNewIssuesForSeverity(DecoratorContext context, RulePriority severity, ListMultimap<RulePriority, Issue> issuesPerSeverities, boolean shouldSaveNewMetrics) {
+ if (shouldSaveNewMetrics) {
+ Metric metric = SeverityUtils.severityToNewMetricIssue(severity);
+ Measure measure = new Measure(metric);
+ saveNewIssues(context, measure, issuesPerSeverities.get(severity));
}
}
- 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.ruleKey().repository(), issue.ruleKey().rule()));
+ private void saveIssuesPerRules(DecoratorContext context, RulePriority severity, Map<RulePriority, Multiset<Rule>> rulesPerSeverity) {
+ 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());
+ }
}
- for (RulePriority severity : RulePriority.values()) {
- Metric metric = SeverityUtils.severityToIssueMetric(severity);
+ 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 void saveNewIssuesPerRule(DecoratorContext context, RulePriority severity, Collection<Issue> issues, boolean shouldSaveNewMetrics) {
+ if (shouldSaveNewMetrics) {
+ Metric metric = SeverityUtils.severityToNewMetricIssue(severity);
+ ListMultimap<Rule, Measure> childMeasuresPerRule = ArrayListMultimap.create();
+ ListMultimap<Rule, Issue> issuesPerRule = ArrayListMultimap.create();
+ Set<Rule> rules = Sets.newHashSet();
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());
+ if (rule != null) {
+ childMeasuresPerRule.put(rule, childRuleMeasure);
+ rules.add(rule);
+ }
+ }
+
+ for (Issue issue : issues) {
+ if (RulePriority.valueOf(issue.severity()).equals(severity)) {
+ Rule rule = rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule());
+ rules.add(rule);
+ issuesPerRule.put(rule, issue);
}
}
- 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);
+ for (Rule rule : rules) {
+ RuleMeasure measure = RuleMeasure.createForRule(metric, rule, null);
+ measure.setSeverity(severity);
+ for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
+ int variationIndex = pastSnapshot.getIndex();
+ int count = countIssuesAfterDate(issuesPerRule.get(rule), pastSnapshot.getTargetDate());
+ double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRule.get(rule)) + count;
+ measure.setVariation(variationIndex, sum);
}
+ context.saveMeasure(measure);
}
}
}
+ private void saveNewIssues(DecoratorContext context, Measure measure, Collection<Issue> issues) {
+ for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
+ int variationIndex = pastSnapshot.getIndex();
+ Collection<Measure> children = context.getChildrenMeasures(measure.getMetric());
+ int count = countIssuesAfterDate(issues, pastSnapshot.getTargetDate());
+ double sum = MeasureUtils.sumOnVariation(true, variationIndex, children) + count;
+ measure.setVariation(variationIndex, sum);
+ }
+ context.saveMeasure(measure);
+ }
+
+ private void saveUnassignedIssues(DecoratorContext context, int countUnassigned) {
+ context.saveMeasure(CoreMetrics.UNASSIGNED_ISSUES, (double) (countUnassigned + sumChildren(context, CoreMetrics.UNASSIGNED_ISSUES)));
+ }
+
+ private void saveFalsePositiveIssues(DecoratorContext context, int falsePositives) {
+ context.saveMeasure(CoreMetrics.FALSE_POSITIVE_ISSUES, (double) (falsePositives + sumChildren(context, CoreMetrics.FALSE_POSITIVE_ISSUES)));
+ }
+
+ private int sumChildren(DecoratorContext context, Metric metric) {
+ int sum = 0;
+ if (!ResourceUtils.isFile(context.getResource())) {
+ sum = MeasureUtils.sum(true, context.getChildrenMeasures(metric)).intValue();
+ }
+ return sum;
+ }
+
private Multiset<Rule> initRules(Map<RulePriority, Multiset<Rule>> rulesPerSeverity, RulePriority severity) {
Multiset<Rule> rulesBag = rulesPerSeverity.get(severity);
if (rulesBag == null) {
@@ -137,6 +250,40 @@ public class IssuesDecorator implements Decorator {
return rulesBag;
}
+ @VisibleForTesting
+ int countIssuesAfterDate(Collection<Issue> issues, Date targetDate) {
+ if (issues == null) {
+ return 0;
+ }
+ int count = 0;
+ for (Issue issue : issues) {
+ if (isAfter(issue, targetDate)) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ private boolean isAfter(Issue issue, Date date) {
+ if (date == null) {
+ return true;
+ }
+ return issue.createdAt() != null && issue.createdAt().after(date);
+ }
+
+ private boolean shouldSaveNewMetrics(DecoratorContext context) {
+ return context.getProject().isLatestAnalysis() && context.getMeasure(CoreMetrics.NEW_ISSUES) == null;
+ }
+
+ private Collection<Issue> getOpenIssues(Collection<Issue> issues) {
+ return newArrayList(Iterables.filter(issues, new Predicate<Issue>() {
+ @Override
+ public boolean apply(final Issue issue) {
+ return !Issue.STATUS_CLOSED.equals(issue.status());
+ }
+ }));
+ }
+
@Override
public String toString() {
return getClass().getSimpleName();
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/IssuesDensityDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/issue/IssuesDensityDecorator.java
index ae6e370d89d..47199681cca 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/IssuesDensityDecorator.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/IssuesDensityDecorator.java
@@ -19,10 +19,7 @@
*/
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.batch.*;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasureUtils;
@@ -33,6 +30,10 @@ import org.sonar.api.resources.Resource;
import java.util.Arrays;
import java.util.List;
+/**
+ * @since 3.6
+ */
+@DependsUpon(DecoratorBarriers.END_OF_ISSUES_UPDATES)
public class IssuesDensityDecorator implements Decorator {
public boolean shouldExecuteOnProject(Project project) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/NewIssuesDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/issue/NewIssuesDecorator.java
deleted file mode 100644
index b905408625b..00000000000
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/NewIssuesDecorator.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * 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.ArrayListMultimap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Sets;
-import org.apache.commons.lang.StringUtils;
-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.notifications.Notification;
-import org.sonar.api.notifications.NotificationManager;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.resources.ResourceUtils;
-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.batch.components.PastSnapshot;
-import org.sonar.batch.components.TimeMachineConfiguration;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.*;
-
-@DependsUpon(DecoratorBarriers.END_OF_ISSUES_UPDATES)
-public class NewIssuesDecorator implements Decorator {
-
- private final ResourcePerspectives perspectives;
- private final RuleFinder rulefinder;
- private TimeMachineConfiguration timeMachineConfiguration;
- private NotificationManager notificationManager;
-
- public NewIssuesDecorator(TimeMachineConfiguration timeMachineConfiguration, NotificationManager notificationManager, ResourcePerspectives perspectives, RuleFinder rulefinder) {
- this.timeMachineConfiguration = timeMachineConfiguration;
- this.notificationManager = notificationManager;
- this.perspectives = perspectives;
- this.rulefinder = rulefinder;
- }
-
- public boolean shouldExecuteOnProject(Project project) {
- return project.isLatestAnalysis();
- }
-
- @DependedUpon
- public List<Metric> generatesMetric() {
- return Arrays.asList(
- CoreMetrics.NEW_ISSUES,
- CoreMetrics.NEW_BLOCKER_ISSUES,
- CoreMetrics.NEW_CRITICAL_ISSUES,
- CoreMetrics.NEW_MAJOR_ISSUES,
- CoreMetrics.NEW_MINOR_ISSUES,
- CoreMetrics.NEW_INFO_ISSUES);
- }
-
- @SuppressWarnings("rawtypes")
- public void decorate(Resource resource, DecoratorContext context) {
- if (shouldDecorateResource(resource, context)) {
- Issuable issuable = perspectives.as(Issuable.class, context.getResource());
- if (issuable != null) {
- Collection<Issue> issues = issuable.issues();
-
- computeNewIssues(context, issues);
- computeNewIssuesPerSeverity(context, issues);
- computeNewIssuesPerRule(context, issues);
- }
- }
- if (ResourceUtils.isRootProject(resource)) {
- notifyNewIssues((Project) resource, context);
- }
- }
-
- private boolean shouldDecorateResource(Resource<?> resource, DecoratorContext context) {
- return (StringUtils.equals(Scopes.PROJECT, resource.getScope()) || StringUtils.equals(Scopes.DIRECTORY, resource.getScope()) || StringUtils
- .equals(Scopes.FILE, resource.getScope()))
- && (context.getMeasure(CoreMetrics.NEW_ISSUES) == null);
- }
-
- private void computeNewIssues(DecoratorContext context, Collection<Issue> issues) {
- Measure measure = new Measure(CoreMetrics.NEW_ISSUES);
- for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
- int variationIndex = pastSnapshot.getIndex();
- Collection<Measure> children = context.getChildrenMeasures(CoreMetrics.NEW_ISSUES);
- int count = countIssues(issues, pastSnapshot.getTargetDate());
- double sum = MeasureUtils.sumOnVariation(true, variationIndex, children) + count;
- measure.setVariation(variationIndex, sum);
- }
- context.saveMeasure(measure);
- }
-
- private void computeNewIssuesPerSeverity(DecoratorContext context, Collection<Issue> issues) {
- ListMultimap<RulePriority, Issue> issuesPerSeverities = ArrayListMultimap.create();
- for (Issue issue : issues) {
- issuesPerSeverities.put(RulePriority.valueOf(issue.severity()), issue);
- }
-
- for (RulePriority severity : RulePriority.values()) {
- Metric metric = severityToMetric(severity);
- Measure measure = new Measure(metric);
- for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
- int variationIndex = pastSnapshot.getIndex();
- int count = countIssues(issuesPerSeverities.get(severity), pastSnapshot.getTargetDate());
- Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.metric(metric));
- double sum = MeasureUtils.sumOnVariation(true, variationIndex, children) + count;
- measure.setVariation(variationIndex, sum);
- }
- context.saveMeasure(measure);
- }
- }
-
- private void computeNewIssuesPerRule(DecoratorContext context, Collection<Issue> issues) {
- for (RulePriority severity : RulePriority.values()) {
- Metric metric = severityToMetric(severity);
- ListMultimap<Rule, Measure> childMeasuresPerRule = ArrayListMultimap.create();
- ListMultimap<Rule, Issue> issuesPerRule = ArrayListMultimap.create();
- Set<Rule> rules = Sets.newHashSet();
-
- Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
- for (Measure child : children) {
- RuleMeasure childRuleMeasure = (RuleMeasure) child;
- Rule rule = childRuleMeasure.getRule();
- if (rule != null) {
- childMeasuresPerRule.put(rule, childRuleMeasure);
- rules.add(rule);
- }
- }
-
- for (Issue issue : issues) {
- if (RulePriority.valueOf(issue.severity()).equals(severity)) {
- Rule rule = rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule());
- rules.add(rule);
- issuesPerRule.put(rule, issue);
- }
- }
-
- for (Rule rule : rules) {
- RuleMeasure measure = RuleMeasure.createForRule(metric, rule, null);
- measure.setSeverity(severity);
- for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
- int variationIndex = pastSnapshot.getIndex();
- int count = countIssues(issuesPerRule.get(rule), pastSnapshot.getTargetDate());
- double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRule.get(rule)) + count;
- measure.setVariation(variationIndex, sum);
- }
- context.saveMeasure(measure);
- }
- }
- }
-
- int countIssues(Collection<Issue> issues, Date targetDate) {
- if (issues == null) {
- return 0;
- }
- int count = 0;
- for (Issue issue : issues) {
- if (isAfter(issue, targetDate)) {
- count++;
- }
- }
- return count;
- }
-
- private boolean isAfter(Issue issue, Date date) {
- if (date == null) {
- return true;
- }
- return issue.createdAt() != null && issue.createdAt().after(date);
- }
-
- private Metric severityToMetric(RulePriority severity) {
- Metric metric;
- if (severity.equals(RulePriority.BLOCKER)) {
- metric = CoreMetrics.NEW_BLOCKER_ISSUES;
- } else if (severity.equals(RulePriority.CRITICAL)) {
- metric = CoreMetrics.NEW_CRITICAL_ISSUES;
- } else if (severity.equals(RulePriority.MAJOR)) {
- metric = CoreMetrics.NEW_MAJOR_ISSUES;
- } else if (severity.equals(RulePriority.MINOR)) {
- metric = CoreMetrics.NEW_MINOR_ISSUES;
- } else if (severity.equals(RulePriority.INFO)) {
- metric = CoreMetrics.NEW_INFO_ISSUES;
- } else {
- throw new IllegalArgumentException("Unsupported severity: " + severity);
- }
- return metric;
- }
-
- protected void notifyNewIssues(Project project, DecoratorContext context) {
- List<PastSnapshot> projectPastSnapshots = timeMachineConfiguration.getProjectPastSnapshots();
- if (projectPastSnapshots.size() >= 1) {
- // we always check new issues against period1
- PastSnapshot pastSnapshot = projectPastSnapshots.get(0);
- Double newIssuesCount = context.getMeasure(CoreMetrics.NEW_ISSUES).getVariation1();
- // Do not send notification if this is the first analysis or if there's no violation
- if (pastSnapshot.getTargetDate() != null && newIssuesCount != null && newIssuesCount > 0) {
- // Maybe we should check if this is the first analysis or not?
- DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
- Notification notification = new Notification("new-issues")
- .setDefaultMessage(newIssuesCount.intValue() + " new issues on " + project.getLongName() + ".")
- .setFieldValue("count", String.valueOf(newIssuesCount.intValue()))
- .setFieldValue("projectName", project.getLongName())
- .setFieldValue("projectKey", project.getKey())
- .setFieldValue("projectId", String.valueOf(project.getId()))
- .setFieldValue("fromDate", dateformat.format(pastSnapshot.getTargetDate()));
- notificationManager.scheduleForSending(notification);
- }
- }
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName();
- }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/SeverityUtils.java b/sonar-batch/src/main/java/org/sonar/batch/issue/SeverityUtils.java
index 7377089dfaf..c90516eaaef 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/SeverityUtils.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/SeverityUtils.java
@@ -45,4 +45,22 @@ final class SeverityUtils {
}
return metric;
}
+
+ static Metric severityToNewMetricIssue(RulePriority severity) {
+ Metric metric;
+ if (severity.equals(RulePriority.BLOCKER)) {
+ metric = CoreMetrics.NEW_BLOCKER_ISSUES;
+ } else if (severity.equals(RulePriority.CRITICAL)) {
+ metric = CoreMetrics.NEW_CRITICAL_ISSUES;
+ } else if (severity.equals(RulePriority.MAJOR)) {
+ metric = CoreMetrics.NEW_MAJOR_ISSUES;
+ } else if (severity.equals(RulePriority.MINOR)) {
+ metric = CoreMetrics.NEW_MINOR_ISSUES;
+ } else if (severity.equals(RulePriority.INFO)) {
+ metric = CoreMetrics.NEW_INFO_ISSUES;
+ } else {
+ throw new IllegalArgumentException("Unsupported severity: " + severity);
+ }
+ return metric;
+ }
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/IssuesDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/IssuesDecoratorTest.java
index 092fc2de768..4a331b8ea72 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/issue/IssuesDecoratorTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/issue/IssuesDecoratorTest.java
@@ -17,45 +17,57 @@
* 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.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.time.DateUtils;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
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.measures.*;
+import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.Scopes;
+import org.sonar.api.rule.RuleKey;
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.batch.components.PastSnapshot;
+import org.sonar.batch.components.TimeMachineConfiguration;
import org.sonar.core.issue.DefaultIssue;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
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.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.*;
public class IssuesDecoratorTest {
+ private IssuesDecorator decorator;
+ private TimeMachineConfiguration timeMachineConfiguration;
+ private RuleFinder rulefinder;
+ private Issuable issuable;
+ private DecoratorContext context;
+ private Resource resource;
+ private Project project;
private Rule ruleA1;
private Rule ruleA2;
private Rule ruleB1;
- private IssuesDecorator decorator;
- private Resource resource;
- private DecoratorContext context;
- private Issuable issuable;
- private RuleFinder rulefinder;
+ private Date rightNow;
+ private Date tenDaysAgo;
+ private Date fiveDaysAgo;
@Before
public void before() {
@@ -68,21 +80,46 @@ public class IssuesDecoratorTest {
when(rulefinder.findByKey(ruleA2.getRepositoryKey(), ruleA2.getKey())).thenReturn(ruleA2);
when(rulefinder.findByKey(ruleB1.getRepositoryKey(), ruleB1.getKey())).thenReturn(ruleB1);
+ rightNow = new Date();
+ tenDaysAgo = DateUtils.addDays(rightNow, -10);
+ fiveDaysAgo = DateUtils.addDays(rightNow, -5);
+
+ PastSnapshot pastSnapshot = mock(PastSnapshot.class);
+ when(pastSnapshot.getIndex()).thenReturn(1);
+ when(pastSnapshot.getTargetDate()).thenReturn(fiveDaysAgo);
+
+ PastSnapshot pastSnapshot2 = mock(PastSnapshot.class);
+ when(pastSnapshot2.getIndex()).thenReturn(2);
+ when(pastSnapshot2.getTargetDate()).thenReturn(tenDaysAgo);
+
+ timeMachineConfiguration = mock(TimeMachineConfiguration.class);
+ when(timeMachineConfiguration.getProjectPastSnapshots()).thenReturn(Arrays.asList(pastSnapshot, pastSnapshot2));
+
+ project = mock(Project.class);
+ when(project.isLatestAnalysis()).thenReturn(true);
+
resource = mock(Resource.class);
context = mock(DecoratorContext.class);
when(context.getResource()).thenReturn(resource);
+ when(context.getProject()).thenReturn(project);
+ when(context.getMeasure(CoreMetrics.NEW_ISSUES)).thenReturn(null);
issuable = mock(Issuable.class);
ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
- decorator = new IssuesDecorator(perspectives, rulefinder);
+ decorator = new IssuesDecorator(perspectives, rulefinder, timeMachineConfiguration);
+ }
+
+ @Test
+ public void should_be_depended_upon_metric() {
+ assertThat(decorator.generatesIssuesMetrics()).hasSize(14);
}
@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());
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
decorator.decorate(resource, context);
@@ -93,7 +130,7 @@ public class IssuesDecoratorTest {
public void should_do_nothing_when_issuable_is_null() {
ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
when(perspectives.as(Issuable.class, resource)).thenReturn(null);
- IssuesDecorator decorator = new IssuesDecorator(perspectives, rulefinder);
+ IssuesDecorator decorator = new IssuesDecorator(perspectives, rulefinder, timeMachineConfiguration);
decorator.decorate(resource, context);
@@ -107,7 +144,7 @@ public class IssuesDecoratorTest {
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.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
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));
@@ -122,7 +159,7 @@ public class IssuesDecoratorTest {
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());
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
decorator.decorate(resource, context);
@@ -133,7 +170,7 @@ public class IssuesDecoratorTest {
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());
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
decorator.decorate(resource, context);
@@ -144,7 +181,7 @@ public class IssuesDecoratorTest {
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());
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
decorator.decorate(resource, context);
@@ -171,6 +208,31 @@ public class IssuesDecoratorTest {
}
@Test
+ public void should_save_unassigned_issues() {
+ List<Issue> issues = newArrayList();
+ issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setStatus(Issue.STATUS_OPEN).setSeverity(RulePriority.CRITICAL.name()));
+ issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setStatus(Issue.STATUS_REOPENED).setSeverity(RulePriority.CRITICAL.name()));
+ issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setStatus(Issue.STATUS_OPEN).setAssigneeLogin("arthur").setSeverity(RulePriority.CRITICAL.name()));
+ when(issuable.issues()).thenReturn(issues);
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.UNASSIGNED_ISSUES, 2.0);
+ }
+
+ @Test
+ public void should_save_false_positive_issues() {
+ List<Issue> issues = newArrayList();
+ issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setStatus(Issue.STATUS_OPEN).setSeverity(RulePriority.CRITICAL.name()));
+ issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setResolution(Issue.RESOLUTION_FIXED).setStatus(Issue.STATUS_OPEN).setSeverity(RulePriority.CRITICAL.name()));
+ when(issuable.issues()).thenReturn(issues);
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.FALSE_POSITIVE_ISSUES, 1.0);
+ }
+
+ @Test
public void same_rule_should_have_different_severities() {
List<Issue> issues = newArrayList();
issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()));
@@ -184,12 +246,159 @@ public class IssuesDecoratorTest {
verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MINOR_ISSUES, ruleA1, 1.0)));
}
+ @Test
+ public void should_count_issues_after_date() {
+ List<Issue> issues = createIssuesForNewMetrics();
+
+ assertThat(decorator.countIssuesAfterDate(null, fiveDaysAgo)).isEqualTo(0);
+ assertThat(decorator.countIssuesAfterDate(issues, fiveDaysAgo)).isEqualTo(1); // 1 rightNow
+ assertThat(decorator.countIssuesAfterDate(issues, tenDaysAgo)).isEqualTo(3); // 1 rightNow + 2 fiveDaysAgo
+ }
+
+ @Test
+ public void should_clear_cache_after_execution() {
+ Issue issue1 = new DefaultIssue().setRuleKey(RuleKey.of(ruleA1.getRepositoryKey(), ruleA1.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreatedAt(rightNow);
+ Issue issue2 = new DefaultIssue().setRuleKey(RuleKey.of(ruleA2.getRepositoryKey(), ruleA2.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreatedAt(rightNow);
+ when(issuable.issues()).thenReturn(newArrayList(issue1)).thenReturn(newArrayList(issue2));
+
+ decorator.decorate(resource, context);
+ decorator.decorate(resource, context);
+
+ verify(context, times(2)).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, 1.0, 1.0)));
+ verify(context, never()).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, 2.0, 2.0)));
+ }
+
+ @Test
+ public void should_save_severity_new_issues() {
+ when(issuable.issues()).thenReturn(createIssuesForNewMetrics());
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_BLOCKER_ISSUES, 0.0, 0.0)));
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, 1.0, 1.0)));
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_MAJOR_ISSUES, 0.0, 1.0)));
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_MINOR_ISSUES, 0.0, 1.0)));
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_INFO_ISSUES, 0.0, 0.0)));
+ }
+
+ @Test
+ public void should_save_rule_new_issues() {
+ when(issuable.issues()).thenReturn(createIssuesForNewMetrics());
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, ruleA1, 1.0, 1.0)));
+ verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_MAJOR_ISSUES, ruleA2, 0.0, 1.0)));
+ verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_MINOR_ISSUES, ruleB1, 0.0, 1.0)));
+ }
+
+ @Test
+ public void should_not_save_new_issues_if_not_last_analysis() {
+ when(project.isLatestAnalysis()).thenReturn(false);
+ when(issuable.issues()).thenReturn(createIssuesForNewMetrics());
+
+ decorator.decorate(resource, context);
+
+ verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_BLOCKER_ISSUES)));
+ verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_CRITICAL_ISSUES)));
+ verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_MAJOR_ISSUES)));
+ verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_MINOR_ISSUES)));
+ verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_INFO_ISSUES)));
+ verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_CRITICAL_ISSUES)));
+ }
+
private List<Issue> createIssues() {
List<Issue> issues = newArrayList();
- issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()));
- issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()));
- issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()));
- issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(RulePriority.MINOR.name()));
+ issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setStatus(Issue.STATUS_OPEN));
+ issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setStatus(Issue.STATUS_REOPENED));
+ issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setStatus(Issue.STATUS_REOPENED));
+ issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(RulePriority.MINOR.name()).setStatus(Issue.STATUS_OPEN));
+ return issues;
+ }
+
+ private List<Issue> createIssuesForNewMetrics() {
+ List<Issue> issues = newArrayList();
+ issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setCreatedAt(rightNow).setStatus(Issue.STATUS_OPEN));
+ issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setCreatedAt(tenDaysAgo).setStatus(Issue.STATUS_OPEN));
+ issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setCreatedAt(fiveDaysAgo).setStatus(Issue.STATUS_REOPENED));
+ issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setCreatedAt(tenDaysAgo).setStatus(Issue.STATUS_REOPENED));
+ issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(RulePriority.MINOR.name()).setCreatedAt(fiveDaysAgo).setStatus(Issue.STATUS_OPEN));
+ issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(RulePriority.MINOR.name()).setCreatedAt(tenDaysAgo).setStatus(Issue.STATUS_OPEN));
return issues;
}
+
+ private class IsVariationRuleMeasure extends BaseMatcher<Measure> {
+ private Metric metric = null;
+ private Rule rule = null;
+ private Double var1 = null;
+ private Double var2 = null;
+
+ public IsVariationRuleMeasure(Metric metric, Rule rule, Double var1, Double var2) {
+ this.metric = metric;
+ this.rule = rule;
+ this.var1 = var1;
+ this.var2 = var2;
+ }
+
+ public boolean matches(Object o) {
+ if (!(o instanceof RuleMeasure)) {
+ return false;
+ }
+ RuleMeasure m = (RuleMeasure) o;
+ return ObjectUtils.equals(metric, m.getMetric()) &&
+ ObjectUtils.equals(rule, m.getRule()) &&
+ ObjectUtils.equals(var1, m.getVariation1()) &&
+ ObjectUtils.equals(var2, m.getVariation2());
+ }
+
+ public void describeTo(Description arg0) {
+ }
+ }
+
+ private class IsVariationMeasure extends BaseMatcher<Measure> {
+ private Metric metric = null;
+ private Double var1 = null;
+ private Double var2 = null;
+
+ public IsVariationMeasure(Metric metric, Double var1, Double var2) {
+ this.metric = metric;
+ this.var1 = var1;
+ this.var2 = var2;
+ }
+
+ public boolean matches(Object o) {
+ if (!(o instanceof Measure)) {
+ return false;
+ }
+ Measure m = (Measure) o;
+ return ObjectUtils.equals(metric, m.getMetric()) &&
+ ObjectUtils.equals(var1, m.getVariation1()) &&
+ ObjectUtils.equals(var2, m.getVariation2()) &&
+ !(m instanceof RuleMeasure);
+ }
+
+ public void describeTo(Description o) {
+ }
+ }
+
+ private class IsMetricMeasure extends BaseMatcher<Measure> {
+ private Metric metric = null;
+
+ public IsMetricMeasure(Metric metric) {
+ this.metric = metric;
+ }
+
+ public boolean matches(Object o) {
+ if (!(o instanceof Measure)) {
+ return false;
+ }
+ Measure m = (Measure) o;
+ return ObjectUtils.equals(metric, m.getMetric());
+ }
+
+ public void describeTo(Description o) {
+ }
+ }
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/NewIssuesDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/NewIssuesDecoratorTest.java
deleted file mode 100644
index 3bc4f7a32b7..00000000000
--- a/sonar-batch/src/test/java/org/sonar/batch/issue/NewIssuesDecoratorTest.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * 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.apache.commons.lang.ObjectUtils;
-import org.apache.commons.lang.time.DateUtils;
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-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.Metric;
-import org.sonar.api.measures.RuleMeasure;
-import org.sonar.api.notifications.Notification;
-import org.sonar.api.notifications.NotificationManager;
-import org.sonar.api.resources.File;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.rules.RulePriority;
-import org.sonar.batch.components.PastSnapshot;
-import org.sonar.batch.components.TimeMachineConfiguration;
-import org.sonar.core.issue.DefaultIssue;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.*;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
-
-public class NewIssuesDecoratorTest {
- private Rule rule1;
- private Rule rule2;
- private Rule rule3;
- private NewIssuesDecorator decorator;
- private Issuable issuable;
- private RuleFinder rulefinder;
- private DecoratorContext context;
- private Resource<?> resource;
- private NotificationManager notificationManager;
- private Date rightNow;
- private Date tenDaysAgo;
- private Date fiveDaysAgo;
- private TimeMachineConfiguration timeMachineConfiguration;
-
- @Before
- public void before() {
- rightNow = new Date();
- tenDaysAgo = DateUtils.addDays(rightNow, -10);
- fiveDaysAgo = DateUtils.addDays(rightNow, -5);
-
- PastSnapshot pastSnapshot = mock(PastSnapshot.class);
- when(pastSnapshot.getIndex()).thenReturn(1);
- when(pastSnapshot.getTargetDate()).thenReturn(fiveDaysAgo);
-
- PastSnapshot pastSnapshot2 = mock(PastSnapshot.class);
- when(pastSnapshot2.getIndex()).thenReturn(2);
- when(pastSnapshot2.getTargetDate()).thenReturn(tenDaysAgo);
-
- timeMachineConfiguration = mock(TimeMachineConfiguration.class);
- when(timeMachineConfiguration.getProjectPastSnapshots()).thenReturn(Arrays.asList(pastSnapshot, pastSnapshot2));
-
- context = mock(DecoratorContext.class);
- resource = new File("com/foo/bar");
- when(context.getResource()).thenReturn(resource);
-
- notificationManager = mock(NotificationManager.class);
-
- rule1 = Rule.create().setRepositoryKey("rule1").setKey("rule1").setName("name1");
- rule2 = Rule.create().setRepositoryKey("rule2").setKey("rule2").setName("name2");
- rule3 = Rule.create().setRepositoryKey("rule3").setKey("rule3").setName("name3");
-
- rulefinder = mock(RuleFinder.class);
- when(rulefinder.findByKey(rule1.getRepositoryKey(), rule1.getKey())).thenReturn(rule1);
- when(rulefinder.findByKey(rule2.getRepositoryKey(), rule2.getKey())).thenReturn(rule2);
- when(rulefinder.findByKey(rule3.getRepositoryKey(), rule3.getKey())).thenReturn(rule3);
-
- issuable = mock(Issuable.class);
- ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
- when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
- decorator = new NewIssuesDecorator(timeMachineConfiguration, notificationManager, perspectives, rulefinder);
- }
-
- @Test
- public void should_execute_if_last_analysis() {
- Project project = mock(Project.class);
-
- when(project.isLatestAnalysis()).thenReturn(false);
- assertThat(decorator.shouldExecuteOnProject(project)).isFalse();
-
- when(project.isLatestAnalysis()).thenReturn(true);
- assertThat(decorator.shouldExecuteOnProject(project)).isTrue();
- }
-
- @Test
- public void should_be_depended_upon_metric() {
- assertThat(decorator.generatesMetric()).hasSize(6);
- }
-
- @Test
- public void should_count_issues_after_date() {
- List<Issue> issues = createIssues();
-
- assertThat(decorator.countIssues(null, fiveDaysAgo)).isEqualTo(0);
- assertThat(decorator.countIssues(issues, fiveDaysAgo)).isEqualTo(1); // 1 rightNow
- assertThat(decorator.countIssues(issues, tenDaysAgo)).isEqualTo(3); // 1 rightNow + 2 fiveDaysAgo
- }
-
- @Test
- public void should_clear_cache_after_execution() {
- Issue issue1 = new DefaultIssue().setRuleKey(RuleKey.of(rule1.getRepositoryKey(), rule1.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreatedAt(rightNow);
- Issue issue2 = new DefaultIssue().setRuleKey(RuleKey.of(rule2.getRepositoryKey(), rule2.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreatedAt(rightNow);
- when(issuable.issues()).thenReturn(newArrayList(issue1)).thenReturn(newArrayList(issue2));
-
- decorator.decorate(resource, context);
- decorator.decorate(resource, context);
-
- verify(context, times(2)).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, 1.0, 1.0)));
- verify(context, never()).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, 2.0, 2.0)));
- }
-
- @Test
- public void severity_issues() {
- when(issuable.issues()).thenReturn(createIssues());
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_BLOCKER_ISSUES, 0.0, 0.0)));
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, 1.0, 1.0)));
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_MAJOR_ISSUES, 0.0, 1.0)));
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_MINOR_ISSUES, 0.0, 1.0)));
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_INFO_ISSUES, 0.0, 0.0)));
- }
-
- @Test
- public void rule_issues() {
- when(issuable.issues()).thenReturn(createIssues());
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, rule1, 1.0, 1.0)));
- verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_MAJOR_ISSUES, rule2, 0.0, 1.0)));
- verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_MINOR_ISSUES, rule3, 0.0, 1.0)));
- }
-
- @Test
- public void should_not_notify_if_not_lastest_analysis() {
- Project project = mock(Project.class);
- when(project.isLatestAnalysis()).thenReturn(false);
- assertThat(decorator.shouldExecuteOnProject(project)).isFalse();
- }
-
- @Test
- public void should_not_notify_if_not_root_project() throws Exception {
- Project project = mock(Project.class);
- when(project.getQualifier()).thenReturn(Qualifiers.MODULE);
-
- decorator.decorate(project, context);
-
- verify(notificationManager, never()).scheduleForSending(any(Notification.class));
- }
-
- @Test
- public void should_not_notify_if_no_not_enough_past_snapshots() throws Exception {
- Project project = new Project("key");
- // the #setUp method adds 2 snapshots: if last period analysis is 3, then it's not enough
- when(timeMachineConfiguration.getProjectPastSnapshots()).thenReturn(new ArrayList<PastSnapshot>());
-
- decorator.notifyNewIssues(project, context);
- verify(notificationManager, never()).scheduleForSending(any(Notification.class));
- }
-
- @Test
- public void should_not_notify_if_no_new_issues() throws Exception {
- Project project = new Project("key");
- Measure m = new Measure(CoreMetrics.NEW_ISSUES);
- when(context.getMeasure(CoreMetrics.NEW_ISSUES)).thenReturn(m);
-
- // NULL is returned here
- decorator.notifyNewIssues(project, context);
- verify(notificationManager, never()).scheduleForSending(any(Notification.class));
-
- // 0 will be returned now
- m.setVariation1(0.0);
- decorator.notifyNewIssues(project, context);
- verify(notificationManager, never()).scheduleForSending(any(Notification.class));
- }
-
- @Test
- public void should_not_notify_user_if_first_analysis() throws Exception {
- Project project = new Project("key").setName("LongName");
- project.setId(45);
- // PastSnapshot with targetDate==null means first analysis
- PastSnapshot pastSnapshot = new PastSnapshot("", null);
- when(timeMachineConfiguration.getProjectPastSnapshots()).thenReturn(Lists.newArrayList(pastSnapshot));
- Measure m = new Measure(CoreMetrics.NEW_ISSUES).setVariation1(0.0);
- when(context.getMeasure(CoreMetrics.NEW_ISSUES)).thenReturn(m);
-
- decorator.decorate(project, context);
- verify(notificationManager, never()).scheduleForSending(any(Notification.class));
- }
-
- @Test
- public void should_notify_user_about_new_issues() throws Exception {
- Project project = new Project("key").setName("LongName");
- project.setId(45);
- Calendar pastDate = new GregorianCalendar(2011, 10, 25);
- PastSnapshot pastSnapshot = new PastSnapshot("", pastDate.getTime());
- when(timeMachineConfiguration.getProjectPastSnapshots()).thenReturn(Lists.newArrayList(pastSnapshot, pastSnapshot));
- Measure m = new Measure(CoreMetrics.NEW_ISSUES).setVariation1(32.0);
- when(context.getMeasure(CoreMetrics.NEW_ISSUES)).thenReturn(m);
-
- decorator.decorate(project, context);
-
- DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
- Notification notification = new Notification("new-issues")
- .setDefaultMessage("32 new issues on LongName.")
- .setFieldValue("count", "32")
- .setFieldValue("projectName", "LongName")
- .setFieldValue("projectKey", "key")
- .setFieldValue("projectId", "45")
- .setFieldValue("fromDate", dateformat.format(pastDate.getTime()));
- verify(notificationManager, times(1)).scheduleForSending(eq(notification));
- }
-
- private List<Issue> createIssues() {
- List<Issue> issues = newArrayList();
- issues.add(new DefaultIssue().setRuleKey(rule1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setCreatedAt(rightNow));
- issues.add(new DefaultIssue().setRuleKey(rule1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setCreatedAt(tenDaysAgo));
- issues.add(new DefaultIssue().setRuleKey(rule2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setCreatedAt(fiveDaysAgo));
- issues.add(new DefaultIssue().setRuleKey(rule2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setCreatedAt(tenDaysAgo));
- issues.add(new DefaultIssue().setRuleKey(rule3.ruleKey()).setSeverity(RulePriority.MINOR.name()).setCreatedAt(fiveDaysAgo));
- issues.add(new DefaultIssue().setRuleKey(rule3.ruleKey()).setSeverity(RulePriority.MINOR.name()).setCreatedAt(tenDaysAgo));
- return issues;
- }
-
- private class IsVariationRuleMeasure extends BaseMatcher<Measure> {
- private Metric metric = null;
- private Rule rule = null;
- private Double var1 = null;
- private Double var2 = null;
-
- public IsVariationRuleMeasure(Metric metric, Rule rule, Double var1, Double var2) {
- this.metric = metric;
- this.rule = rule;
- this.var1 = var1;
- this.var2 = var2;
- }
-
- public boolean matches(Object o) {
- if (!(o instanceof RuleMeasure)) {
- return false;
- }
- RuleMeasure m = (RuleMeasure) o;
- return ObjectUtils.equals(metric, m.getMetric()) &&
- ObjectUtils.equals(rule, m.getRule()) &&
- ObjectUtils.equals(var1, m.getVariation1()) &&
- ObjectUtils.equals(var2, m.getVariation2());
- }
-
- public void describeTo(Description arg0) {
- }
- }
-
- private class IsVariationMeasure extends BaseMatcher<Measure> {
- private Metric metric = null;
- private Double var1 = null;
- private Double var2 = null;
-
- public IsVariationMeasure(Metric metric, Double var1, Double var2) {
- this.metric = metric;
- this.var1 = var1;
- this.var2 = var2;
- }
-
- public boolean matches(Object o) {
- if (!(o instanceof Measure)) {
- return false;
- }
- Measure m = (Measure) o;
- return ObjectUtils.equals(metric, m.getMetric()) &&
- ObjectUtils.equals(var1, m.getVariation1()) &&
- ObjectUtils.equals(var2, m.getVariation2()) &&
- !(m instanceof RuleMeasure);
- }
-
- public void describeTo(Description o) {
- }
- }
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
index ef463a683ff..aafff9113b1 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
@@ -47,6 +47,7 @@ public final class CoreMetrics {
public static String DOMAIN_RULES = "Rules";
public static String DOMAIN_SCM = "SCM";
public static String DOMAIN_REVIEWS = "Reviews";
+ public static String DOMAIN_ISSUES = "Issues";
/**
* @deprecated since 2.5 See SONAR-2007
@@ -1532,150 +1533,315 @@ public final class CoreMetrics {
//
// --------------------------------------------------------------------------------------------------------------------
+ /**
+ * @since 3.6
+ */
public static final String ISSUES_KEY = "issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String WEIGHTED_ISSUES_KEY = "weighted_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String ISSUES_DENSITY_KEY = "issues_density";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.create();
+ /**
+ * @since 3.6
+ */
public static final String BLOCKER_ISSUES_KEY = "blocker_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String CRITICAL_ISSUES_KEY = "critical_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String MAJOR_ISSUES_KEY = "major_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String MINOR_ISSUES_KEY = "minor_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String INFO_ISSUES_KEY = "info_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String NEW_ISSUES_KEY = "new_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.setDeleteHistoricalData(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String NEW_BLOCKER_ISSUES_KEY = "new_blocker_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.setDeleteHistoricalData(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String NEW_CRITICAL_ISSUES_KEY = "new_critical_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.setDeleteHistoricalData(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String NEW_MAJOR_ISSUES_KEY = "new_major_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.setDeleteHistoricalData(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String NEW_MINOR_ISSUES_KEY = "new_minor_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.setDeleteHistoricalData(true)
.create();
+ /**
+ * @since 3.6
+ */
public static final String NEW_INFO_ISSUES_KEY = "new_info_issues";
+
+ /**
+ * @since 3.6
+ */
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)
+ .setDomain(DOMAIN_ISSUES)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.setDeleteHistoricalData(true)
- .create();
-
+ .create();
+
+ /**
+ * @since 3.6
+ */
+ public static final String FALSE_POSITIVE_ISSUES_KEY = "false_positive_issues";
+
+ /**
+ * @since 3.6
+ */
+ public static final Metric FALSE_POSITIVE_ISSUES = new Metric.Builder(FALSE_POSITIVE_ISSUES_KEY, "False-positive issues", Metric.ValueType.INT)
+ .setDescription("Active false-positive issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setDomain(DOMAIN_ISSUES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .create();
+
+ /**
+ * @since 3.6
+ */
+ public static final String UNASSIGNED_ISSUES_KEY = "unassigned_issues";
+
+ /**
+ * @since 3.6
+ */
+ public static final Metric UNASSIGNED_ISSUES = new Metric.Builder(UNASSIGNED_ISSUES_KEY, "Unassigned issues", Metric.ValueType.INT)
+ .setDescription("Active unassigned issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setDomain(DOMAIN_ISSUES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .create();
+
+ /**
+ * @since 3.6
+ */
+ public static final String UNPLANNED_ISSUES_KEY = "unplanned_issues";
+
+ /**
+ * @since 3.6
+ */
+ public static final Metric UNPLANNED_ISSUES = new Metric.Builder(UNPLANNED_ISSUES_KEY, "Unplanned issues", Metric.ValueType.INT)
+ .setDescription("Active unplanned issues")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setDomain(DOMAIN_ISSUES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .create();
+
+ /**
+ * @since 3.6
+ */
+ public static final String NEW_UNPLANNED_ISSUES_KEY = "new_unplanned_issues";
+
+ /**
+ * @since 3.6
+ */
+ public static final Metric NEW_UNPLANNED_ISSUES = new Metric.Builder(NEW_UNPLANNED_ISSUES_KEY, "New unplanned issues", Metric.ValueType.INT)
+ .setDescription("New issues that have not been planned yet")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setQualitative(true)
+ .setDomain(DOMAIN_ISSUES)
+ .setBestValue(0.0)
+ .setOptimizedBestValue(true)
+ .setDeleteHistoricalData(true)
+ .create();
+
+
// --------------------------------------------------------------------------------------------------------------------
//
// DESIGN