From 997785024dffbc84f9f460a8abe7839317a6d756 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 30 May 2013 18:26:02 +0200 Subject: [PATCH] Add Issuable#unresolvedIssues() --- .../org/sonar/plugins/core/CorePlugin.java | 2 +- ...va => CountUnresolvedIssuesDecorator.java} | 15 +---- ...> CountUnresolvedIssuesDecoratorTest.java} | 42 ++++++------ .../sonar/batch/issue/DefaultIssuable.java | 12 ++++ .../batch/issue/DefaultIssuableTest.java | 65 +++++++++++++++++++ .../java/org/sonar/api/issue/Issuable.java | 8 +++ 6 files changed, 109 insertions(+), 35 deletions(-) rename plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/{CountOpenIssuesDecorator.java => CountUnresolvedIssuesDecorator.java} (95%) rename plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/{CountOpenIssuesDecoratorTest.java => CountUnresolvedIssuesDecoratorTest.java} (91%) create mode 100644 sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java 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 e6b107c67a0..ee121f9f706 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 @@ -389,7 +389,7 @@ public final class CorePlugin extends SonarPlugin { IssueTrackingDecorator.class, IssueTracking.class, IssueHandlers.class, - CountOpenIssuesDecorator.class, + CountUnresolvedIssuesDecorator.class, CountFalsePositivesDecorator.class, WeightedIssuesDecorator.class, IssuesDensityDecorator.class, diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountOpenIssuesDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecorator.java similarity index 95% rename from plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountOpenIssuesDecorator.java rename to plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecorator.java index aea3cd13071..f8495296c02 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountOpenIssuesDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecorator.java @@ -49,13 +49,13 @@ import static com.google.common.collect.Lists.newArrayList; * @since 3.6 */ @DependsUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING) -public class CountOpenIssuesDecorator implements Decorator { +public class CountUnresolvedIssuesDecorator implements Decorator { private final ResourcePerspectives perspectives; private final RuleFinder rulefinder; private final TimeMachineConfiguration timeMachineConfiguration; - public CountOpenIssuesDecorator(ResourcePerspectives perspectives, RuleFinder rulefinder, TimeMachineConfiguration timeMachineConfiguration) { + public CountUnresolvedIssuesDecorator(ResourcePerspectives perspectives, RuleFinder rulefinder, TimeMachineConfiguration timeMachineConfiguration) { this.perspectives = perspectives; this.rulefinder = rulefinder; this.timeMachineConfiguration = timeMachineConfiguration; @@ -89,7 +89,7 @@ public class CountOpenIssuesDecorator implements Decorator { public void decorate(Resource resource, DecoratorContext context) { Issuable issuable = perspectives.as(Issuable.class, resource); if (issuable != null) { - Collection issues = getOpenIssues(issuable.issues()); + Collection issues = issuable.unresolvedIssues(); boolean shouldSaveNewMetrics = shouldSaveNewMetrics(context); Multiset severityBag = HashMultiset.create(); @@ -281,15 +281,6 @@ public class CountOpenIssuesDecorator implements Decorator { return context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null; } - private Collection getOpenIssues(Collection issues) { - return newArrayList(Iterables.filter(issues, new Predicate() { - @Override - public boolean apply(final Issue issue) { - return issue.resolution()==null; - } - })); - } - @Override public String toString() { return getClass().getSimpleName(); diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountOpenIssuesDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecoratorTest.java similarity index 91% rename from plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountOpenIssuesDecoratorTest.java rename to plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecoratorTest.java index a40b5435c30..6ea2df98b52 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountOpenIssuesDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecoratorTest.java @@ -35,6 +35,7 @@ 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.rule.Severity; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RulePriority; @@ -52,9 +53,9 @@ import static com.google.common.collect.Lists.newArrayList; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Mockito.*; -public class CountOpenIssuesDecoratorTest { +public class CountUnresolvedIssuesDecoratorTest { - CountOpenIssuesDecorator decorator; + CountUnresolvedIssuesDecorator decorator; TimeMachineConfiguration timeMachineConfiguration; RuleFinder ruleFinder; Issuable issuable; @@ -104,7 +105,7 @@ public class CountOpenIssuesDecoratorTest { issuable = mock(Issuable.class); ResourcePerspectives perspectives = mock(ResourcePerspectives.class); when(perspectives.as(Issuable.class, resource)).thenReturn(issuable); - decorator = new CountOpenIssuesDecorator(perspectives, ruleFinder, timeMachineConfiguration); + decorator = new CountUnresolvedIssuesDecorator(perspectives, ruleFinder, timeMachineConfiguration); } @Test @@ -115,7 +116,7 @@ public class CountOpenIssuesDecoratorTest { @Test public void should_count_issues() { when(resource.getScope()).thenReturn(Scopes.PROJECT); - when(issuable.issues()).thenReturn(createIssues()); + when(issuable.unresolvedIssues()).thenReturn(createIssues()); when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.emptyList()); decorator.decorate(resource, context); @@ -127,7 +128,7 @@ public class CountOpenIssuesDecoratorTest { public void should_do_nothing_when_issuable_is_null() { ResourcePerspectives perspectives = mock(ResourcePerspectives.class); when(perspectives.as(Issuable.class, resource)).thenReturn(null); - CountOpenIssuesDecorator decorator = new CountOpenIssuesDecorator(perspectives, ruleFinder, timeMachineConfiguration); + CountUnresolvedIssuesDecorator decorator = new CountUnresolvedIssuesDecorator(perspectives, ruleFinder, timeMachineConfiguration); decorator.decorate(resource, context); @@ -140,7 +141,7 @@ public class CountOpenIssuesDecoratorTest { @Test public void should_not_count_issues_if_measure_already_exists() { when(resource.getScope()).thenReturn(Scopes.PROJECT); - when(issuable.issues()).thenReturn(createIssues()); + when(issuable.unresolvedIssues()).thenReturn(createIssues()); when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.emptyList()); when(context.getMeasure(CoreMetrics.VIOLATIONS)).thenReturn(new Measure(CoreMetrics.VIOLATIONS, 3000.0)); when(context.getMeasure(CoreMetrics.MAJOR_VIOLATIONS)).thenReturn(new Measure(CoreMetrics.MAJOR_VIOLATIONS, 500.0)); @@ -155,7 +156,7 @@ public class CountOpenIssuesDecoratorTest { @Test public void should_save_zero_on_projects() { when(resource.getScope()).thenReturn(Scopes.PROJECT); - when(issuable.issues()).thenReturn(Lists.newArrayList()); + when(issuable.unresolvedIssues()).thenReturn(Lists.newArrayList()); when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.emptyList()); decorator.decorate(resource, context); @@ -166,7 +167,7 @@ public class CountOpenIssuesDecoratorTest { @Test public void should_save_zero_on_directories() { when(resource.getScope()).thenReturn(Scopes.DIRECTORY); - when(issuable.issues()).thenReturn(Lists.newArrayList()); + when(issuable.unresolvedIssues()).thenReturn(Lists.newArrayList()); when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.emptyList()); decorator.decorate(resource, context); @@ -177,7 +178,7 @@ public class CountOpenIssuesDecoratorTest { @Test public void should_count_issues_by_severity() { when(resource.getScope()).thenReturn(Scopes.PROJECT); - when(issuable.issues()).thenReturn(createIssues()); + when(issuable.unresolvedIssues()).thenReturn(createIssues()); when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.emptyList()); decorator.decorate(resource, context); @@ -195,7 +196,7 @@ public class CountOpenIssuesDecoratorTest { 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())); - when(issuable.issues()).thenReturn(issues); + when(issuable.unresolvedIssues()).thenReturn(issues); decorator.decorate(resource, context); @@ -210,7 +211,7 @@ public class CountOpenIssuesDecoratorTest { 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(ruleA1.ruleKey()).setSeverity(RulePriority.MINOR.name())); - when(issuable.issues()).thenReturn(issues); + when(issuable.unresolvedIssues()).thenReturn(issues); decorator.decorate(resource, context); @@ -231,7 +232,7 @@ public class CountOpenIssuesDecoratorTest { public void should_clear_cache_after_execution() { Issue issue1 = new DefaultIssue().setRuleKey(RuleKey.of(ruleA1.getRepositoryKey(), ruleA1.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(rightNow); Issue issue2 = new DefaultIssue().setRuleKey(RuleKey.of(ruleA2.getRepositoryKey(), ruleA2.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(rightNow); - when(issuable.issues()).thenReturn(newArrayList(issue1)).thenReturn(newArrayList(issue2)); + when(issuable.unresolvedIssues()).thenReturn(newArrayList(issue1)).thenReturn(newArrayList(issue2)); decorator.decorate(resource, context); decorator.decorate(resource, context); @@ -242,7 +243,7 @@ public class CountOpenIssuesDecoratorTest { @Test public void should_save_severity_new_issues() { - when(issuable.issues()).thenReturn(createIssuesForNewMetrics()); + when(issuable.unresolvedIssues()).thenReturn(createIssuesForNewMetrics()); decorator.decorate(resource, context); @@ -256,7 +257,7 @@ public class CountOpenIssuesDecoratorTest { @Test public void should_save_rule_new_issues() { - when(issuable.issues()).thenReturn(createIssuesForNewMetrics()); + when(issuable.unresolvedIssues()).thenReturn(createIssuesForNewMetrics()); decorator.decorate(resource, context); @@ -269,7 +270,7 @@ public class CountOpenIssuesDecoratorTest { @Test public void should_not_save_new_issues_if_measure_already_computed() { when(context.getMeasure(CoreMetrics.NEW_VIOLATIONS)).thenReturn(new Measure()); - when(issuable.issues()).thenReturn(createIssuesForNewMetrics()); + when(issuable.unresolvedIssues()).thenReturn(createIssuesForNewMetrics()); decorator.decorate(resource, context); @@ -283,13 +284,10 @@ public class CountOpenIssuesDecoratorTest { List createIssues() { List issues = newArrayList(); - 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)); - - // resolved - issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setResolution(Issue.RESOLUTION_FIXED).setStatus(Issue.STATUS_RESOLVED)); + issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(Severity.CRITICAL).setStatus(Issue.STATUS_OPEN)); + issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(Severity.CRITICAL).setStatus(Issue.STATUS_REOPENED)); + issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(Severity.MAJOR).setStatus(Issue.STATUS_REOPENED)); + issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(Severity.MINOR).setStatus(Issue.STATUS_OPEN)); return issues; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java index 06a0c3df42d..475afd38f71 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java @@ -60,6 +60,18 @@ public class DefaultIssuable implements Issuable { return Lists.newArrayList(elements); } + @SuppressWarnings("unchecked") + @Override + public List unresolvedIssues() { + List result = Lists.newArrayList(); + for (DefaultIssue issue : cache.byComponent(component.key())) { + if (issue.resolution()==null) { + result.add(issue); + } + } + return result; + } + @Override public Component component() { return component; diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java new file mode 100644 index 00000000000..364aa4df207 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java @@ -0,0 +1,65 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.issue; + +import org.junit.Test; +import org.sonar.api.component.Component; +import org.sonar.api.issue.Issue; +import org.sonar.core.issue.DefaultIssue; + +import java.util.Arrays; +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultIssuableTest { + + ScanIssues scanIssues = mock(ScanIssues.class); + IssueCache cache = mock(IssueCache.class); + Component component = mock(Component.class); + + @Test + public void test_unresolvedIssues() throws Exception { + when(component.key()).thenReturn("struts:org.apache.Action"); + DefaultIssue resolved = new DefaultIssue().setResolution(Issue.RESOLUTION_FALSE_POSITIVE); + DefaultIssue unresolved = new DefaultIssue(); + when(cache.byComponent("struts:org.apache.Action")).thenReturn(Arrays.asList(resolved, unresolved)); + + DefaultIssuable perspective = new DefaultIssuable(component, scanIssues, cache); + + List issues = perspective.unresolvedIssues(); + assertThat(issues).containsOnly(unresolved); + } + + @Test + public void test_issues() throws Exception { + when(component.key()).thenReturn("struts:org.apache.Action"); + DefaultIssue resolved = new DefaultIssue().setResolution(Issue.RESOLUTION_FALSE_POSITIVE); + DefaultIssue unresolved = new DefaultIssue(); + when(cache.byComponent("struts:org.apache.Action")).thenReturn(Arrays.asList(resolved, unresolved)); + + DefaultIssuable perspective = new DefaultIssuable(component, scanIssues, cache); + + List issues = perspective.issues(); + assertThat(issues).containsOnly(unresolved, resolved); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java index bd600591619..d5cc74389c9 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java @@ -56,5 +56,13 @@ public interface Issuable extends Perspective { */ boolean addIssue(Issue issue); + /** + * The issues that are not resolved (=open). They include the manual issues reported by end-users. + */ + List unresolvedIssues(); + + /** + * All issues, even the issues that are resolved or closed during this analysis. + */ List issues(); } -- 2.39.5