From 6c7dcb3a2af976d6cfdf7ac7dec2cedbe6013619 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 5 Mar 2014 19:22:20 +0100 Subject: [PATCH] SONAR-5056 Update Issue.debt from Long to Duration --- .../core/issue/IssueTrackingDecorator.java | 4 +- .../technicaldebt/TechnicalDebtDecorator.java | 2 +- .../core/widgets/issues/issues.html.erb | 2 +- .../widgets/technical_debt_pyramid.html.erb | 6 +-- .../issue/IssueTrackingDecoratorTest.java | 3 +- .../NewTechnicalDebtDecoratorTest.java | 39 ++++++++++--------- .../TechnicalDebtDecoratorTest.java | 17 ++++---- .../debt/IssueChangelogDebtCalculator.java | 4 +- .../org/sonar/batch/issue/ModuleIssues.java | 4 +- .../qualitygate/QualityGateVerifier.java | 3 +- .../IssueChangelogDebtCalculatorTest.java | 20 +++++----- .../batch/debt/RuleDebtCalculatorTest.java | 4 +- .../sonar/batch/issue/ModuleIssuesTest.java | 4 +- .../qualitygate/QualityGateVerifierTest.java | 3 +- .../java/org/sonar/core/i18n/DefaultI18n.java | 8 ++-- .../org/sonar/core/issue/IssueUpdater.java | 15 ++++--- .../org/sonar/core/issue/db/IssueDto.java | 7 ++-- .../org/sonar/core/i18n/DefaultI18nTest.java | 5 ++- .../sonar/core/issue/IssueUpdaterTest.java | 17 ++++---- .../org/sonar/core/issue/db/IssueDtoTest.java | 3 +- .../sonar/core/issue/db/IssueStorageTest.java | 5 ++- .../issue/workflow/IssueWorkflowTest.java | 1 + .../main/java/org/sonar/api/i18n/I18n.java | 5 ++- .../api/issue/internal/DefaultIssue.java | 12 ++++-- .../api/issue/internal/DefaultIssueTest.java | 5 ++- .../server/issue/IssueChangelogFormatter.java | 5 ++- .../server/issue/ws/IssueShowWsHandler.java | 5 ++- .../java/org/sonar/server/ui/JRubyI18n.java | 7 +++- .../main/webapp/WEB-INF/app/models/issue.rb | 9 ++++- .../WEB-INF/app/models/project_measure.rb | 2 +- .../server/issue/DefaultIssueFinderTest.java | 3 +- .../issue/IssueChangelogFormatterTest.java | 7 ++-- .../issue/ws/IssueShowWsHandlerTest.java | 9 +++-- .../org/sonar/server/ui/JRubyI18nTest.java | 11 +++++- 34 files changed, 152 insertions(+), 104 deletions(-) diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java index ce9be7292fd..789ef022ebd 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java @@ -38,6 +38,7 @@ import org.sonar.api.resources.ResourceUtils; import org.sonar.api.rules.ActiveRule; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; +import org.sonar.api.utils.Duration; import org.sonar.api.utils.KeyValueFormat; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.scan.LastSnapshots; @@ -176,7 +177,8 @@ public class IssueTrackingDecorator implements Decorator { updater.setPastLine(issue, ref.getLine()); updater.setPastMessage(issue, ref.getMessage(), changeContext); updater.setPastEffortToFix(issue, ref.getEffortToFix(), changeContext); - Long previousTechnicalDebt = ref.getDebt(); + Long debtInMinutes = ref.getDebt(); + Duration previousTechnicalDebt = debtInMinutes != null ? Duration.create(debtInMinutes) : null; updater.setPastTechnicalDebt(issue, previousTechnicalDebt, changeContext); } } diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java index 324678a7b5a..8d4c893f436 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java @@ -164,7 +164,7 @@ public final class TechnicalDebtDecorator implements Decorator { long debt = 0L; if (issues != null) { for (Issue issue : issues) { - Long currentDebt = ((DefaultIssue) issue).debt(); + Long currentDebt = ((DefaultIssue) issue).debtInMinutes(); if (currentDebt != null) { debt += currentDebt; } diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/issues.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/issues.html.erb index db627c8cdd8..660d6236804 100644 --- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/issues.html.erb +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/issues.html.erb @@ -71,7 +71,7 @@
<%= message('widget.rules.removed') -%>  - <%= Internal.i18n.formatWorkDuration(estimated_cleared_technical_debt) -%> + <%= Internal.i18n.formatLongWorkDuration(estimated_cleared_technical_debt) -%> <% end %> <% end %> diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/technical_debt_pyramid.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/technical_debt_pyramid.html.erb index 6ee8536cdfc..93c3cfd1e2b 100644 --- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/technical_debt_pyramid.html.erb +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/technical_debt_pyramid.html.erb @@ -115,7 +115,7 @@ - <%= Internal.i18n.formatWorkDuration(value.to_i) -%> + <%= Internal.i18n.formatLongWorkDuration(value.to_i) -%> <% if should_display_diff_measures %> <% if diff_by_characteristic_id[characteristic.id] %> <%= format_variation(measure) -%> @@ -125,13 +125,13 @@ <% end %> - <%= Internal.i18n.formatWorkDuration(cumulated.to_i) -%> + <%= Internal.i18n.formatLongWorkDuration(cumulated.to_i) -%> <% if should_display_diff_measures css_style = 'var' css_style += 'b' if total_diff < 0 css_style += 'w' if total_diff > 0 - diff_to_display = (total_diff < 0 ? '' : '+') + Internal.i18n.formatWorkDuration(total_diff.to_i) + diff_to_display = (total_diff < 0 ? '' : '+') + Internal.i18n.formatLongWorkDuration(total_diff.to_i) %> (<%= diff_to_display -%>) <% diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java index 912fcf8aac7..248934f9912 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java @@ -36,6 +36,7 @@ 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.utils.Duration; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.scan.LastSnapshots; import org.sonar.core.issue.IssueUpdater; @@ -519,7 +520,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase { verify(updater).setPastLine(eq(issue), eq(10)); verify(updater).setPastMessage(eq(issue), eq("Message"), any(IssueChangeContext.class)); verify(updater).setPastEffortToFix(eq(issue), eq(1.5), any(IssueChangeContext.class)); - verify(updater).setPastTechnicalDebt(eq(issue), eq(1L), any(IssueChangeContext.class)); + verify(updater).setPastTechnicalDebt(eq(issue), eq(Duration.create(1L)), any(IssueChangeContext.class)); } @Test diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecoratorTest.java index d3c5cb76b44..86e2e4843cc 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecoratorTest.java @@ -41,6 +41,7 @@ import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric; import org.sonar.api.resources.Resource; import org.sonar.api.test.IsMeasure; +import org.sonar.api.utils.Duration; import org.sonar.batch.components.Period; import org.sonar.batch.components.TimeMachineConfiguration; import org.sonar.batch.debt.IssueChangelogDebtCalculator; @@ -114,7 +115,7 @@ public class NewTechnicalDebtDecoratorTest { @Test public void save_on_one_issue_with_one_new_changelog() { - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(TWO_DAYS_IN_MINUTES).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges( newArrayList( // changelog created at is null because it has just been created on the current analysis new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(null) @@ -130,7 +131,7 @@ public class NewTechnicalDebtDecoratorTest { @Test public void save_on_one_issue_with_changelog() { - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(FIVE_DAYS_IN_MINUTES).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( newArrayList( new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(null), new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo) @@ -146,7 +147,7 @@ public class NewTechnicalDebtDecoratorTest { @Test public void save_on_one_issue_with_changelog_only_in_the_past() { - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(ONE_DAY_IN_MINUTES).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges( newArrayList( // Change before all periods new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(elevenDaysAgo) @@ -162,7 +163,7 @@ public class NewTechnicalDebtDecoratorTest { @Test public void save_on_one_issue_with_changelog_having_null_value() { - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(FIVE_DAYS_IN_MINUTES).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( newArrayList( new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null), new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo), @@ -181,7 +182,7 @@ public class NewTechnicalDebtDecoratorTest { public void save_on_one_issue_with_changelog_and_periods_have_no_dates() { when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null))); - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(FIVE_DAYS_IN_MINUTES).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( newArrayList( new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null), new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo), @@ -198,7 +199,7 @@ public class NewTechnicalDebtDecoratorTest { @Test public void save_on_one_issue_with_changelog_having_not_only_technical_debt_changes() { - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(FIVE_DAYS_IN_MINUTES).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( newArrayList( new FieldDiffs() .setDiff("actionPlan", "1.0", "1.1").setCreationDate(fourDaysAgo) @@ -215,14 +216,14 @@ public class NewTechnicalDebtDecoratorTest { @Test public void save_on_issues_with_changelog() { - Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(FIVE_DAYS_IN_MINUTES).setChanges( + Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( newArrayList( new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow), new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo), new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo) ) ); - Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(TWO_DAYS_IN_MINUTES).setChanges( + Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges( newArrayList( new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow), new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo) @@ -239,7 +240,7 @@ public class NewTechnicalDebtDecoratorTest { @Test public void save_on_one_issue_without_changelog() { when(issuable.issues()).thenReturn(newArrayList( - (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(FIVE_DAYS_IN_MINUTES)) + (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES))) ); decorator.decorate(resource, context); @@ -265,7 +266,7 @@ public class NewTechnicalDebtDecoratorTest { when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null))); when(issuable.issues()).thenReturn(newArrayList( - (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(FIVE_DAYS_IN_MINUTES)) + (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES))) ); decorator.decorate(resource, context); @@ -277,8 +278,8 @@ public class NewTechnicalDebtDecoratorTest { @Test public void save_on_issues_without_changelog() { when(issuable.issues()).thenReturn(newArrayList( - (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(FIVE_DAYS_IN_MINUTES), - new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(TWO_DAYS_IN_MINUTES) + (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)), + new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)) )); decorator.decorate(resource, context); @@ -290,14 +291,14 @@ public class NewTechnicalDebtDecoratorTest { @Test public void save_on_issues_with_changelog_and_issues_without_changelog() { // issue1 and issue2 have changelog - Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(FIVE_DAYS_IN_MINUTES).setChanges( + Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( newArrayList( new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow), new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo), new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo) ) ); - Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(TWO_DAYS_IN_MINUTES).setChanges( + Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges( newArrayList( new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow), new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo) @@ -305,8 +306,8 @@ public class NewTechnicalDebtDecoratorTest { ); // issue3 and issue4 have no changelog - Issue issue3 = new DefaultIssue().setKey("C").setCreationDate(nineDaysAgo).setDebt(FIVE_DAYS_IN_MINUTES); - Issue issue4 = new DefaultIssue().setKey("D").setCreationDate(fiveDaysAgo).setDebt(TWO_DAYS_IN_MINUTES); + Issue issue3 = new DefaultIssue().setKey("C").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)); + Issue issue4 = new DefaultIssue().setKey("D").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)); when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4)); decorator.decorate(resource, context); @@ -319,8 +320,8 @@ public class NewTechnicalDebtDecoratorTest { public void not_save_if_measure_already_computed() { when(context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)).thenReturn(new Measure()); when(issuable.issues()).thenReturn(newArrayList( - (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(FIVE_DAYS_IN_MINUTES), - new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(TWO_DAYS_IN_MINUTES) + (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)), + new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)) )); decorator.decorate(resource, context); @@ -333,7 +334,7 @@ public class NewTechnicalDebtDecoratorTest { */ @Test public void not_return_negative_debt() { - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(ONE_DAY_IN_MINUTES).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges( newArrayList( // changelog created at is null because it has just been created on the current analysis new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, ONE_DAY_IN_MINUTES).setCreationDate(null) diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java index 9690d6a888a..248e2921192 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java @@ -50,6 +50,7 @@ import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement; import org.sonar.api.test.IsMeasure; +import org.sonar.api.utils.Duration; import java.util.List; @@ -130,7 +131,7 @@ public class TechnicalDebtDecoratorTest { @Test public void add_technical_debt_from_one_issue_and_no_parent() throws Exception { - Issue issue = createIssue("rule1", "repo1").setDebt(ONE_DAY_IN_MINUTES); + Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); when(issuable.issues()).thenReturn(newArrayList(issue)); Requirement requirement = mock(Requirement.class); @@ -159,7 +160,7 @@ public class TechnicalDebtDecoratorTest { @Test public void add_technical_debt_from_one_issue_and_propagate_to_parents() throws Exception { - Issue issue = createIssue("rule1", "repo1").setDebt(ONE_DAY_IN_MINUTES); + Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); when(issuable.issues()).thenReturn(newArrayList(issue)); DefaultCharacteristic parentCharacteristic = new DefaultCharacteristic().setKey("parentCharacteristic"); @@ -183,10 +184,10 @@ public class TechnicalDebtDecoratorTest { Long technicalDebt1 = ONE_DAY_IN_MINUTES; Long technicalDebt2 = 2 * ONE_DAY_IN_MINUTES; - Issue issue1 = createIssue("rule1", "repo1").setDebt(technicalDebt1); - Issue issue2 = createIssue("rule1", "repo1").setDebt(technicalDebt1); - Issue issue3 = createIssue("rule2", "repo2").setDebt(technicalDebt2); - Issue issue4 = createIssue("rule2", "repo2").setDebt(technicalDebt2); + Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1)); + Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1)); + Issue issue3 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2)); + Issue issue4 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2)); when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4)); DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("rootCharacteristic"); @@ -209,8 +210,8 @@ public class TechnicalDebtDecoratorTest { @Test public void add_technical_debt_from_children_measures() throws Exception { - Issue issue1 = createIssue("rule1", "repo1").setDebt(ONE_DAY_IN_MINUTES); - Issue issue2 = createIssue("rule1", "repo1").setDebt(ONE_DAY_IN_MINUTES); + Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); + Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); when(issuable.issues()).thenReturn(newArrayList(issue1, issue2)); DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("rootCharacteristic"); diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/IssueChangelogDebtCalculator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/IssueChangelogDebtCalculator.java index 276671185f0..9b352136b6e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/IssueChangelogDebtCalculator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/debt/IssueChangelogDebtCalculator.java @@ -31,6 +31,7 @@ import org.sonar.core.issue.IssueUpdater; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import java.util.*; import static com.google.common.collect.Lists.newArrayList; @@ -39,7 +40,7 @@ public class IssueChangelogDebtCalculator implements BatchComponent { @CheckForNull public Long calculateNewTechnicalDebt(Issue issue, @Nullable Date periodDate) { - Long debt = ((DefaultIssue) issue).debt(); + Long debt = ((DefaultIssue) issue).debtInMinutes(); Date periodDatePlusOneSecond = periodDate != null ? DateUtils.addSeconds(periodDate, 1) : null; if (isAfter(issue.creationDate(), periodDatePlusOneSecond)) { return debt; @@ -129,5 +130,4 @@ public class IssueChangelogDebtCalculator implements BatchComponent { return (currentDate != null) && (pastDate == null || (DateUtils.truncatedCompareTo(currentDate, pastDate, Calendar.SECOND) <= 0)); } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java index 442dfbd71f9..d19ad5de7ad 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java @@ -28,6 +28,7 @@ import org.sonar.api.rules.ActiveRule; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.Violation; +import org.sonar.api.utils.Duration; import org.sonar.api.utils.MessageException; import org.sonar.batch.debt.RuleDebtCalculator; import org.sonar.core.issue.DefaultIssueBuilder; @@ -111,7 +112,8 @@ public class ModuleIssues { if (issue.severity() == null) { issue.setSeverity(activeRule.getSeverity().name()); } - issue.setDebt(technicalDebtCalculator.calculateTechnicalDebt(issue.ruleKey(), issue.effortToFix())); + Long debt = technicalDebtCalculator.calculateTechnicalDebt(issue.ruleKey(), issue.effortToFix()); + issue.setDebt(debt != null ? Duration.create(debt) : null); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java index 50d09eed2f5..016d24a6d25 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java +++ b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java @@ -32,6 +32,7 @@ import org.sonar.api.profiles.Alert; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; +import org.sonar.api.utils.Duration; import org.sonar.core.timemachine.Periods; import java.util.Collection; @@ -151,7 +152,7 @@ public class QualityGateVerifier implements Decorator { private String alertValue(Alert alert, Metric.Level level){ String value = level.equals(Metric.Level.ERROR) ? alert.getValueError() : alert.getValueWarning(); if (alert.getMetric().getType().equals(Metric.ValueType.WORK_DUR)) { - return i18n.formatWorkDuration(Locale.ENGLISH, Long.parseLong(value)); + return i18n.formatWorkDuration(Locale.ENGLISH, Duration.create(Long.parseLong(value))); } else { return value; } diff --git a/sonar-batch/src/test/java/org/sonar/batch/debt/IssueChangelogDebtCalculatorTest.java b/sonar-batch/src/test/java/org/sonar/batch/debt/IssueChangelogDebtCalculatorTest.java index 479b8e2f888..ca022819bc8 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/debt/IssueChangelogDebtCalculatorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/debt/IssueChangelogDebtCalculatorTest.java @@ -23,11 +23,10 @@ package org.sonar.batch.debt; import org.apache.commons.lang.time.DateUtils; import org.junit.Before; import org.junit.Test; -import org.sonar.api.CoreProperties; -import org.sonar.api.config.Settings; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.FieldDiffs; +import org.sonar.api.utils.Duration; import java.util.Date; @@ -51,17 +50,18 @@ public class IssueChangelogDebtCalculatorTest { long twoDays = 2 * HOURS_IN_DAY * 60 * 60L; long fiveDays = 5 * HOURS_IN_DAY * 60 * 60L; + Duration oneDayDebt = Duration.create(oneDay); + Duration twoDaysDebt = Duration.create(twoDays); + Duration fiveDaysDebt = Duration.create(fiveDays); + @Before public void setUp() throws Exception { - Settings settings = new Settings(); - settings.setProperty(CoreProperties.HOURS_IN_DAY, Integer.toString(HOURS_IN_DAY)); - issueChangelogDebtCalculator = new IssueChangelogDebtCalculator(); } @Test public void calculate_new_technical_debt_with_one_diff_in_changelog() throws Exception { - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(twoDays).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(twoDaysDebt).setChanges( newArrayList( // changelog created at is null because it has just been created on the current analysis new FieldDiffs().setDiff("technicalDebt", oneDay, twoDays).setCreationDate(null) @@ -76,7 +76,7 @@ public class IssueChangelogDebtCalculatorTest { @Test public void calculate_new_technical_debt_with_many_diffs_in_changelog() throws Exception { - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(fiveDays).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(fiveDaysDebt).setChanges( newArrayList( new FieldDiffs().setDiff("technicalDebt", twoDays, fiveDays).setCreationDate(null), new FieldDiffs().setDiff("technicalDebt", oneDay, twoDays).setCreationDate(fourDaysAgo) @@ -90,7 +90,7 @@ public class IssueChangelogDebtCalculatorTest { @Test public void changelog_can_be_in_wrong_order() { - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(fiveDays).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(fiveDaysDebt).setChanges( newArrayList( // 3rd new FieldDiffs().setDiff("technicalDebt", null, oneDay).setCreationDate(nineDaysAgo), @@ -107,7 +107,7 @@ public class IssueChangelogDebtCalculatorTest { @Test public void calculate_new_technical_debt_with_null_date() throws Exception { - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(twoDays).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(twoDaysDebt).setChanges( newArrayList( new FieldDiffs().setDiff("technicalDebt", oneDay, twoDays).setCreationDate(null) ) @@ -137,7 +137,7 @@ public class IssueChangelogDebtCalculatorTest { @Test public void not_return_negative_debt() { - Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(oneDay).setChanges( + Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(oneDayDebt).setChanges( newArrayList( new FieldDiffs().setDiff("technicalDebt", twoDays, oneDay).setCreationDate(null) ) diff --git a/sonar-batch/src/test/java/org/sonar/batch/debt/RuleDebtCalculatorTest.java b/sonar-batch/src/test/java/org/sonar/batch/debt/RuleDebtCalculatorTest.java index 59cd9c3efbd..c53cdcdc2d5 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/debt/RuleDebtCalculatorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/debt/RuleDebtCalculatorTest.java @@ -48,9 +48,7 @@ public class RuleDebtCalculatorTest { @Before public void before() { - Settings settings = new Settings(); - settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY); - calculator = new RuleDebtCalculator(model, settings); + calculator = new RuleDebtCalculator(model, new Settings().setProperty(CoreProperties.HOURS_IN_DAY, 8)); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java index 0835389fdf0..1150211081e 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java @@ -34,6 +34,7 @@ import org.sonar.api.resources.Resource; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.api.rules.*; +import org.sonar.api.utils.Duration; import org.sonar.api.utils.MessageException; import org.sonar.batch.debt.RuleDebtCalculator; @@ -277,7 +278,6 @@ public class ModuleIssuesTest { Date analysisDate = new Date(); when(project.getAnalysisDate()).thenReturn(analysisDate); - DefaultIssue issue = new DefaultIssue() .setKey("ABCDE") .setRuleKey(SQUID_RULE_KEY) @@ -291,7 +291,7 @@ public class ModuleIssuesTest { ArgumentCaptor argument = ArgumentCaptor.forClass(DefaultIssue.class); verify(cache).put(argument.capture()); - assertThat(argument.getValue().debt()).isEqualTo(debt); + assertThat(argument.getValue().debt()).isEqualTo(Duration.create(debt)); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java index 8cfc816b46c..bf1d149df9c 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java @@ -37,6 +37,7 @@ import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.test.IsMeasure; +import org.sonar.api.utils.Duration; import org.sonar.core.timemachine.Periods; import java.util.Locale; @@ -350,7 +351,7 @@ public class QualityGateVerifierTest { // metric name is declared in l10n bundle when(i18n.message(any(Locale.class), eq("metric.tech_debt.name"), anyString())).thenReturn("The Debt"); - when(i18n.formatWorkDuration(any(Locale.class), eq(3600L))).thenReturn("1h"); + when(i18n.formatWorkDuration(any(Locale.class), eq(Duration.create(3600L)))).thenReturn("1h"); when(context.getMeasure(metric)).thenReturn(new Measure(metric, 1800d)); projectAlerts.addAll(Lists.newArrayList(new Alert(null, metric, Alert.OPERATOR_SMALLER, "3600", null))); diff --git a/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java b/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java index 8be028d4134..eebe7325cd7 100644 --- a/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java +++ b/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java @@ -30,6 +30,7 @@ import org.sonar.api.ServerExtension; import org.sonar.api.i18n.I18n; import org.sonar.api.platform.PluginMetadata; import org.sonar.api.platform.PluginRepository; +import org.sonar.api.utils.Duration; import org.sonar.api.utils.SonarException; import org.sonar.api.utils.System2; @@ -148,11 +149,12 @@ public class DefaultI18n implements I18n, ServerExtension, BatchExtension, Start } @Override - public String formatWorkDuration(Locale locale, long duration) { - if (duration == 0) { + public String formatWorkDuration(Locale locale, Duration duration) { + Long durationInMinutes = duration.toMinutes(); + if (durationInMinutes == 0) { return "0"; } - List results = workDurationFormatter.format(duration); + List results = workDurationFormatter.format(durationInMinutes); StringBuilder message = new StringBuilder(); for (WorkDurationFormatter.Result result : results) { if (" ".equals(result.key())) { diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java index 754ad01f50b..8a2a57b0983 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java @@ -29,8 +29,10 @@ import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.DefaultIssueComment; import org.sonar.api.issue.internal.IssueChangeContext; import org.sonar.api.user.User; +import org.sonar.api.utils.Duration; import javax.annotation.Nullable; + import java.util.Calendar; import java.util.Date; @@ -198,11 +200,11 @@ public class IssueUpdater implements BatchComponent, ServerComponent { return setEffortToFix(issue, currentEffort, context); } - public boolean setTechnicalDebt(DefaultIssue issue, @Nullable Long value, IssueChangeContext context) { - Long oldValue = issue.debt(); + public boolean setTechnicalDebt(DefaultIssue issue, @Nullable Duration value, IssueChangeContext context) { + Duration oldValue = issue.debt(); if (!Objects.equal(value, oldValue)) { - issue.setDebt(value); - issue.setFieldChange(context, TECHNICAL_DEBT, oldValue, value); + issue.setDebt(value != null ? value : null); + issue.setFieldChange(context, TECHNICAL_DEBT, oldValue != null ? oldValue.toMinutes() : null, value != null ? value.toMinutes() : null); issue.setUpdateDate(context.date()); issue.setChanged(true); return true; @@ -210,8 +212,8 @@ public class IssueUpdater implements BatchComponent, ServerComponent { return false; } - public boolean setPastTechnicalDebt(DefaultIssue issue, @Nullable Long previousTechnicalDebt, IssueChangeContext context) { - Long currentTechnicalDebt = issue.debt(); + public boolean setPastTechnicalDebt(DefaultIssue issue, @Nullable Duration previousTechnicalDebt, IssueChangeContext context) { + Duration currentTechnicalDebt = issue.debt(); issue.setDebt(previousTechnicalDebt); return setTechnicalDebt(issue, currentTechnicalDebt, context); } @@ -244,4 +246,5 @@ public class IssueUpdater implements BatchComponent, ServerComponent { } return false; } + } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java index e95036f788d..da2bb38b728 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java @@ -25,6 +25,7 @@ import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.Duration; import org.sonar.api.utils.KeyValueFormat; import javax.annotation.CheckForNull; @@ -367,7 +368,7 @@ public final class IssueDto implements Serializable { .setLine(issue.line()) .setMessage(issue.message()) .setEffortToFix(issue.effortToFix()) - .setDebt(issue.debt()) + .setDebt(issue.debtInMinutes()) .setResolution(issue.resolution()) .setStatus(issue.status()) .setSeverity(issue.severity()) @@ -396,7 +397,7 @@ public final class IssueDto implements Serializable { .setLine(issue.line()) .setMessage(issue.message()) .setEffortToFix(issue.effortToFix()) - .setDebt(issue.debt()) + .setDebt(issue.debtInMinutes()) .setResolution(issue.resolution()) .setStatus(issue.status()) .setSeverity(issue.severity()) @@ -421,7 +422,7 @@ public final class IssueDto implements Serializable { issue.setResolution(resolution); issue.setMessage(message); issue.setEffortToFix(effortToFix); - issue.setDebt(debt); + issue.setDebt(debt != null ? Duration.create(debt) : null); issue.setLine(line); issue.setSeverity(severity); issue.setReporter(reporter); diff --git a/sonar-core/src/test/java/org/sonar/core/i18n/DefaultI18nTest.java b/sonar-core/src/test/java/org/sonar/core/i18n/DefaultI18nTest.java index f225a1b0e40..954f4802092 100644 --- a/sonar-core/src/test/java/org/sonar/core/i18n/DefaultI18nTest.java +++ b/sonar-core/src/test/java/org/sonar/core/i18n/DefaultI18nTest.java @@ -27,6 +27,7 @@ import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.platform.PluginMetadata; import org.sonar.api.platform.PluginRepository; import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.Duration; import org.sonar.api.utils.System2; import java.net.URL; @@ -208,12 +209,12 @@ public class DefaultI18nTest { new WorkDurationFormatter.Result(" ", null), new WorkDurationFormatter.Result("work_duration.x_minutes", 1) )); - assertThat(manager.formatWorkDuration(Locale.ENGLISH, 10)).isEqualTo("5d 2h 1min"); + assertThat(manager.formatWorkDuration(Locale.ENGLISH, Duration.create(10))).isEqualTo("5d 2h 1min"); } @Test public void format_work_duration_when_0() { - assertThat(manager.formatWorkDuration(Locale.ENGLISH, 0)).isEqualTo("0"); + assertThat(manager.formatWorkDuration(Locale.ENGLISH, Duration.create(0))).isEqualTo("0"); } static URLClassLoader newCoreClassloader() { diff --git a/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java b/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java index 8f3c28a01c3..0f003bec50f 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java @@ -26,6 +26,7 @@ import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.FieldDiffs; import org.sonar.api.issue.internal.IssueChangeContext; import org.sonar.api.user.User; +import org.sonar.api.utils.Duration; import org.sonar.core.user.DefaultUser; import java.util.Date; @@ -372,8 +373,8 @@ public class IssueUpdaterTest { @Test public void set_past_technical_debt() throws Exception { - long newDebt = 15 * 8 * 60 * 60; - long previousDebt = 10 * 8 * 60 * 60; + Duration newDebt = Duration.create(15 * 8 * 60); + Duration previousDebt = Duration.create(10 * 8 * 60); issue.setDebt(newDebt); boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context); assertThat(updated).isTrue(); @@ -381,13 +382,13 @@ public class IssueUpdaterTest { assertThat(issue.mustSendNotifications()).isFalse(); FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); - assertThat(diff.oldValue()).isEqualTo(previousDebt); - assertThat(diff.newValue()).isEqualTo(newDebt); + assertThat(diff.oldValue()).isEqualTo(10L * 8 * 60); + assertThat(diff.newValue()).isEqualTo(15L * 8 * 60); } @Test public void set_past_technical_debt_without_previous_value() throws Exception { - long newDebt = 15 * 8 * 60 * 60; + Duration newDebt = Duration.create(15 * 8 * 60); issue.setDebt(newDebt); boolean updated = updater.setPastTechnicalDebt(issue, null, context); assertThat(updated).isTrue(); @@ -396,20 +397,20 @@ public class IssueUpdaterTest { FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); assertThat(diff.oldValue()).isNull(); - assertThat(diff.newValue()).isEqualTo(newDebt); + assertThat(diff.newValue()).isEqualTo(15L * 8 * 60); } @Test public void set_past_technical_debt_with_null_new_value() throws Exception { issue.setDebt(null); - long previousDebt = 10 * 8 * 60 * 60; + Duration previousDebt = Duration.create(10 * 8 * 60); boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context); assertThat(updated).isTrue(); assertThat(issue.debt()).isNull(); assertThat(issue.mustSendNotifications()).isFalse(); FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); - assertThat(diff.oldValue()).isEqualTo(previousDebt); + assertThat(diff.oldValue()).isEqualTo(10L * 8 * 60); assertThat(diff.newValue()).isNull(); } diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDtoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDtoTest.java index 571782bdb15..e1040723d00 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDtoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDtoTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.utils.Duration; import java.util.Calendar; import java.util.Date; @@ -87,7 +88,7 @@ public class IssueDtoTest { assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED); assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FALSE_POSITIVE); assertThat(issue.effortToFix()).isEqualTo(15.0); - assertThat(issue.debt()).isEqualTo(10L); + assertThat(issue.debt()).isEqualTo(Duration.create(10L)); assertThat(issue.line()).isEqualTo(6); assertThat(issue.severity()).isEqualTo("BLOCKER"); assertThat(issue.message()).isEqualTo("message"); diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueStorageTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueStorageTest.java index 4a399e34fdf..aad75923ec7 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueStorageTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueStorageTest.java @@ -28,6 +28,7 @@ import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RuleQuery; import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.Duration; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.persistence.MyBatis; @@ -53,7 +54,7 @@ public class IssueStorageTest extends AbstractDaoTestCase { .setRuleKey(RuleKey.of("squid", "AvoidCycle")) .setLine(5000) - .setDebt(10L) + .setDebt(Duration.create(10L)) .setReporter("emmerik") .setResolution("OPEN") .setStatus("OPEN") @@ -87,7 +88,7 @@ public class IssueStorageTest extends AbstractDaoTestCase { // updated fields .setLine(5000) - .setDebt(10L) + .setDebt(Duration.create(10L)) .setChecksum("FFFFF") .setAuthorLogin("simon") .setAssignee("loic") diff --git a/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java b/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java index b42d1b691f4..da84dee6ab9 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java @@ -31,6 +31,7 @@ import org.sonar.api.rule.RuleKey; import org.sonar.core.issue.IssueUpdater; import javax.annotation.Nullable; + import java.util.Calendar; import java.util.Collection; import java.util.Date; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/i18n/I18n.java b/sonar-plugin-api/src/main/java/org/sonar/api/i18n/I18n.java index c11356bd7c0..e30990f12dc 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/i18n/I18n.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/i18n/I18n.java @@ -21,6 +21,7 @@ package org.sonar.api.i18n; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; +import org.sonar.api.utils.Duration; import javax.annotation.Nullable; @@ -105,10 +106,10 @@ public interface I18n extends ServerComponent, BatchComponent { /** * Return the formatted work duration. *
- * Example : format(Locale.ENGLISH, WorkDuration.create(10, 2, 0, 8)) -> 10d 2h + * Example : format(Locale.ENGLISH, Duration.create(10 * 24 * 60 + 2 * 60)) -> 10d 2h * * @since 4.3 */ - String formatWorkDuration(Locale locale, long duration); + String formatWorkDuration(Locale locale, Duration duration); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java index 7ef2cc6c6f6..02547538274 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java @@ -33,6 +33,7 @@ import org.sonar.api.issue.Issue; import org.sonar.api.issue.IssueComment; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; +import org.sonar.api.utils.Duration; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -59,7 +60,7 @@ public class DefaultIssue implements Issue { private String message; private Integer line; private Double effortToFix; - private Long debt; + private Duration debt; private String status; private String resolution; private String reporter; @@ -209,11 +210,16 @@ public class DefaultIssue implements Issue { * Elapsed time to fix the issue */ @CheckForNull - public Long debt() { + public Duration debt() { return debt; } - public DefaultIssue setDebt(@Nullable Long t) { + @CheckForNull + public Long debtInMinutes(){ + return debt != null ? debt.toMinutes() : null; + } + + public DefaultIssue setDebt(@Nullable Duration t) { this.debt = t; return this; } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java index f3bd658e70b..227a6e767b5 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import org.sonar.api.issue.Issue; import org.sonar.api.issue.IssueComment; import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.Duration; import java.text.SimpleDateFormat; import java.util.List; @@ -50,7 +51,7 @@ public class DefaultIssueTest { .setMessage("a message") .setLine(7) .setEffortToFix(1.2d) - .setDebt(28800L) + .setDebt(Duration.create(28800L)) .setActionPlanKey("BCDE") .setStatus(Issue.STATUS_CLOSED) .setResolution(Issue.RESOLUTION_FIXED) @@ -79,7 +80,7 @@ public class DefaultIssueTest { assertThat(issue.message()).isEqualTo("a message"); assertThat(issue.line()).isEqualTo(7); assertThat(issue.effortToFix()).isEqualTo(1.2d); - assertThat(issue.debt()).isEqualTo(28800L); + assertThat(issue.debt()).isEqualTo(Duration.create(28800L)); assertThat(issue.actionPlanKey()).isEqualTo("BCDE"); assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED); assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FIXED); diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java b/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java index d33f3089aea..d87e499e3e2 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java @@ -22,6 +22,7 @@ package org.sonar.server.issue; import org.sonar.api.ServerComponent; import org.sonar.api.i18n.I18n; import org.sonar.api.issue.internal.FieldDiffs; +import org.sonar.api.utils.Duration; import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; @@ -71,10 +72,10 @@ public class IssueChangelogFormatter implements ServerComponent { String oldValueString = oldValue != null && !"".equals(oldValue) ? oldValue.toString() : null; if (IssueUpdater.TECHNICAL_DEBT.equals(key)) { if (newValueString != null) { - newValueString = i18n.formatWorkDuration(UserSession.get().locale(), Long.parseLong(newValueString)); + newValueString = i18n.formatWorkDuration(UserSession.get().locale(), Duration.create(Long.parseLong(newValueString))); } if (oldValueString != null) { - oldValueString = i18n.formatWorkDuration(UserSession.get().locale(), Long.parseLong(oldValueString)); + oldValueString = i18n.formatWorkDuration(UserSession.get().locale(), Duration.create(Long.parseLong(oldValueString))); } } return new IssueChangelogDiffFormat(oldValueString, newValueString); diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java index 0f588f7fd47..116b32da940 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java @@ -33,6 +33,7 @@ import org.sonar.api.server.ws.Response; import org.sonar.api.technicaldebt.server.Characteristic; import org.sonar.api.user.User; import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.Duration; import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; @@ -98,7 +99,7 @@ public class IssueShowWsHandler implements RequestHandler { private void writeIssue(IssueQueryResult result, DefaultIssue issue, JsonWriter json) { String actionPlanKey = issue.actionPlanKey(); ActionPlan actionPlan = result.actionPlan(issue); - Long technicalDebt = issue.debt(); + Duration debt = issue.debt(); Date updateDate = issue.updateDate(); Date closeDate = issue.closeDate(); @@ -114,7 +115,7 @@ public class IssueShowWsHandler implements RequestHandler { .prop("author", issue.authorLogin()) .prop("actionPlan", actionPlanKey) .prop("actionPlanName", actionPlan != null ? actionPlan.name() : null) - .prop("debt", technicalDebt != null ? i18n.formatWorkDuration(UserSession.get().locale(), technicalDebt) : null) + .prop("debt", debt != null ? i18n.formatWorkDuration(UserSession.get().locale(), debt) : null) .prop("creationDate", DateUtils.formatDateTime(issue.creationDate())) .prop("fCreationDate", formatDate(issue.creationDate())) .prop("updateDate", updateDate != null ? DateUtils.formatDateTime(updateDate) : null) diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyI18n.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyI18n.java index 3931567b6fb..80a6ba85320 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyI18n.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyI18n.java @@ -23,6 +23,7 @@ import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; import org.sonar.api.ServerComponent; import org.sonar.api.i18n.I18n; +import org.sonar.api.utils.Duration; import org.sonar.core.i18n.GwtI18n; import org.sonar.server.user.UserSession; @@ -86,8 +87,12 @@ public class JRubyI18n implements ServerComponent { return i18n.ageFromNow(UserSession.get().locale(), date); } - public String formatWorkDuration(long duration) { + public String formatWorkDuration(Duration duration) { return i18n.formatWorkDuration(UserSession.get().locale(), duration); } + public String formatLongWorkDuration(long duration) { + return formatWorkDuration(Duration.create(duration)); + } + } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb index 59b3edb73d6..2c99796de44 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb @@ -75,8 +75,8 @@ class Issue hash_diff = {} hash_diff[:key] = key if key == 'technicalDebt' - hash_diff[:newValue] = debt_to_hash(diff.newValue()) if diff.newValue.present? - hash_diff[:oldValue] = debt_to_hash(diff.oldValue()) if diff.oldValue.present? + hash_diff[:newValue] = long_debt_to_hash(diff.newValue()) if diff.newValue.present? + hash_diff[:oldValue] = long_debt_to_hash(diff.oldValue()) if diff.oldValue.present? else hash_diff[:newValue] = diff.newValue() if diff.newValue.present? hash_diff[:oldValue] = diff.oldValue() if diff.oldValue.present? @@ -93,6 +93,11 @@ class Issue private def self.debt_to_hash(debt) + long_debt_to_hash(debt.toMinutes) + end + + def self.long_debt_to_hash(debt) + # TODO do not use toWorkDuration() but calculate days, hours and minutes from the duration and from hoursInDay property work_duration = Internal.technical_debt.toWorkDuration(debt.to_i) { :days => work_duration.days(), diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/project_measure.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/project_measure.rb index f600e43430d..0198b35e57a 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/project_measure.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/project_measure.rb @@ -164,7 +164,7 @@ class ProjectMeasure < ActiveRecord::Base end def work_duration_formatted_value(value) - Internal.i18n.formatWorkDuration(value.to_i) + Internal.i18n.formatLongWorkDuration(value.to_i) end def color diff --git a/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java b/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java index f35b8b0cd7f..33c965d5113 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java @@ -33,6 +33,7 @@ import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.rules.Rule; import org.sonar.api.user.User; import org.sonar.api.user.UserFinder; +import org.sonar.api.utils.Duration; import org.sonar.core.component.ComponentDto; import org.sonar.core.issue.DefaultActionPlan; import org.sonar.core.issue.db.IssueChangeDao; @@ -333,7 +334,7 @@ public class assertThat(results.issues()).hasSize(1); DefaultIssue result = (DefaultIssue) results.issues().iterator().next(); - assertThat(result.debt()).isEqualTo(10L); + assertThat(result.debt()).isEqualTo(Duration.create(10L)); } } diff --git a/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java b/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java index 1dcb81c46c7..98323c582b6 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java @@ -26,6 +26,7 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.i18n.I18n; import org.sonar.api.issue.internal.FieldDiffs; +import org.sonar.api.utils.Duration; import java.util.List; import java.util.Locale; @@ -127,8 +128,8 @@ public class IssueChangelogFormatterTest { FieldDiffs diffs = new FieldDiffs(); diffs.setDiff("technicalDebt", "18000", "28800"); - when(i18n.formatWorkDuration(any(Locale.class), eq(18000L))).thenReturn("5 hours"); - when(i18n.formatWorkDuration(any(Locale.class), eq(28800L))).thenReturn("1 days"); + when(i18n.formatWorkDuration(any(Locale.class), eq(Duration.create(18000L)))).thenReturn("5 hours"); + when(i18n.formatWorkDuration(any(Locale.class), eq(Duration.create(28800L)))).thenReturn("1 days"); when(i18n.message(DEFAULT_LOCALE, "issue.changelog.field.technicalDebt", null)).thenReturn("Technical Debt"); when(i18n.message(DEFAULT_LOCALE, "issue.changelog.changed_to", null, "Technical Debt", "1 days")).thenReturn("Technical Debt changed to 1 days"); @@ -145,7 +146,7 @@ public class IssueChangelogFormatterTest { FieldDiffs diffs = new FieldDiffs(); diffs.setDiff("technicalDebt", null, "28800"); - when(i18n.formatWorkDuration(any(Locale.class), eq(28800L))).thenReturn("1 days"); + when(i18n.formatWorkDuration(any(Locale.class), eq(Duration.create(28800L)))).thenReturn("1 days"); when(i18n.message(DEFAULT_LOCALE, "issue.changelog.field.technicalDebt", null)).thenReturn("Technical Debt"); when(i18n.message(DEFAULT_LOCALE, "issue.changelog.changed_to", null, "Technical Debt", "1 days")).thenReturn("Technical Debt changed to 1 days"); diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java index cf27de1d585..2dc944f5534 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java @@ -42,6 +42,7 @@ import org.sonar.api.technicaldebt.server.Characteristic; import org.sonar.api.technicaldebt.server.internal.DefaultCharacteristic; import org.sonar.api.user.User; import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.Duration; import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; import org.sonar.core.issue.DefaultActionPlan; @@ -302,11 +303,11 @@ public class IssueShowWsHandlerTest { @Test public void show_issue_with_technical_debt() throws Exception { - Long technicalDebt = 7260L; - Issue issue = createStandardIssue().setDebt(technicalDebt); + Duration debt = (Duration.create(7260L)); + Issue issue = createStandardIssue().setDebt(debt); issues.add(issue); - when(i18n.formatWorkDuration(any(Locale.class), eq(technicalDebt))).thenReturn("2 hours 1 minutes"); + when(i18n.formatWorkDuration(any(Locale.class), eq(debt))).thenReturn("2 hours 1 minutes"); MockUserSession.set(); WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key()); @@ -315,7 +316,7 @@ public class IssueShowWsHandlerTest { @Test public void show_issue_with_characteristics() throws Exception { - Issue issue = createStandardIssue().setDebt(7260L); + Issue issue = createStandardIssue().setDebt(Duration.create(7260L)); issues.add(issue); Characteristic requirement = new DefaultCharacteristic().setId(5).setParentId(2).setRootId(1); diff --git a/sonar-server/src/test/java/org/sonar/server/ui/JRubyI18nTest.java b/sonar-server/src/test/java/org/sonar/server/ui/JRubyI18nTest.java index 1060704e119..ef26d5c0209 100644 --- a/sonar-server/src/test/java/org/sonar/server/ui/JRubyI18nTest.java +++ b/sonar-server/src/test/java/org/sonar/server/ui/JRubyI18nTest.java @@ -25,6 +25,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.i18n.I18n; +import org.sonar.api.utils.Duration; import org.sonar.core.i18n.GwtI18n; import java.util.Date; @@ -94,8 +95,14 @@ public class JRubyI18nTest { @Test public void format_work_duration() throws Exception { - jRubyI18n.formatWorkDuration(10L); - verify(i18n).formatWorkDuration(any(Locale.class), eq(10L)); + jRubyI18n.formatWorkDuration(Duration.create(10L)); + verify(i18n).formatWorkDuration(any(Locale.class), eq(Duration.create(10L))); + } + + @Test + public void format_long_work_duration() throws Exception { + jRubyI18n.formatLongWorkDuration(10L); + verify(i18n).formatWorkDuration(any(Locale.class), eq(Duration.create(10L))); } } -- 2.39.5