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.technicaldebt;
23 import org.apache.commons.lang.ObjectUtils;
24 import org.junit.Before;
25 import org.junit.Test;
26 import org.junit.runner.RunWith;
27 import org.mockito.ArgumentMatcher;
28 import org.mockito.Mock;
29 import org.mockito.runners.MockitoJUnitRunner;
30 import org.sonar.api.batch.DecoratorContext;
31 import org.sonar.api.component.ResourcePerspectives;
32 import org.sonar.api.issue.Issuable;
33 import org.sonar.api.issue.Issue;
34 import org.sonar.api.issue.internal.DefaultIssue;
35 import org.sonar.api.issue.internal.FieldDiffs;
36 import org.sonar.api.issue.internal.WorkDayDuration;
37 import org.sonar.api.measures.CoreMetrics;
38 import org.sonar.api.measures.Measure;
39 import org.sonar.api.measures.Metric;
40 import org.sonar.api.resources.Resource;
41 import org.sonar.api.test.IsMeasure;
42 import org.sonar.batch.components.Period;
43 import org.sonar.batch.components.TimeMachineConfiguration;
44 import org.sonar.core.technicaldebt.TechnicalDebtConverter;
46 import java.util.Date;
48 import static com.google.common.collect.Lists.newArrayList;
49 import static org.fest.assertions.Assertions.assertThat;
50 import static org.mockito.Matchers.argThat;
51 import static org.mockito.Mockito.*;
53 @RunWith(MockitoJUnitRunner.class)
54 public class NewTechnicalDebtDecoratorTest {
56 NewTechnicalDebtDecorator decorator;
59 TimeMachineConfiguration timeMachineConfiguration;
68 DecoratorContext context;
71 TechnicalDebtConverter technicalDebtConverter;
80 WorkDayDuration oneDaysDebt = WorkDayDuration.of(0, 0, 1);
81 WorkDayDuration twoDaysDebt = WorkDayDuration.of(0, 0, 2);
82 WorkDayDuration fiveDaysDebt = WorkDayDuration.of(0, 0, 5);
86 when(technicalDebtConverter.toDays(oneDaysDebt)).thenReturn(1d);
87 when(technicalDebtConverter.toDays(twoDaysDebt)).thenReturn(2d);
88 when(technicalDebtConverter.toDays(fiveDaysDebt)).thenReturn(5d);
90 ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
91 when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
93 rightNow = new Date();
94 elevenDaysAgo = org.apache.commons.lang.time.DateUtils.addDays(rightNow, -11);
95 tenDaysAgo = org.apache.commons.lang.time.DateUtils.addDays(rightNow, -10);
96 nineDaysAgo = org.apache.commons.lang.time.DateUtils.addDays(rightNow, -9);
97 fiveDaysAgo = org.apache.commons.lang.time.DateUtils.addDays(rightNow, -5);
98 fourDaysAgo = org.apache.commons.lang.time.DateUtils.addDays(rightNow, -4);
100 when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, fiveDaysAgo, fiveDaysAgo), new Period(2, tenDaysAgo, tenDaysAgo)));
102 decorator = new NewTechnicalDebtDecorator(perspectives, timeMachineConfiguration, technicalDebtConverter);
106 public void generates_metrics() throws Exception {
107 assertThat(decorator.generatesMetrics()).hasSize(1);
111 public void execute_on_project() throws Exception {
112 assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
116 public void save_on_one_issue_with_one_new_changelog() {
117 Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setTechnicalDebt(twoDaysDebt).setChanges(
119 // changelog created at is null because it has just been created on the current analysis
120 new FieldDiffs().setDiff("technicalDebt", fromWorkDayDuration(oneDaysDebt), fromWorkDayDuration(twoDaysDebt)).setCreationDate(null)
123 when(issuable.issues()).thenReturn(newArrayList(issue));
125 decorator.decorate(resource, context);
127 // remember : period1 is 5daysAgo, period2 is 10daysAgo
128 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 1.0, 1.0)));
132 public void save_on_one_issue_with_changelog() {
133 Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setTechnicalDebt(fiveDaysDebt).setChanges(
135 new FieldDiffs().setDiff("technicalDebt", fromWorkDayDuration(twoDaysDebt), fromWorkDayDuration(fiveDaysDebt)).setCreationDate(null),
136 new FieldDiffs().setDiff("technicalDebt", fromWorkDayDuration(oneDaysDebt), fromWorkDayDuration(twoDaysDebt)).setCreationDate(fourDaysAgo)
139 when(issuable.issues()).thenReturn(newArrayList(issue));
141 decorator.decorate(resource, context);
143 // remember : period1 is 5daysAgo, period2 is 10daysAgo
144 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0, 4.0)));
148 public void save_on_one_issue_with_changelog_only_in_the_past() {
149 Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setTechnicalDebt(oneDaysDebt).setChanges(
151 // Change before all periods
152 new FieldDiffs().setDiff("technicalDebt", null, fromWorkDayDuration(oneDaysDebt)).setCreationDate(elevenDaysAgo)
155 when(issuable.issues()).thenReturn(newArrayList(issue));
157 decorator.decorate(resource, context);
159 // remember : period1 is 5daysAgo, period2 is 10daysAgo
160 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
164 public void save_on_one_issue_with_changelog_having_null_value() {
165 Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setTechnicalDebt(fiveDaysDebt).setChanges(
167 new FieldDiffs().setDiff("technicalDebt", null, fromWorkDayDuration(fiveDaysDebt)).setCreationDate(null),
168 new FieldDiffs().setDiff("technicalDebt", fromWorkDayDuration(oneDaysDebt), null).setCreationDate(fourDaysAgo),
169 new FieldDiffs().setDiff("technicalDebt", null, fromWorkDayDuration(oneDaysDebt)).setCreationDate(nineDaysAgo)
172 when(issuable.issues()).thenReturn(newArrayList(issue));
174 decorator.decorate(resource, context);
176 // remember : period1 is 5daysAgo, period2 is 10daysAgo
177 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0, 5.0)));
181 public void save_on_one_issue_with_changelog_and_periods_have_no_dates() {
182 when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null, null), new Period(2, null, null)));
184 Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setTechnicalDebt(fiveDaysDebt).setChanges(
186 new FieldDiffs().setDiff("technicalDebt", null, fromWorkDayDuration(fiveDaysDebt)).setCreationDate(null),
187 new FieldDiffs().setDiff("technicalDebt", fromWorkDayDuration(oneDaysDebt), null).setCreationDate(fourDaysAgo),
188 new FieldDiffs().setDiff("technicalDebt", null, fromWorkDayDuration(oneDaysDebt)).setCreationDate(nineDaysAgo)
191 when(issuable.issues()).thenReturn(newArrayList(issue));
193 decorator.decorate(resource, context);
195 // remember : period1 is 5daysAgo, period2 is 10daysAgo
196 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0, 5.0)));
200 public void save_on_one_issue_with_changelog_having_not_only_technical_debt_changes() {
201 Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setTechnicalDebt(fiveDaysDebt).setChanges(
204 .setDiff("actionPlan", "1.0", "1.1").setCreationDate(fourDaysAgo)
205 .setDiff("technicalDebt", fromWorkDayDuration(oneDaysDebt), fromWorkDayDuration(twoDaysDebt)).setCreationDate(fourDaysAgo)
208 when(issuable.issues()).thenReturn(newArrayList(issue));
210 decorator.decorate(resource, context);
212 // remember : period1 is 5daysAgo, period2 is 10daysAgo
213 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0, 4.0)));
217 public void save_on_issues_with_changelog() {
218 Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setTechnicalDebt(fiveDaysDebt).setChanges(
220 new FieldDiffs().setDiff("technicalDebt", fromWorkDayDuration(twoDaysDebt), fromWorkDayDuration(fiveDaysDebt)).setCreationDate(rightNow),
221 new FieldDiffs().setDiff("technicalDebt", fromWorkDayDuration(oneDaysDebt), fromWorkDayDuration(twoDaysDebt)).setCreationDate(fourDaysAgo),
222 new FieldDiffs().setDiff("technicalDebt", null, fromWorkDayDuration(oneDaysDebt)).setCreationDate(nineDaysAgo)
225 Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setTechnicalDebt(twoDaysDebt).setChanges(
227 new FieldDiffs().setDiff("technicalDebt", fromWorkDayDuration(oneDaysDebt), fromWorkDayDuration(twoDaysDebt)).setCreationDate(rightNow),
228 new FieldDiffs().setDiff("technicalDebt", null, fromWorkDayDuration(oneDaysDebt)).setCreationDate(nineDaysAgo)
231 when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
233 decorator.decorate(resource, context);
235 // remember : period1 is 5daysAgo, period2 is 10daysAgo
236 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0, 7.0)));
240 public void save_on_one_issue_without_changelog() {
241 when(issuable.issues()).thenReturn(newArrayList(
242 (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setTechnicalDebt(fiveDaysDebt))
245 decorator.decorate(resource, context);
247 // remember : period1 is 5daysAgo, period2 is 10daysAgo
248 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 5.0)));
252 public void save_on_one_issue_without_technical_debt_and_without_changelog() {
253 when(issuable.issues()).thenReturn(newArrayList(
254 (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setTechnicalDebt(null))
257 decorator.decorate(resource, context);
259 // remember : period1 is 5daysAgo, period2 is 10daysAgo
260 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
264 public void save_on_one_issue_without_changelog_and_periods_have_no_dates() {
265 when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null, null), new Period(2, null, null)));
267 when(issuable.issues()).thenReturn(newArrayList(
268 (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setTechnicalDebt(fiveDaysDebt))
271 decorator.decorate(resource, context);
273 // remember : period1 is null, period2 is null
274 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0, 5.0)));
278 public void save_on_issues_without_changelog() {
279 when(issuable.issues()).thenReturn(newArrayList(
280 (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setTechnicalDebt(fiveDaysDebt),
281 new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setTechnicalDebt(twoDaysDebt)
284 decorator.decorate(resource, context);
286 // remember : period1 is 5daysAgo, period2 is 10daysAgo
287 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 7.0)));
291 public void save_on_issues_with_changelog_and_issues_without_changelog() {
292 // issue1 and issue2 have changelog
293 Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setTechnicalDebt(fiveDaysDebt).setChanges(
295 new FieldDiffs().setDiff("technicalDebt", fromWorkDayDuration(twoDaysDebt), fromWorkDayDuration(fiveDaysDebt)).setCreationDate(rightNow),
296 new FieldDiffs().setDiff("technicalDebt", fromWorkDayDuration(oneDaysDebt), fromWorkDayDuration(twoDaysDebt)).setCreationDate(fourDaysAgo),
297 new FieldDiffs().setDiff("technicalDebt", null, fromWorkDayDuration(oneDaysDebt)).setCreationDate(nineDaysAgo)
300 Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setTechnicalDebt(twoDaysDebt).setChanges(
302 new FieldDiffs().setDiff("technicalDebt", fromWorkDayDuration(oneDaysDebt), fromWorkDayDuration(twoDaysDebt)).setCreationDate(rightNow),
303 new FieldDiffs().setDiff("technicalDebt", null, fromWorkDayDuration(oneDaysDebt)).setCreationDate(nineDaysAgo)
307 // issue3 and issue4 have no changelog
308 Issue issue3 = new DefaultIssue().setKey("C").setCreationDate(nineDaysAgo).setTechnicalDebt(fiveDaysDebt);
309 Issue issue4 = new DefaultIssue().setKey("D").setCreationDate(fiveDaysAgo).setTechnicalDebt(twoDaysDebt);
310 when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
312 decorator.decorate(resource, context);
314 // remember : period1 is 5daysAgo, period2 is 10daysAgo
315 verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0, 14.0)));
319 public void not_save_if_measure_already_computed() {
320 when(context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)).thenReturn(new Measure());
321 when(issuable.issues()).thenReturn(newArrayList(
322 (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setTechnicalDebt(fiveDaysDebt),
323 new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setTechnicalDebt(twoDaysDebt)
326 decorator.decorate(resource, context);
328 verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)));
331 private Long fromWorkDayDuration(WorkDayDuration workDayDuration){
332 return workDayDuration.toLong();
336 class IsVariationMeasure extends ArgumentMatcher<Measure> {
337 Metric metric = null;
341 public IsVariationMeasure(Metric metric, Double var1, Double var2) {
342 this.metric = metric;
347 public boolean matches(Object o) {
348 if (!(o instanceof Measure)) {
351 Measure m = (Measure) o;
352 return ObjectUtils.equals(metric, m.getMetric()) &&
353 ObjectUtils.equals(var1, m.getVariation1()) &&
354 ObjectUtils.equals(var2, m.getVariation2());