2 * SonarQube, open source software quality management tool.
3 * Copyright (C) 2008-2013 SonarSource
4 * mailto:contact AT sonarsource DOT com
6 * SonarQube is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * SonarQube is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 package org.sonar.plugins.core.issue;
23 import com.google.common.collect.Lists;
24 import org.apache.commons.lang.ObjectUtils;
25 import org.apache.commons.lang.time.DateUtils;
26 import org.junit.Before;
27 import org.junit.Test;
28 import org.mockito.ArgumentMatcher;
29 import org.sonar.api.batch.DecoratorContext;
30 import org.sonar.api.component.ResourcePerspectives;
31 import org.sonar.api.issue.Issuable;
32 import org.sonar.api.issue.Issue;
33 import org.sonar.api.measures.*;
34 import org.sonar.api.resources.Project;
35 import org.sonar.api.resources.Resource;
36 import org.sonar.api.resources.Scopes;
37 import org.sonar.api.rule.RuleKey;
38 import org.sonar.api.rules.Rule;
39 import org.sonar.api.rules.RuleFinder;
40 import org.sonar.api.rules.RulePriority;
41 import org.sonar.api.test.IsRuleMeasure;
42 import org.sonar.batch.components.PastSnapshot;
43 import org.sonar.batch.components.TimeMachineConfiguration;
44 import org.sonar.core.issue.DefaultIssue;
46 import java.util.Arrays;
47 import java.util.Collections;
48 import java.util.Date;
49 import java.util.List;
51 import static com.google.common.collect.Lists.newArrayList;
52 import static org.fest.assertions.Assertions.assertThat;
53 import static org.mockito.Mockito.*;
55 public class CountOpenIssuesDecoratorTest {
57 CountOpenIssuesDecorator decorator;
58 TimeMachineConfiguration timeMachineConfiguration;
59 RuleFinder ruleFinder;
61 DecoratorContext context;
72 public void before() {
73 ruleA1 = Rule.create().setRepositoryKey("ruleA1").setKey("ruleA1").setName("nameA1");
74 ruleA2 = Rule.create().setRepositoryKey("ruleA2").setKey("ruleA2").setName("nameA2");
75 ruleB1 = Rule.create().setRepositoryKey("ruleB1").setKey("ruleB1").setName("nameB1");
77 ruleFinder = mock(RuleFinder.class);
78 when(ruleFinder.findByKey(ruleA1.getRepositoryKey(), ruleA1.getKey())).thenReturn(ruleA1);
79 when(ruleFinder.findByKey(ruleA2.getRepositoryKey(), ruleA2.getKey())).thenReturn(ruleA2);
80 when(ruleFinder.findByKey(ruleB1.getRepositoryKey(), ruleB1.getKey())).thenReturn(ruleB1);
82 rightNow = new Date();
83 tenDaysAgo = DateUtils.addDays(rightNow, -10);
84 fiveDaysAgo = DateUtils.addDays(rightNow, -5);
86 PastSnapshot pastSnapshot = mock(PastSnapshot.class);
87 when(pastSnapshot.getIndex()).thenReturn(1);
88 when(pastSnapshot.getTargetDate()).thenReturn(fiveDaysAgo);
90 PastSnapshot pastSnapshot2 = mock(PastSnapshot.class);
91 when(pastSnapshot2.getIndex()).thenReturn(2);
92 when(pastSnapshot2.getTargetDate()).thenReturn(tenDaysAgo);
94 timeMachineConfiguration = mock(TimeMachineConfiguration.class);
95 when(timeMachineConfiguration.getProjectPastSnapshots()).thenReturn(Arrays.asList(pastSnapshot, pastSnapshot2));
97 project = mock(Project.class);
98 when(project.isLatestAnalysis()).thenReturn(true);
100 resource = mock(Resource.class);
101 context = mock(DecoratorContext.class);
102 when(context.getResource()).thenReturn(resource);
103 when(context.getProject()).thenReturn(project);
104 when(context.getMeasure(CoreMetrics.NEW_ISSUES)).thenReturn(null);
106 issuable = mock(Issuable.class);
107 ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
108 when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
109 decorator = new CountOpenIssuesDecorator(perspectives, ruleFinder, timeMachineConfiguration);
113 public void should_be_depended_upon_metric() {
114 assertThat(decorator.generatesIssuesMetrics()).hasSize(16);
118 public void should_count_issues() {
119 when(resource.getScope()).thenReturn(Scopes.PROJECT);
120 when(issuable.issues()).thenReturn(createIssues());
121 when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
123 decorator.decorate(resource, context);
125 verify(context).saveMeasure(CoreMetrics.ISSUES, 4.0);
129 public void should_do_nothing_when_issuable_is_null() {
130 ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
131 when(perspectives.as(Issuable.class, resource)).thenReturn(null);
132 CountOpenIssuesDecorator decorator = new CountOpenIssuesDecorator(perspectives, ruleFinder, timeMachineConfiguration);
134 decorator.decorate(resource, context);
136 verifyZeroInteractions(context);
140 * See http://jira.codehaus.org/browse/SONAR-1729
143 public void should_not_count_issues_if_measure_already_exists() {
144 when(resource.getScope()).thenReturn(Scopes.PROJECT);
145 when(issuable.issues()).thenReturn(createIssues());
146 when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
147 when(context.getMeasure(CoreMetrics.ISSUES)).thenReturn(new Measure(CoreMetrics.ISSUES, 3000.0));
148 when(context.getMeasure(CoreMetrics.MAJOR_ISSUES)).thenReturn(new Measure(CoreMetrics.MAJOR_ISSUES, 500.0));
150 decorator.decorate(resource, context);
152 verify(context, never()).saveMeasure(eq(CoreMetrics.ISSUES), anyDouble());// not changed
153 verify(context, never()).saveMeasure(eq(CoreMetrics.MAJOR_ISSUES), anyDouble());// not changed
154 verify(context, times(1)).saveMeasure(eq(CoreMetrics.CRITICAL_ISSUES), anyDouble());// did not exist
158 public void should_save_zero_on_projects() {
159 when(resource.getScope()).thenReturn(Scopes.PROJECT);
160 when(issuable.issues()).thenReturn(Lists.<Issue>newArrayList());
161 when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
163 decorator.decorate(resource, context);
165 verify(context).saveMeasure(CoreMetrics.ISSUES, 0.0);
169 public void should_save_zero_on_directories() {
170 when(resource.getScope()).thenReturn(Scopes.DIRECTORY);
171 when(issuable.issues()).thenReturn(Lists.<Issue>newArrayList());
172 when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
174 decorator.decorate(resource, context);
176 verify(context).saveMeasure(CoreMetrics.ISSUES, 0.0);
180 public void should_count_issues_by_severity() {
181 when(resource.getScope()).thenReturn(Scopes.PROJECT);
182 when(issuable.issues()).thenReturn(createIssues());
183 when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
185 decorator.decorate(resource, context);
187 verify(context).saveMeasure(CoreMetrics.BLOCKER_ISSUES, 0.0);
188 verify(context).saveMeasure(CoreMetrics.CRITICAL_ISSUES, 2.0);
189 verify(context).saveMeasure(CoreMetrics.MAJOR_ISSUES, 1.0);
190 verify(context).saveMeasure(CoreMetrics.MINOR_ISSUES, 1.0);
191 verify(context).saveMeasure(CoreMetrics.INFO_ISSUES, 0.0);
195 public void should_count_issues_per_rule() {
196 List<Issue> issues = newArrayList();
197 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()));
198 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()));
199 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()));
200 when(issuable.issues()).thenReturn(issues);
202 decorator.decorate(resource, context);
204 verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.CRITICAL_ISSUES, ruleA1, 2.0)));
205 verify(context, never()).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MAJOR_ISSUES, ruleA1, 0.0)));
206 verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MAJOR_ISSUES, ruleA2, 1.0)));
210 public void should_save_unassigned_issues() {
211 List<Issue> issues = newArrayList();
212 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setStatus(Issue.STATUS_OPEN).setSeverity(RulePriority.CRITICAL.name()));
213 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setStatus(Issue.STATUS_REOPENED).setSeverity(RulePriority.CRITICAL.name()));
214 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setStatus(Issue.STATUS_OPEN).setAssignee("arthur").setSeverity(RulePriority.CRITICAL.name()));
215 when(issuable.issues()).thenReturn(issues);
217 decorator.decorate(resource, context);
219 verify(context).saveMeasure(CoreMetrics.UNASSIGNED_ISSUES, 2.0);
223 public void should_save_open_issues() {
224 List<Issue> issues = newArrayList();
225 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setStatus(Issue.STATUS_OPEN).setSeverity(RulePriority.CRITICAL.name()));
226 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setStatus(Issue.STATUS_REOPENED).setSeverity(RulePriority.CRITICAL.name()));
227 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setStatus(Issue.STATUS_OPEN).setSeverity(RulePriority.CRITICAL.name()));
228 when(issuable.issues()).thenReturn(issues);
230 decorator.decorate(resource, context);
232 verify(context).saveMeasure(CoreMetrics.OPEN_ISSUES, 2.0);
236 public void should_save_reopened_issues() {
237 List<Issue> issues = newArrayList();
238 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setStatus(Issue.STATUS_OPEN).setSeverity(RulePriority.CRITICAL.name()));
239 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setStatus(Issue.STATUS_REOPENED).setSeverity(RulePriority.CRITICAL.name()));
240 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setStatus(Issue.STATUS_OPEN).setSeverity(RulePriority.CRITICAL.name()));
241 when(issuable.issues()).thenReturn(issues);
243 decorator.decorate(resource, context);
245 verify(context).saveMeasure(CoreMetrics.REOPENED_ISSUES, 1.0);
250 public void should_save_confirmed_issues() {
251 List<Issue> issues = newArrayList();
252 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setStatus(Issue.STATUS_CONFIRMED).setSeverity(RulePriority.CRITICAL.name()));
253 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setStatus(Issue.STATUS_REOPENED).setSeverity(RulePriority.CRITICAL.name()));
254 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setStatus(Issue.STATUS_CONFIRMED).setSeverity(RulePriority.CRITICAL.name()));
255 when(issuable.issues()).thenReturn(issues);
257 decorator.decorate(resource, context);
259 verify(context).saveMeasure(CoreMetrics.CONFIRMED_ISSUES, 2.0);
263 public void same_rule_should_have_different_severities() {
264 List<Issue> issues = newArrayList();
265 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()));
266 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()));
267 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.MINOR.name()));
268 when(issuable.issues()).thenReturn(issues);
270 decorator.decorate(resource, context);
272 verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.CRITICAL_ISSUES, ruleA1, 2.0)));
273 verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MINOR_ISSUES, ruleA1, 1.0)));
277 public void should_count_issues_after_date() {
278 List<Issue> issues = createIssuesForNewMetrics();
280 assertThat(decorator.countIssuesAfterDate(null, fiveDaysAgo)).isEqualTo(0);
281 assertThat(decorator.countIssuesAfterDate(issues, fiveDaysAgo)).isEqualTo(1); // 1 rightNow
282 assertThat(decorator.countIssuesAfterDate(issues, tenDaysAgo)).isEqualTo(3); // 1 rightNow + 2 fiveDaysAgo
286 public void should_clear_cache_after_execution() {
287 Issue issue1 = new DefaultIssue().setRuleKey(RuleKey.of(ruleA1.getRepositoryKey(), ruleA1.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(rightNow);
288 Issue issue2 = new DefaultIssue().setRuleKey(RuleKey.of(ruleA2.getRepositoryKey(), ruleA2.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(rightNow);
289 when(issuable.issues()).thenReturn(newArrayList(issue1)).thenReturn(newArrayList(issue2));
291 decorator.decorate(resource, context);
292 decorator.decorate(resource, context);
294 verify(context, times(2)).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, 1.0, 1.0)));
295 verify(context, never()).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, 2.0, 2.0)));
299 public void should_save_severity_new_issues() {
300 when(issuable.issues()).thenReturn(createIssuesForNewMetrics());
302 decorator.decorate(resource, context);
304 // remember : period1 is 5daysAgo, period2 is 10daysAgo
305 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_BLOCKER_ISSUES, 0.0, 0.0)));
306 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, 1.0, 1.0)));
307 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_MAJOR_ISSUES, 0.0, 1.0)));
308 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_MINOR_ISSUES, 0.0, 1.0)));
309 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_INFO_ISSUES, 0.0, 0.0)));
313 public void should_save_rule_new_issues() {
314 when(issuable.issues()).thenReturn(createIssuesForNewMetrics());
316 decorator.decorate(resource, context);
318 // remember : period1 is 5daysAgo, period2 is 10daysAgo
319 verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_CRITICAL_ISSUES, ruleA1, 1.0, 1.0)));
320 verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_MAJOR_ISSUES, ruleA2, 0.0, 1.0)));
321 verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_MINOR_ISSUES, ruleB1, 0.0, 1.0)));
325 public void should_not_save_new_issues_if_not_last_analysis() {
326 when(project.isLatestAnalysis()).thenReturn(false);
327 when(issuable.issues()).thenReturn(createIssuesForNewMetrics());
329 decorator.decorate(resource, context);
331 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_BLOCKER_ISSUES)));
332 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_CRITICAL_ISSUES)));
333 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_MAJOR_ISSUES)));
334 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_MINOR_ISSUES)));
335 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_INFO_ISSUES)));
336 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_CRITICAL_ISSUES)));
339 List<Issue> createIssues() {
340 List<Issue> issues = newArrayList();
341 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setStatus(Issue.STATUS_OPEN));
342 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setStatus(Issue.STATUS_REOPENED));
343 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setStatus(Issue.STATUS_REOPENED));
344 issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(RulePriority.MINOR.name()).setStatus(Issue.STATUS_OPEN));
347 issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setResolution(Issue.RESOLUTION_FIXED).setStatus(Issue.STATUS_RESOLVED));
351 List<Issue> createIssuesForNewMetrics() {
352 List<Issue> issues = newArrayList();
353 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(rightNow).setStatus(Issue.STATUS_OPEN));
354 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(tenDaysAgo).setStatus(Issue.STATUS_OPEN));
355 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setCreationDate(fiveDaysAgo).setStatus(Issue.STATUS_REOPENED));
356 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setCreationDate(tenDaysAgo).setStatus(Issue.STATUS_REOPENED));
357 issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(RulePriority.MINOR.name()).setCreationDate(fiveDaysAgo).setStatus(Issue.STATUS_OPEN));
358 issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(RulePriority.MINOR.name()).setCreationDate(tenDaysAgo).setStatus(Issue.STATUS_OPEN));
362 class IsVariationRuleMeasure extends ArgumentMatcher<Measure> {
363 Metric metric = null;
368 public IsVariationRuleMeasure(Metric metric, Rule rule, Double var1, Double var2) {
369 this.metric = metric;
375 public boolean matches(Object o) {
376 if (!(o instanceof RuleMeasure)) {
379 RuleMeasure m = (RuleMeasure) o;
380 return ObjectUtils.equals(metric, m.getMetric()) &&
381 ObjectUtils.equals(rule, m.getRule()) &&
382 ObjectUtils.equals(var1, m.getVariation1()) &&
383 ObjectUtils.equals(var2, m.getVariation2());
387 class IsVariationMeasure extends ArgumentMatcher<Measure> {
388 Metric metric = null;
392 public IsVariationMeasure(Metric metric, Double var1, Double var2) {
393 this.metric = metric;
398 public boolean matches(Object o) {
399 if (!(o instanceof Measure)) {
402 Measure m = (Measure) o;
403 return ObjectUtils.equals(metric, m.getMetric()) &&
404 ObjectUtils.equals(var1, m.getVariation1()) &&
405 ObjectUtils.equals(var2, m.getVariation2()) &&
406 !(m instanceof RuleMeasure);
410 class IsMetricMeasure extends ArgumentMatcher<Measure> {
411 Metric metric = null;
413 public IsMetricMeasure(Metric metric) {
414 this.metric = metric;
417 public boolean matches(Object o) {
418 if (!(o instanceof Measure)) {
421 Measure m = (Measure) o;
422 return ObjectUtils.equals(metric, m.getMetric());