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.issue.internal.DefaultIssue;
34 import org.sonar.api.measures.*;
35 import org.sonar.api.resources.Project;
36 import org.sonar.api.resources.Resource;
37 import org.sonar.api.resources.Scopes;
38 import org.sonar.api.rule.RuleKey;
39 import org.sonar.api.rule.Severity;
40 import org.sonar.api.rules.Rule;
41 import org.sonar.api.rules.RuleFinder;
42 import org.sonar.api.rules.RulePriority;
43 import org.sonar.api.test.IsRuleMeasure;
44 import org.sonar.batch.components.Period;
45 import org.sonar.batch.components.TimeMachineConfiguration;
47 import java.util.Calendar;
48 import java.util.Collections;
49 import java.util.Date;
50 import java.util.List;
52 import static com.google.common.collect.Lists.newArrayList;
53 import static org.fest.assertions.Assertions.assertThat;
54 import static org.mockito.Matchers.any;
55 import static org.mockito.Matchers.anyDouble;
56 import static org.mockito.Matchers.argThat;
57 import static org.mockito.Matchers.eq;
58 import static org.mockito.Mockito.*;
60 public class CountUnresolvedIssuesDecoratorTest {
62 CountUnresolvedIssuesDecorator decorator;
63 TimeMachineConfiguration timeMachineConfiguration;
64 RuleFinder ruleFinder;
66 DecoratorContext context;
76 Date afterFiveDaysAgo;
80 public void before() {
81 ruleA1 = Rule.create().setRepositoryKey("ruleA1").setKey("ruleA1").setName("nameA1");
82 ruleA2 = Rule.create().setRepositoryKey("ruleA2").setKey("ruleA2").setName("nameA2");
83 ruleB1 = Rule.create().setRepositoryKey("ruleB1").setKey("ruleB1").setName("nameB1");
85 ruleFinder = mock(RuleFinder.class);
86 when(ruleFinder.findByKey(ruleA1.getRepositoryKey(), ruleA1.getKey())).thenReturn(ruleA1);
87 when(ruleFinder.findByKey(ruleA2.getRepositoryKey(), ruleA2.getKey())).thenReturn(ruleA2);
88 when(ruleFinder.findByKey(ruleB1.getRepositoryKey(), ruleB1.getKey())).thenReturn(ruleB1);
90 rightNow = new Date();
91 tenDaysAgo = DateUtils.addDays(rightNow, -10);
92 afterTenDaysAgo = DateUtils.addDays(tenDaysAgo, 1);
93 fiveDaysAgo = DateUtils.addDays(rightNow, -5);
94 afterFiveDaysAgo = DateUtils.addDays(fiveDaysAgo, 1);
95 sameSecond = DateUtils.truncate(rightNow, Calendar.SECOND);
97 timeMachineConfiguration = mock(TimeMachineConfiguration.class);
98 when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, fiveDaysAgo, afterFiveDaysAgo), new Period(2, tenDaysAgo, afterTenDaysAgo)));
100 project = mock(Project.class);
101 resource = mock(Resource.class);
102 context = mock(DecoratorContext.class);
103 when(context.getResource()).thenReturn(resource);
104 when(context.getProject()).thenReturn(project);
105 when(context.getMeasure(CoreMetrics.NEW_VIOLATIONS)).thenReturn(null);
107 issuable = mock(Issuable.class);
108 ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
109 when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
110 decorator = new CountUnresolvedIssuesDecorator(perspectives, ruleFinder, timeMachineConfiguration);
114 public void should_be_depended_upon_metric() {
115 assertThat(decorator.generatesIssuesMetrics()).hasSize(15);
119 public void should_count_issues() {
120 when(resource.getScope()).thenReturn(Scopes.PROJECT);
121 when(issuable.issues()).thenReturn(createIssues());
122 when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
124 decorator.decorate(resource, context);
126 verify(context).saveMeasure(CoreMetrics.VIOLATIONS, 4.0);
130 public void should_do_nothing_when_issuable_is_null() {
131 ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
132 when(perspectives.as(Issuable.class, resource)).thenReturn(null);
133 CountUnresolvedIssuesDecorator decorator = new CountUnresolvedIssuesDecorator(perspectives, ruleFinder, timeMachineConfiguration);
135 decorator.decorate(resource, context);
137 verifyZeroInteractions(context);
141 * See http://jira.codehaus.org/browse/SONAR-1729
144 public void should_not_count_issues_if_measure_already_exists() {
145 when(resource.getScope()).thenReturn(Scopes.PROJECT);
146 when(issuable.issues()).thenReturn(createIssues());
147 when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
148 when(context.getMeasure(CoreMetrics.VIOLATIONS)).thenReturn(new Measure(CoreMetrics.VIOLATIONS, 3000.0));
149 when(context.getMeasure(CoreMetrics.MAJOR_VIOLATIONS)).thenReturn(new Measure(CoreMetrics.MAJOR_VIOLATIONS, 500.0));
151 decorator.decorate(resource, context);
153 verify(context, never()).saveMeasure(eq(CoreMetrics.VIOLATIONS), anyDouble());// not changed
154 verify(context, never()).saveMeasure(eq(CoreMetrics.MAJOR_VIOLATIONS), anyDouble());// not changed
155 verify(context, times(1)).saveMeasure(eq(CoreMetrics.CRITICAL_VIOLATIONS), anyDouble());// did not exist
159 public void should_save_zero_on_projects() {
160 when(resource.getScope()).thenReturn(Scopes.PROJECT);
161 when(issuable.issues()).thenReturn(Lists.<Issue>newArrayList());
162 when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
164 decorator.decorate(resource, context);
166 verify(context).saveMeasure(CoreMetrics.VIOLATIONS, 0.0);
170 public void should_save_zero_on_directories() {
171 when(resource.getScope()).thenReturn(Scopes.DIRECTORY);
172 when(issuable.issues()).thenReturn(Lists.<Issue>newArrayList());
173 when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
175 decorator.decorate(resource, context);
177 verify(context).saveMeasure(CoreMetrics.VIOLATIONS, 0.0);
181 public void should_count_issues_by_severity() {
182 when(resource.getScope()).thenReturn(Scopes.PROJECT);
183 when(issuable.issues()).thenReturn(createIssues());
184 when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.<Measure>emptyList());
186 decorator.decorate(resource, context);
188 verify(context).saveMeasure(CoreMetrics.BLOCKER_VIOLATIONS, 0.0);
189 verify(context).saveMeasure(CoreMetrics.CRITICAL_VIOLATIONS, 2.0);
190 verify(context).saveMeasure(CoreMetrics.MAJOR_VIOLATIONS, 1.0);
191 verify(context).saveMeasure(CoreMetrics.MINOR_VIOLATIONS, 1.0);
192 verify(context).saveMeasure(CoreMetrics.INFO_VIOLATIONS, 0.0);
196 public void should_count_issues_per_rule() {
197 List<Issue> issues = newArrayList();
198 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()));
199 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()));
200 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()));
201 when(issuable.issues()).thenReturn(issues);
203 decorator.decorate(resource, context);
205 verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.CRITICAL_VIOLATIONS, ruleA1, 2.0)));
206 verify(context, never()).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MAJOR_VIOLATIONS, ruleA1, 0.0)));
207 verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MAJOR_VIOLATIONS, ruleA2, 1.0)));
211 public void same_rule_should_have_different_severities() {
212 List<Issue> issues = newArrayList();
213 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()));
214 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()));
215 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.MINOR.name()));
216 when(issuable.issues()).thenReturn(issues);
218 decorator.decorate(resource, context);
220 verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.CRITICAL_VIOLATIONS, ruleA1, 2.0)));
221 verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.MINOR_VIOLATIONS, ruleA1, 1.0)));
225 public void should_count_issues_after_date() {
226 List<Issue> issues = createIssuesForNewMetrics();
228 assertThat(decorator.countIssuesAfterDate(null, fiveDaysAgo)).isEqualTo(0);
229 assertThat(decorator.countIssuesAfterDate(issues, fiveDaysAgo)).isEqualTo(1); // 1 rightNow
230 assertThat(decorator.countIssuesAfterDate(issues, tenDaysAgo)).isEqualTo(3); // 1 rightNow + 2 fiveDaysAgo
231 assertThat(decorator.countIssuesAfterDate(issues, sameSecond)).isEqualTo(0); // 0
235 public void should_clear_cache_after_execution() {
236 Issue issue1 = new DefaultIssue().setRuleKey(RuleKey.of(ruleA1.getRepositoryKey(), ruleA1.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(rightNow);
237 Issue issue2 = new DefaultIssue().setRuleKey(RuleKey.of(ruleA2.getRepositoryKey(), ruleA2.getKey())).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(rightNow);
238 when(issuable.issues()).thenReturn(newArrayList(issue1)).thenReturn(newArrayList(issue2));
240 decorator.decorate(resource, context);
241 decorator.decorate(resource, context);
243 verify(context, times(2)).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 1.0, 1.0)));
244 verify(context, never()).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 2.0, 2.0)));
248 public void should_save_severity_new_issues() {
249 when(issuable.issues()).thenReturn(createIssuesForNewMetrics());
251 decorator.decorate(resource, context);
253 // remember : period1 is 5daysAgo, period2 is 10daysAgo
254 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 0.0, 0.0)));
255 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 1.0, 1.0)));
256 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_MAJOR_VIOLATIONS, 0.0, 1.0)));
257 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_MINOR_VIOLATIONS, 0.0, 1.0)));
258 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_INFO_VIOLATIONS, 0.0, 0.0)));
262 public void should_save_rule_new_issues() {
263 when(issuable.issues()).thenReturn(createIssuesForNewMetrics());
265 decorator.decorate(resource, context);
267 // remember : period1 is 5daysAgo, period2 is 10daysAgo
268 verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS, ruleA1, 1.0, 1.0)));
269 verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_MAJOR_VIOLATIONS, ruleA2, 0.0, 1.0)));
270 verify(context).saveMeasure(argThat(new IsVariationRuleMeasure(CoreMetrics.NEW_MINOR_VIOLATIONS, ruleB1, 0.0, 1.0)));
274 public void should_not_save_new_issues_if_measure_already_computed() {
275 when(context.getMeasure(CoreMetrics.NEW_VIOLATIONS)).thenReturn(new Measure());
276 when(issuable.issues()).thenReturn(createIssuesForNewMetrics());
278 decorator.decorate(resource, context);
280 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_BLOCKER_VIOLATIONS)));
281 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS)));
282 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_MAJOR_VIOLATIONS)));
283 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_MINOR_VIOLATIONS)));
284 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_INFO_VIOLATIONS)));
285 verify(context, never()).saveMeasure(argThat(new IsMetricMeasure(CoreMetrics.NEW_CRITICAL_VIOLATIONS)));
288 List<Issue> createIssues() {
289 List<Issue> issues = newArrayList();
290 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(Severity.CRITICAL).setStatus(Issue.STATUS_OPEN));
291 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(Severity.CRITICAL).setStatus(Issue.STATUS_REOPENED));
292 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(Severity.MAJOR).setStatus(Issue.STATUS_REOPENED));
293 issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(Severity.MINOR).setStatus(Issue.STATUS_OPEN));
297 List<Issue> createIssuesForNewMetrics() {
298 List<Issue> issues = newArrayList();
299 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(rightNow).setStatus(Issue.STATUS_OPEN));
300 issues.add(new DefaultIssue().setRuleKey(ruleA1.ruleKey()).setSeverity(RulePriority.CRITICAL.name()).setCreationDate(tenDaysAgo).setStatus(Issue.STATUS_OPEN));
301 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setCreationDate(fiveDaysAgo).setStatus(Issue.STATUS_REOPENED));
302 issues.add(new DefaultIssue().setRuleKey(ruleA2.ruleKey()).setSeverity(RulePriority.MAJOR.name()).setCreationDate(tenDaysAgo).setStatus(Issue.STATUS_REOPENED));
303 issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(RulePriority.MINOR.name()).setCreationDate(fiveDaysAgo).setStatus(Issue.STATUS_OPEN));
304 issues.add(new DefaultIssue().setRuleKey(ruleB1.ruleKey()).setSeverity(RulePriority.MINOR.name()).setCreationDate(tenDaysAgo).setStatus(Issue.STATUS_OPEN));
308 class IsVariationRuleMeasure extends ArgumentMatcher<Measure> {
309 Metric metric = null;
314 public IsVariationRuleMeasure(Metric metric, Rule rule, Double var1, Double var2) {
315 this.metric = metric;
321 public boolean matches(Object o) {
322 if (!(o instanceof RuleMeasure)) {
325 RuleMeasure m = (RuleMeasure) o;
326 return ObjectUtils.equals(metric, m.getMetric()) &&
327 ObjectUtils.equals(rule, m.getRule()) &&
328 ObjectUtils.equals(var1, m.getVariation1()) &&
329 ObjectUtils.equals(var2, m.getVariation2());
333 class IsVariationMeasure extends ArgumentMatcher<Measure> {
334 Metric metric = null;
338 public IsVariationMeasure(Metric metric, Double var1, Double var2) {
339 this.metric = metric;
344 public boolean matches(Object o) {
345 if (!(o instanceof Measure)) {
348 Measure m = (Measure) o;
349 return ObjectUtils.equals(metric, m.getMetric()) &&
350 ObjectUtils.equals(var1, m.getVariation1()) &&
351 ObjectUtils.equals(var2, m.getVariation2()) &&
352 !(m instanceof RuleMeasure);
356 class IsMetricMeasure extends ArgumentMatcher<Measure> {
357 Metric metric = null;
359 public IsMetricMeasure(Metric metric) {
360 this.metric = metric;
363 public boolean matches(Object o) {
364 if (!(o instanceof Measure)) {
367 Measure m = (Measure) o;
368 return ObjectUtils.equals(metric, m.getMetric());