diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-02-10 18:08:00 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-02-10 18:08:20 +0100 |
commit | 9ea9f3c3ea844683314926282da93ab743686806 (patch) | |
tree | 3530180c4aee2731e0fe8e33e9dcb527bc5490aa | |
parent | 59559f3f9f711e3c8468d2419caf944979e27580 (diff) | |
download | sonarqube-9ea9f3c3ea844683314926282da93ab743686806.tar.gz sonarqube-9ea9f3c3ea844683314926282da93ab743686806.zip |
Merge WorkDayDuration with WorkUnit
43 files changed, 479 insertions, 640 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java index 7bc015f104d..a4539278b05 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java @@ -25,7 +25,6 @@ import org.sonar.api.checks.NoSonarFilter; import org.sonar.api.config.PropertyDefinition; import org.sonar.api.resources.Qualifiers; import org.sonar.batch.components.PastSnapshotFinder; -import org.sonar.core.technicaldebt.TechnicalDebtConverter; import org.sonar.core.timemachine.Periods; import org.sonar.plugins.core.batch.IndexProjectPostJob; import org.sonar.plugins.core.charts.DistributionAreaChart; @@ -290,7 +289,6 @@ public final class CorePlugin extends SonarPlugin { NewFalsePositiveNotificationDispatcher.newMetadata(), // technical debt - TechnicalDebtConverter.class, TechnicalDebtDecorator.class, NewTechnicalDebtDecorator.class, 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 90603e96ce7..3c568ae78f9 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 @@ -31,7 +31,6 @@ import org.sonar.api.issue.Issuable; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.IssueChangeContext; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; @@ -40,6 +39,7 @@ import org.sonar.api.rules.ActiveRule; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.utils.KeyValueFormat; +import org.sonar.api.utils.WorkUnit; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.scan.LastSnapshots; import org.sonar.core.issue.IssueUpdater; @@ -178,7 +178,7 @@ public class IssueTrackingDecorator implements Decorator { updater.setPastMessage(issue, ref.getMessage(), changeContext); updater.setPastEffortToFix(issue, ref.getEffortToFix(), changeContext); Long technicalDebt = ref.getTechnicalDebt(); - WorkDayDuration previousTechnicalDebt = technicalDebt != null ? WorkDayDuration.fromLong(technicalDebt) : null; + WorkUnit previousTechnicalDebt = technicalDebt != null ? WorkUnit.fromLong(technicalDebt) : null; updater.setPastTechnicalDebt(issue, previousTechnicalDebt, changeContext); } } diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecorator.java index 641d659c25e..cd540b55525 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecorator.java @@ -24,23 +24,24 @@ import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; import org.apache.commons.lang.time.DateUtils; +import org.sonar.api.CoreProperties; import org.sonar.api.batch.*; import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.config.Settings; import org.sonar.api.issue.Issuable; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.FieldDiffs; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasureUtils; import org.sonar.api.measures.Metric; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; +import org.sonar.api.utils.WorkUnit; import org.sonar.batch.components.Period; import org.sonar.batch.components.TimeMachineConfiguration; import org.sonar.core.issue.IssueUpdater; -import org.sonar.core.technicaldebt.TechnicalDebtConverter; import javax.annotation.Nullable; @@ -56,12 +57,13 @@ public final class NewTechnicalDebtDecorator implements Decorator { private final ResourcePerspectives perspectives; private final TimeMachineConfiguration timeMachineConfiguration; - private final TechnicalDebtConverter technicalDebtConverter; - public NewTechnicalDebtDecorator(ResourcePerspectives perspectives, TimeMachineConfiguration timeMachineConfiguration, TechnicalDebtConverter technicalDebtConverter) { + private final int hoursInDay; + + public NewTechnicalDebtDecorator(ResourcePerspectives perspectives, TimeMachineConfiguration timeMachineConfiguration, Settings settings) { this.perspectives = perspectives; this.timeMachineConfiguration = timeMachineConfiguration; - this.technicalDebtConverter = technicalDebtConverter; + this.hoursInDay = settings.getInt(CoreProperties.HOURS_IN_DAY); } public boolean shouldExecuteOnProject(Project project) { @@ -98,32 +100,34 @@ public final class NewTechnicalDebtDecorator implements Decorator { private Double calculateNewTechnicalDebtValue(Collection<Issue> issues, @Nullable Date periodDate) { double value = 0; for (Issue issue : issues) { - WorkDayDuration currentTechnicalDebt = ((DefaultIssue) issue).technicalDebt(); + double currentTechnicalDebtValue = 0d; + WorkUnit currentTechnicalDebt = ((DefaultIssue) issue).technicalDebt(); + if (currentTechnicalDebt != null) { + currentTechnicalDebtValue = currentTechnicalDebt.toDays(hoursInDay); + } Date periodDatePlusOneSecond = periodDate != null ? DateUtils.addSeconds(periodDate, 1) : null; if (isAfter(issue.creationDate(), periodDatePlusOneSecond)) { - value += technicalDebtConverter.toDays(currentTechnicalDebt); + value += currentTechnicalDebtValue; } else { - value += calculateNewTechnicalDebtValueFromChangelog(currentTechnicalDebt, issue, periodDate); + value += calculateNewTechnicalDebtValueFromChangelog(currentTechnicalDebtValue, issue, periodDate); } } return value; } - private double calculateNewTechnicalDebtValueFromChangelog(WorkDayDuration currentTechnicalDebt, Issue issue, Date periodDate) { - double currentTechnicalDebtValue = technicalDebtConverter.toDays(currentTechnicalDebt); - + private double calculateNewTechnicalDebtValueFromChangelog(double currentTechnicalDebtValue, Issue issue, Date periodDate) { List<FieldDiffs> changelog = technicalDebtHistory(issue); for (Iterator<FieldDiffs> iterator = changelog.iterator(); iterator.hasNext(); ) { FieldDiffs diff = iterator.next(); Date date = diff.creationDate(); if (isLesserOrEqual(date, periodDate)) { // return new value from the change that is just before the period date - return currentTechnicalDebtValue - technicalDebtConverter.toDays(newValue(diff)); + return currentTechnicalDebtValue - newValue(diff).toDays(hoursInDay); } if (!iterator.hasNext()) { // return old value from the change that is just after the period date when there's no more element in changelog - return currentTechnicalDebtValue - technicalDebtConverter.toDays(oldValue(diff)); + return currentTechnicalDebtValue - oldValue(diff).toDays(hoursInDay); } } // Return 0 when no changelog @@ -155,24 +159,24 @@ public final class NewTechnicalDebtDecorator implements Decorator { return diffs; } - private WorkDayDuration newValue(FieldDiffs fieldDiffs) { + private WorkUnit newValue(FieldDiffs fieldDiffs) { for (Map.Entry<String, FieldDiffs.Diff> entry : fieldDiffs.diffs().entrySet()) { if (entry.getKey().equals(IssueUpdater.TECHNICAL_DEBT)) { Long newValue = entry.getValue().newValueLong(); - return newValue != null ? WorkDayDuration.fromLong(newValue) : null; + return newValue != null ? WorkUnit.fromLong(newValue) : WorkUnit.fromLong(0); } } - return null; + return WorkUnit.fromLong(0); } - private WorkDayDuration oldValue(FieldDiffs fieldDiffs) { + private WorkUnit oldValue(FieldDiffs fieldDiffs) { for (Map.Entry<String, FieldDiffs.Diff> entry : fieldDiffs.diffs().entrySet()) { if (entry.getKey().equals(IssueUpdater.TECHNICAL_DEBT)) { Long value = entry.getValue().oldValueLong(); - return value != null ? WorkDayDuration.fromLong(value) : null; + return value != null ? WorkUnit.fromLong(value) : WorkUnit.fromLong(0); } } - return null; + return WorkUnit.fromLong(0); } private boolean isAfter(@Nullable Date currentDate, @Nullable Date pastDate) { 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 2cbc2ce8285..44eb76e6ba1 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 @@ -30,6 +30,7 @@ import org.sonar.api.PropertyType; import org.sonar.api.batch.*; import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.config.PropertyDefinition; +import org.sonar.api.config.Settings; import org.sonar.api.issue.Issuable; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; @@ -40,7 +41,7 @@ import org.sonar.api.resources.ResourceUtils; import org.sonar.api.technicaldebt.batch.Characteristic; import org.sonar.api.technicaldebt.batch.Requirement; import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; -import org.sonar.core.technicaldebt.TechnicalDebtConverter; +import org.sonar.api.utils.WorkUnit; import java.util.Arrays; import java.util.Collection; @@ -60,12 +61,13 @@ public final class TechnicalDebtDecorator implements Decorator { private final ResourcePerspectives perspectives; private final TechnicalDebtModel model; - private final TechnicalDebtConverter converter; - public TechnicalDebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model, TechnicalDebtConverter converter) { + private final int hoursInDay; + + public TechnicalDebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model, Settings settings) { this.perspectives = perspectives; this.model = model; - this.converter = converter; + this.hoursInDay = settings.getInt(CoreProperties.HOURS_IN_DAY); } public boolean shouldExecuteOnProject(Project project) { @@ -168,8 +170,13 @@ public final class TechnicalDebtDecorator implements Decorator { private double computeTechnicalDebt(Metric metric, DecoratorContext context, Requirement requirement, Collection<Issue> issues) { double value = 0.0; if (issues != null) { - for (Issue issue : issues){ - value += converter.toDays(((DefaultIssue) issue).technicalDebt()); + for (Issue issue : issues) { + WorkUnit debt = ((DefaultIssue) issue).technicalDebt(); + if (debt != null) { + value += debt.toDays(hoursInDay); + } else { + value += 0d; + } } } @@ -200,7 +207,7 @@ public final class TechnicalDebtDecorator implements Decorator { public static List<PropertyDefinition> definitions() { return ImmutableList.of( - PropertyDefinition.builder(TechnicalDebtConverter.PROPERTY_HOURS_IN_DAY) + PropertyDefinition.builder(CoreProperties.HOURS_IN_DAY) .name("Number of working hours in a day") .type(PropertyType.INTEGER) .defaultValue("8") 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 9a573b3d62a..0cb689bb9d8 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 @@ -29,7 +29,6 @@ import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.IssueChangeContext; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.resources.File; import org.sonar.api.resources.Project; @@ -37,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.WorkUnit; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.scan.LastSnapshots; import org.sonar.core.issue.IssueUpdater; @@ -520,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(WorkDayDuration.of(1, 0, 0)), any(IssueChangeContext.class)); + verify(updater).setPastTechnicalDebt(eq(issue), eq(new WorkUnit.Builder().setMinutes(1).build()), 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 2dfc4b3df36..4b968938136 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 @@ -27,21 +27,22 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.CoreProperties; import org.sonar.api.batch.DecoratorContext; import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.config.Settings; import org.sonar.api.issue.Issuable; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.FieldDiffs; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.measures.CoreMetrics; 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.WorkUnit; import org.sonar.batch.components.Period; import org.sonar.batch.components.TimeMachineConfiguration; -import org.sonar.core.technicaldebt.TechnicalDebtConverter; import java.util.Date; @@ -67,9 +68,6 @@ public class NewTechnicalDebtDecoratorTest { @Mock DecoratorContext context; - @Mock - TechnicalDebtConverter technicalDebtConverter; - Date rightNow; Date elevenDaysAgo; Date tenDaysAgo; @@ -77,15 +75,14 @@ public class NewTechnicalDebtDecoratorTest { Date fiveDaysAgo; Date fourDaysAgo; - WorkDayDuration oneDaysDebt = WorkDayDuration.of(0, 0, 1); - WorkDayDuration twoDaysDebt = WorkDayDuration.of(0, 0, 2); - WorkDayDuration fiveDaysDebt = WorkDayDuration.of(0, 0, 5); + WorkUnit oneDaysDebt = new WorkUnit.Builder().setDays(1).build(); + WorkUnit twoDaysDebt = new WorkUnit.Builder().setDays(2).build(); + WorkUnit fiveDaysDebt = new WorkUnit.Builder().setDays(5).build(); @Before public void setup() { - when(technicalDebtConverter.toDays(oneDaysDebt)).thenReturn(1d); - when(technicalDebtConverter.toDays(twoDaysDebt)).thenReturn(2d); - when(technicalDebtConverter.toDays(fiveDaysDebt)).thenReturn(5d); + Settings settings = new Settings(); + settings.setProperty(CoreProperties.HOURS_IN_DAY, "8"); ResourcePerspectives perspectives = mock(ResourcePerspectives.class); when(perspectives.as(Issuable.class, resource)).thenReturn(issuable); @@ -99,7 +96,7 @@ public class NewTechnicalDebtDecoratorTest { when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, fiveDaysAgo), new Period(2, tenDaysAgo))); - decorator = new NewTechnicalDebtDecorator(perspectives, timeMachineConfiguration, technicalDebtConverter); + decorator = new NewTechnicalDebtDecorator(perspectives, timeMachineConfiguration, settings); } @Test @@ -328,7 +325,7 @@ public class NewTechnicalDebtDecoratorTest { verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.NEW_TECHNICAL_DEBT))); } - private Long fromWorkDayDuration(WorkDayDuration workDayDuration){ + private Long fromWorkDayDuration(WorkUnit workDayDuration) { return workDayDuration.toLong(); } 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 eb5376d47f0..3c13be9a8a3 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 @@ -28,12 +28,13 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.CoreProperties; import org.sonar.api.batch.DecoratorContext; import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.config.Settings; import org.sonar.api.issue.Issuable; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; @@ -48,7 +49,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.core.technicaldebt.TechnicalDebtConverter; +import org.sonar.api.utils.WorkUnit; import java.util.List; @@ -67,9 +68,6 @@ public class TechnicalDebtDecoratorTest { Resource resource; @Mock - TechnicalDebtConverter converter; - - @Mock TechnicalDebtModel defaultTechnicalDebtModel; @Mock @@ -82,7 +80,10 @@ public class TechnicalDebtDecoratorTest { ResourcePerspectives perspectives = mock(ResourcePerspectives.class); when(perspectives.as(Issuable.class, resource)).thenReturn(issuable); - decorator = new TechnicalDebtDecorator(perspectives, defaultTechnicalDebtModel, converter); + Settings settings = new Settings(); + settings.setProperty(CoreProperties.HOURS_IN_DAY, "8"); + + decorator = new TechnicalDebtDecorator(perspectives, defaultTechnicalDebtModel, settings); } @Test @@ -128,8 +129,7 @@ public class TechnicalDebtDecoratorTest { @Test public void add_technical_debt_from_one_issue_and_no_parent() throws Exception { - WorkDayDuration technicalDebt = mock(WorkDayDuration.class); - when(converter.toDays(technicalDebt)).thenReturn(1.0); + WorkUnit technicalDebt = new WorkUnit.Builder().setDays(1).build(); Issue issue = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt); when(issuable.issues()).thenReturn(newArrayList(issue)); @@ -145,9 +145,22 @@ public class TechnicalDebtDecoratorTest { } @Test + public void add_technical_debt_from_one_issue_without_debt() throws Exception { + Issue issue = createIssue("rule1", "repo1").setTechnicalDebt(null); + when(issuable.issues()).thenReturn(newArrayList(issue)); + + Requirement requirement = mock(Requirement.class); + when(defaultTechnicalDebtModel.requirementsByRule(RuleKey.of("repo1", "rule1"))).thenReturn(requirement); + doReturn(newArrayList(requirement)).when(defaultTechnicalDebtModel).requirements(); + + decorator.decorate(resource, context); + + verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, 0.0))); + } + + @Test public void add_technical_debt_from_one_issue_and_propagate_to_parents() throws Exception { - WorkDayDuration technicalDebt = mock(WorkDayDuration.class); - when(converter.toDays(technicalDebt)).thenReturn(1.0); + WorkUnit technicalDebt = new WorkUnit.Builder().setDays(1).build(); Issue issue = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt); when(issuable.issues()).thenReturn(newArrayList(issue)); @@ -170,11 +183,8 @@ public class TechnicalDebtDecoratorTest { @Test public void add_technical_debt_from_issues() throws Exception { - WorkDayDuration technicalDebt1 = mock(WorkDayDuration.class); - when(converter.toDays(technicalDebt1)).thenReturn(1.0); - - WorkDayDuration technicalDebt2 = mock(WorkDayDuration.class); - when(converter.toDays(technicalDebt2)).thenReturn(2.0); + WorkUnit technicalDebt1 = new WorkUnit.Builder().setDays(1).build(); + WorkUnit technicalDebt2 = new WorkUnit.Builder().setDays(2).build(); Issue issue1 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt1); Issue issue2 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt1); @@ -202,11 +212,10 @@ public class TechnicalDebtDecoratorTest { @Test public void add_technical_debt_from_children_measures() throws Exception { - WorkDayDuration technicalDebt1 = mock(WorkDayDuration.class); - when(converter.toDays(technicalDebt1)).thenReturn(1.0); + WorkUnit technicalDebt = new WorkUnit.Builder().setDays(1).build(); - Issue issue1 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt1); - Issue issue2 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt1); + Issue issue1 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt); + Issue issue2 = createIssue("rule1", "repo1").setTechnicalDebt(technicalDebt); when(issuable.issues()).thenReturn(newArrayList(issue1, issue2)); DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("rootCharacteristic"); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index 285e29b5652..821de088059 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -35,32 +35,10 @@ import org.sonar.batch.DefaultFileLinesContextFactory; import org.sonar.batch.DefaultResourceCreationLock; import org.sonar.batch.ProjectConfigurator; import org.sonar.batch.ProjectTree; -import org.sonar.batch.bootstrap.BootstrapSettings; -import org.sonar.batch.bootstrap.ExtensionInstaller; -import org.sonar.batch.bootstrap.ExtensionMatcher; -import org.sonar.batch.bootstrap.ExtensionUtils; -import org.sonar.batch.bootstrap.MetricProvider; +import org.sonar.batch.bootstrap.*; import org.sonar.batch.components.PeriodsDefinition; -import org.sonar.batch.index.Caches; -import org.sonar.batch.index.ComponentDataCache; -import org.sonar.batch.index.ComponentDataPersister; -import org.sonar.batch.index.DefaultIndex; -import org.sonar.batch.index.DefaultPersistenceManager; -import org.sonar.batch.index.DefaultResourcePersister; -import org.sonar.batch.index.DependencyPersister; -import org.sonar.batch.index.EventPersister; -import org.sonar.batch.index.LinkPersister; -import org.sonar.batch.index.MeasurePersister; -import org.sonar.batch.index.MemoryOptimizer; -import org.sonar.batch.index.ResourceCache; -import org.sonar.batch.index.ResourceKeyMigration; -import org.sonar.batch.index.SnapshotCache; -import org.sonar.batch.index.SourcePersister; -import org.sonar.batch.issue.DefaultProjectIssues; -import org.sonar.batch.issue.DeprecatedViolations; -import org.sonar.batch.issue.IssueCache; -import org.sonar.batch.issue.IssuePersister; -import org.sonar.batch.issue.ScanIssueStorage; +import org.sonar.batch.index.*; +import org.sonar.batch.issue.*; import org.sonar.batch.phases.GraphPersister; import org.sonar.batch.profiling.PhasesSumUpTimeProfiler; import org.sonar.batch.scan.filesystem.InputFileCache; @@ -77,7 +55,6 @@ import org.sonar.core.issue.IssueUpdater; import org.sonar.core.issue.workflow.FunctionExecutor; import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.notification.DefaultNotificationManager; -import org.sonar.core.technicaldebt.TechnicalDebtConverter; import org.sonar.core.test.TestPlanBuilder; import org.sonar.core.test.TestPlanPerspectiveLoader; import org.sonar.core.test.TestableBuilder; @@ -177,7 +154,6 @@ public class ProjectScanContainer extends ComponentContainer { // technical debt TechnicalDebtModelLoader.class, - TechnicalDebtConverter.class, TechnicalDebtCalculator.class, new TechnicalDebtModelProvider(), diff --git a/sonar-batch/src/main/java/org/sonar/batch/technicaldebt/TechnicalDebtCalculator.java b/sonar-batch/src/main/java/org/sonar/batch/technicaldebt/TechnicalDebtCalculator.java index 996cd321c99..3cfefd8af44 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/technicaldebt/TechnicalDebtCalculator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/technicaldebt/TechnicalDebtCalculator.java @@ -21,35 +21,39 @@ package org.sonar.batch.technicaldebt; import com.google.common.base.Objects; import org.sonar.api.BatchExtension; +import org.sonar.api.CoreProperties; +import org.sonar.api.config.Settings; import org.sonar.api.issue.Issue; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.technicaldebt.batch.Requirement; import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement; import org.sonar.api.utils.WorkUnit; -import org.sonar.core.technicaldebt.TechnicalDebtConverter; /** * Computes the remediation cost based on the quality and analysis models. */ public class TechnicalDebtCalculator implements BatchExtension { - private final TechnicalDebtConverter converter; + private int hoursInDay; + private TechnicalDebtModel model; - public TechnicalDebtCalculator(TechnicalDebtModel model, TechnicalDebtConverter converter) { + public TechnicalDebtCalculator(TechnicalDebtModel model, Settings settings) { this.model = model; - this.converter = converter; + this.hoursInDay = settings.getInt(CoreProperties.HOURS_IN_DAY); } - public WorkDayDuration calculTechnicalDebt(Issue issue) { + /** + * Get the technical debt from the requirement + */ + public WorkUnit calculTechnicalDebt(Issue issue) { Requirement requirement = model.requirementsByRule(issue.ruleKey()); if (requirement != null) { if (requirement.function().equals(DefaultRequirement.CONSTANT_ISSUE) && issue.effortToFix() != null) { - throw new IllegalArgumentException("Requirement for '"+ issue.ruleKey() +"' can not use 'Constant/issue' remediation function " + + throw new IllegalArgumentException("Requirement for '" + issue.ruleKey() + "' can not use 'Constant/issue' remediation function " + "because this rule does not have a fixed remediation cost."); } - return converter.fromMinutes(calculTechnicalDebt(requirement, issue)); + return fromMinutes(calculTechnicalDebt(requirement, issue)); } return null; } @@ -58,11 +62,45 @@ public class TechnicalDebtCalculator implements BatchExtension { long effortToFix = Objects.firstNonNull(issue.effortToFix(), 1L).longValue(); WorkUnit factorUnit = requirement.factor(); - long factor = factorUnit != null ? converter.toMinutes(factorUnit) : 0L; + long factor = factorUnit != null ? toMinutes(factorUnit) : 0L; WorkUnit offsetUnit = requirement.offset(); - long offset = offsetUnit != null ? converter.toMinutes(offsetUnit) : 0L; + long offset = offsetUnit != null ? toMinutes(offsetUnit) : 0L; return effortToFix * factor + offset; } + + private long toMinutes(WorkUnit factor) { + if (factor.days() > 0) { + return Double.valueOf(factor.days() * hoursInDay * 60d).longValue(); + } else if (factor.hours() > 0) { + return Double.valueOf(factor.hours() * 60d).longValue(); + } else { + return Double.valueOf(factor.minutes()).longValue(); + } + } + + private WorkUnit fromMinutes(Long inMinutes) { + int oneHourInMinute = 60; + int days = 0; + int hours = 0; + int minutes = 0; + + int oneWorkingDay = hoursInDay * oneHourInMinute; + if (inMinutes >= oneWorkingDay) { + Long nbDays = inMinutes / oneWorkingDay; + days = nbDays.shortValue(); + inMinutes = inMinutes - (nbDays * oneWorkingDay); + } + + if (inMinutes >= oneHourInMinute) { + Long nbHours = inMinutes / oneHourInMinute; + hours = nbHours.shortValue(); + inMinutes = inMinutes - (nbHours * oneHourInMinute); + } + + minutes = inMinutes.shortValue(); + + return new WorkUnit.Builder().setDays(days).setHours(hours).setMinutes(minutes).build(); + } } 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 667de9f373a..532d056a306 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 @@ -27,7 +27,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.resources.JavaFile; import org.sonar.api.resources.Project; @@ -36,6 +35,7 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.api.rules.*; import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.WorkUnit; import org.sonar.batch.technicaldebt.TechnicalDebtCalculator; import java.util.Calendar; @@ -284,14 +284,14 @@ public class ModuleIssuesTest { .setRuleKey(SQUID_RULE_KEY) .setSeverity(Severity.CRITICAL); - when(technicalDebtCalculator.calculTechnicalDebt(issue)).thenReturn(WorkDayDuration.of(10, 0, 0)); + when(technicalDebtCalculator.calculTechnicalDebt(issue)).thenReturn(new WorkUnit.Builder().setDays(10).build()); when(filters.accept(issue, null)).thenReturn(true); moduleIssues.initAndAddIssue(issue); ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); verify(cache).put(argument.capture()); - assertThat(argument.getValue().technicalDebt()).isEqualTo(WorkDayDuration.of(10, 0, 0)); + assertThat(argument.getValue().technicalDebt()).isEqualTo(new WorkUnit.Builder().setDays(10).build()); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/technicaldebt/TechnicalDebtCalculatorTest.java b/sonar-batch/src/test/java/org/sonar/batch/technicaldebt/TechnicalDebtCalculatorTest.java index ccb9554d225..684f4426353 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/technicaldebt/TechnicalDebtCalculatorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/technicaldebt/TechnicalDebtCalculatorTest.java @@ -25,16 +25,18 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.CoreProperties; +import org.sonar.api.config.Settings; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.rule.RuleKey; import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement; import org.sonar.api.utils.WorkUnit; -import org.sonar.core.technicaldebt.TechnicalDebtConverter; import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Fail.fail; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class TechnicalDebtCalculatorTest { @@ -42,20 +44,17 @@ public class TechnicalDebtCalculatorTest { @Mock TechnicalDebtModel model; - @Mock - TechnicalDebtConverter converter; - - WorkUnit tenMinutes = WorkUnit.create(10d, WorkUnit.MINUTES); - WorkUnit fiveMinutes = WorkUnit.create(5d, WorkUnit.MINUTES); + WorkUnit tenMinutes = new WorkUnit.Builder().setMinutes(10).build(); + WorkUnit fiveMinutes = new WorkUnit.Builder().setMinutes(5).build(); TechnicalDebtCalculator remediationCostCalculator; @Before public void before() { - when(converter.toMinutes(tenMinutes)).thenReturn(10l); - when(converter.toMinutes(fiveMinutes)).thenReturn(5l); + Settings settings = new Settings(); + settings.setProperty(CoreProperties.HOURS_IN_DAY, 8); - remediationCostCalculator = new TechnicalDebtCalculator(model, converter); + remediationCostCalculator = new TechnicalDebtCalculator(model, settings); } @Test @@ -69,9 +68,7 @@ public class TechnicalDebtCalculatorTest { Mockito.when(requirement.offset()).thenReturn(fiveMinutes); when(model.requirementsByRule(ruleKey)).thenReturn(requirement); - remediationCostCalculator.calculTechnicalDebt(issue); - - verify(converter).fromMinutes(10l + 5l); + assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isEqualTo(new WorkUnit.Builder().setMinutes(15).build()); } @Test @@ -85,9 +82,7 @@ public class TechnicalDebtCalculatorTest { Mockito.when(requirement.offset()).thenReturn(fiveMinutes); when(model.requirementsByRule(ruleKey)).thenReturn(requirement); - remediationCostCalculator.calculTechnicalDebt(issue); - - verify(converter).fromMinutes(10l * 2 + 5l); + assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isEqualTo(new WorkUnit.Builder().setMinutes((10 * 2) + 5).build()); } @Test @@ -101,9 +96,7 @@ public class TechnicalDebtCalculatorTest { Mockito.when(requirement.offset()).thenReturn(null); when(model.requirementsByRule(ruleKey)).thenReturn(requirement); - remediationCostCalculator.calculTechnicalDebt(issue); - - verify(converter).fromMinutes(10l * 2 + 0l); + assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isEqualTo(new WorkUnit.Builder().setMinutes(10 * 2).build()); } @Test @@ -117,9 +110,7 @@ public class TechnicalDebtCalculatorTest { Mockito.when(requirement.offset()).thenReturn(fiveMinutes); when(model.requirementsByRule(ruleKey)).thenReturn(requirement); - remediationCostCalculator.calculTechnicalDebt(issue); - - verify(converter).fromMinutes(0l + 5l); + assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isEqualTo(new WorkUnit.Builder().setMinutes(5).build()); } @Test @@ -129,7 +120,6 @@ public class TechnicalDebtCalculatorTest { when(model.requirementsByRule(ruleKey)).thenReturn(null); assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isNull(); - verify(converter, never()).fromMinutes(anyLong()); } @Test @@ -144,13 +134,12 @@ public class TechnicalDebtCalculatorTest { when(model.requirementsByRule(ruleKey)).thenReturn(requirement); try { - remediationCostCalculator.calculTechnicalDebt(issue); + assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isEqualTo(new WorkUnit.Builder().setMinutes(15).build()); fail(); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalArgumentException.class) .hasMessage("Requirement for 'squid:AvoidCycle' can not use 'Constant/issue' remediation function because this rule does not have a fixed remediation cost."); } - verifyZeroInteractions(converter); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/technicaldebt/TechnicalDebtModelLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/technicaldebt/TechnicalDebtModelLoaderTest.java index cb1ecab4b73..b60bb584426 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/technicaldebt/TechnicalDebtModelLoaderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/technicaldebt/TechnicalDebtModelLoaderTest.java @@ -108,8 +108,8 @@ public class TechnicalDebtModelLoaderTest { DefaultRequirement requirement = result.requirementsByRule(ruleKey); assertThat(requirement.ruleKey()).isEqualTo(ruleKey); assertThat(requirement.function()).isEqualTo("linear"); - assertThat(requirement.factor()).isEqualTo(WorkUnit.create(2d, WorkUnit.DAYS)); - assertThat(requirement.offset()).isEqualTo(WorkUnit.create(0d, WorkUnit.DEFAULT_UNIT)); + assertThat(requirement.factor()).isEqualTo(new WorkUnit.Builder().setDays(2).build()); + assertThat(requirement.offset()).isEqualTo(new WorkUnit.Builder().setDays(0).build()); } } 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 9cbd6c92045..538104db87c 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 @@ -28,8 +28,8 @@ import org.sonar.api.issue.ActionPlan; 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.issue.internal.WorkDayDuration; import org.sonar.api.user.User; +import org.sonar.api.utils.WorkUnit; import javax.annotation.Nullable; @@ -85,7 +85,7 @@ public class IssueUpdater implements BatchComponent, ServerComponent { public boolean assign(DefaultIssue issue, @Nullable User user, IssueChangeContext context) { String sanitizedAssignee = null; - if(user != null) { + if (user != null) { sanitizedAssignee = StringUtils.defaultIfBlank(user.login(), null); } if (!Objects.equal(sanitizedAssignee, issue.assignee())) { @@ -200,8 +200,8 @@ public class IssueUpdater implements BatchComponent, ServerComponent { return setEffortToFix(issue, currentEffort, context); } - public boolean setTechnicalDebt(DefaultIssue issue, @Nullable WorkDayDuration value, IssueChangeContext context) { - WorkDayDuration oldValue = issue.technicalDebt(); + public boolean setTechnicalDebt(DefaultIssue issue, @Nullable WorkUnit value, IssueChangeContext context) { + WorkUnit oldValue = issue.technicalDebt(); if (!Objects.equal(value, oldValue)) { issue.setTechnicalDebt(value); issue.setFieldChange(context, TECHNICAL_DEBT, oldValue != null ? oldValue.toLong() : null, value != null ? value.toLong() : null); @@ -212,8 +212,8 @@ public class IssueUpdater implements BatchComponent, ServerComponent { return false; } - public boolean setPastTechnicalDebt(DefaultIssue issue, @Nullable WorkDayDuration previousTechnicalDebt, IssueChangeContext context) { - WorkDayDuration currentTechnicalDebt = issue.technicalDebt(); + public boolean setPastTechnicalDebt(DefaultIssue issue, @Nullable WorkUnit previousTechnicalDebt, IssueChangeContext context) { + WorkUnit currentTechnicalDebt = issue.technicalDebt(); issue.setTechnicalDebt(previousTechnicalDebt); return setTechnicalDebt(issue, currentTechnicalDebt, context); } @@ -232,7 +232,7 @@ public class IssueUpdater implements BatchComponent, ServerComponent { public boolean plan(DefaultIssue issue, @Nullable ActionPlan actionPlan, IssueChangeContext context) { String sanitizedActionPlanKey = null; - if(actionPlan != null) { + if (actionPlan != null) { sanitizedActionPlanKey = StringUtils.defaultIfBlank(actionPlan.key(), null); } if (!Objects.equal(sanitizedActionPlanKey, issue.actionPlanKey())) { 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 1574dcc9ff5..3e34ad2c738 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 @@ -24,9 +24,9 @@ import com.google.common.base.Preconditions; 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.issue.internal.WorkDayDuration; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.KeyValueFormat; +import org.sonar.api.utils.WorkUnit; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -422,7 +422,7 @@ public final class IssueDto implements Serializable { issue.setResolution(resolution); issue.setMessage(message); issue.setEffortToFix(effortToFix); - issue.setTechnicalDebt(technicalDebt != null ? WorkDayDuration.fromLong(technicalDebt) : null); + issue.setTechnicalDebt(technicalDebt != null ? WorkUnit.fromLong(technicalDebt) : null); issue.setLine(line); issue.setSeverity(severity); issue.setReporter(reporter); diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtConverter.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtConverter.java deleted file mode 100644 index 7191034e8e6..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtConverter.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.core.technicaldebt; - -import org.apache.commons.lang.StringUtils; -import org.sonar.api.BatchComponent; -import org.sonar.api.ServerComponent; -import org.sonar.api.config.Settings; -import org.sonar.api.issue.internal.WorkDayDuration; -import org.sonar.api.utils.WorkUnit; - -import javax.annotation.Nullable; - -public class TechnicalDebtConverter implements BatchComponent, ServerComponent { - - public static final String PROPERTY_HOURS_IN_DAY = "sonar.technicalDebt.hoursInDay"; - - private int hoursInDay; - - public TechnicalDebtConverter(Settings settings) { - this.hoursInDay = settings.getInt(PROPERTY_HOURS_IN_DAY); - } - - public long toMinutes(WorkUnit factor) { - if (StringUtils.equals(WorkUnit.DAYS, factor.getUnit())) { - return Double.valueOf(factor.getValue() * hoursInDay * 60d).longValue(); - - } else if (StringUtils.equals(WorkUnit.HOURS, factor.getUnit())) { - return Double.valueOf(factor.getValue() * 60d).longValue(); - - } else if (StringUtils.equals(WorkUnit.MINUTES, factor.getUnit())) { - return Double.valueOf(factor.getValue()).longValue(); - - } else { - throw new IllegalArgumentException("Unknown remediation factor unit: " + factor.getUnit()); - } - } - - public double toDays(@Nullable WorkDayDuration technicalDebt) { - if (technicalDebt == null) { - return 0d; - } - double resultDays = technicalDebt.days(); - resultDays += Double.valueOf(technicalDebt.hours()) / hoursInDay; - resultDays += Double.valueOf(technicalDebt.minutes()) / (hoursInDay * 60.0); - return resultDays; - } - - public WorkDayDuration fromMinutes(Long inMinutes){ - int oneHourInMinute = 60; - int days = 0; - int hours = 0; - int minutes = 0; - - int oneWorkingDay = hoursInDay * oneHourInMinute; - if (inMinutes >= oneWorkingDay) { - Long nbDays = inMinutes / oneWorkingDay; - days = nbDays.shortValue(); - inMinutes = inMinutes - (nbDays * oneWorkingDay); - } - - if (inMinutes >= oneHourInMinute) { - Long nbHours = inMinutes / oneHourInMinute; - hours = nbHours.shortValue(); - inMinutes = inMinutes - (nbHours * oneHourInMinute); - } - - minutes = inMinutes.shortValue(); - - return WorkDayDuration.of(minutes, hours, days); - } - -} 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 0533e9c157b..3a78a443010 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 @@ -24,8 +24,8 @@ import org.sonar.api.issue.ActionPlan; 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.issue.internal.WorkDayDuration; import org.sonar.api.user.User; +import org.sonar.api.utils.WorkUnit; import org.sonar.core.user.DefaultUser; import java.util.Date; @@ -366,42 +366,42 @@ public class IssueUpdaterTest { @Test public void set_past_technical_debt() throws Exception { - issue.setTechnicalDebt(WorkDayDuration.of(15, 0, 0)); - WorkDayDuration previousDebt = WorkDayDuration.of(10, 0, 0); + issue.setTechnicalDebt(new WorkUnit.Builder().setDays(15).build()); + WorkUnit previousDebt = new WorkUnit.Builder().setDays(10).build(); boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context); assertThat(updated).isTrue(); - assertThat(issue.technicalDebt()).isEqualTo(WorkDayDuration.of(15, 0, 0)); + assertThat(issue.technicalDebt()).isEqualTo(new WorkUnit.Builder().setDays(15).build()); assertThat(issue.mustSendNotifications()).isFalse(); FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); - assertThat(diff.oldValue()).isEqualTo(WorkDayDuration.of(10, 0, 0).toLong()); - assertThat(diff.newValue()).isEqualTo(WorkDayDuration.of(15, 0, 0).toLong()); + assertThat(diff.oldValue()).isEqualTo(new WorkUnit.Builder().setDays(10).build().toLong()); + assertThat(diff.newValue()).isEqualTo(new WorkUnit.Builder().setDays(15).build().toLong()); } @Test public void set_past_technical_debt_without_previous_value() throws Exception { - issue.setTechnicalDebt(WorkDayDuration.of(15, 0, 0)); + issue.setTechnicalDebt(new WorkUnit.Builder().setDays(15).build()); boolean updated = updater.setPastTechnicalDebt(issue, null, context); assertThat(updated).isTrue(); - assertThat(issue.technicalDebt()).isEqualTo(WorkDayDuration.of(15, 0, 0)); + assertThat(issue.technicalDebt()).isEqualTo(new WorkUnit.Builder().setDays(15).build()); assertThat(issue.mustSendNotifications()).isFalse(); FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); assertThat(diff.oldValue()).isNull(); - assertThat(diff.newValue()).isEqualTo(WorkDayDuration.of(15, 0, 0).toLong()); + assertThat(diff.newValue()).isEqualTo(new WorkUnit.Builder().setDays(15).build().toLong()); } @Test public void set_past_technical_debt_with_null_new_value() throws Exception { issue.setTechnicalDebt(null); - WorkDayDuration previousDebt = WorkDayDuration.of(10, 0, 0); + WorkUnit previousDebt = new WorkUnit.Builder().setDays(10).build(); boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context); assertThat(updated).isTrue(); assertThat(issue.technicalDebt()).isNull(); assertThat(issue.mustSendNotifications()).isFalse(); FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); - assertThat(diff.oldValue()).isEqualTo(WorkDayDuration.of(10, 0, 0).toLong()); + assertThat(diff.oldValue()).isEqualTo(new WorkUnit.Builder().setDays(10).build().toLong()); 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 484ad84f2a8..e87ffb98153 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,7 +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.issue.internal.WorkDayDuration; +import org.sonar.api.utils.WorkUnit; import java.util.Calendar; import java.util.Date; @@ -56,28 +56,28 @@ public class IssueDtoTest { Date closedAt = DateUtils.addDays(new Date(), -1); IssueDto dto = new IssueDto() - .setKee("100") - .setRuleId(1) - .setRuleKey_unit_test_only("squid", "AvoidCycle") - .setComponentKey_unit_test_only("org.sonar.sample:Sample") - .setRootComponentKey_unit_test_only("org.sonar.sample") - .setComponentId(1l) - .setRootComponentId(1l) - .setStatus(Issue.STATUS_CLOSED) - .setResolution(Issue.RESOLUTION_FALSE_POSITIVE) - .setEffortToFix(15.0) - .setTechnicalDebt(101010L) - .setLine(6) - .setSeverity("BLOCKER") - .setMessage("message") - .setManualSeverity(true) - .setReporter("arthur") - .setAssignee("perceval") - .setIssueAttributes("key=value") - .setAuthorLogin("pierre") - .setIssueCreationDate(createdAt) - .setIssueUpdateDate(updatedAt) - .setIssueCloseDate(closedAt); + .setKee("100") + .setRuleId(1) + .setRuleKey_unit_test_only("squid", "AvoidCycle") + .setComponentKey_unit_test_only("org.sonar.sample:Sample") + .setRootComponentKey_unit_test_only("org.sonar.sample") + .setComponentId(1l) + .setRootComponentId(1l) + .setStatus(Issue.STATUS_CLOSED) + .setResolution(Issue.RESOLUTION_FALSE_POSITIVE) + .setEffortToFix(15.0) + .setTechnicalDebt(101010L) + .setLine(6) + .setSeverity("BLOCKER") + .setMessage("message") + .setManualSeverity(true) + .setReporter("arthur") + .setAssignee("perceval") + .setIssueAttributes("key=value") + .setAuthorLogin("pierre") + .setIssueCreationDate(createdAt) + .setIssueUpdateDate(updatedAt) + .setIssueCloseDate(closedAt); DefaultIssue issue = dto.toDefaultIssue(); assertThat(issue.key()).isEqualTo("100"); @@ -87,7 +87,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.technicalDebt()).isEqualTo(WorkDayDuration.of(10, 10, 10)); + assertThat(issue.technicalDebt()).isEqualTo(new WorkUnit.Builder().setDays(10).setHours(10).setMinutes(10).build()); 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 1c6281c837a..5c8b3487f8e 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 @@ -23,12 +23,12 @@ import org.junit.Test; 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.issue.internal.WorkDayDuration; import org.sonar.api.rule.RuleKey; 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.WorkUnit; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.persistence.MyBatis; @@ -54,7 +54,7 @@ public class IssueStorageTest extends AbstractDaoTestCase { .setRuleKey(RuleKey.of("squid", "AvoidCycle")) .setLine(5000) - .setTechnicalDebt(WorkDayDuration.of(10, 0, 0)) + .setTechnicalDebt(new WorkUnit.Builder().setMinutes(10).build()) .setReporter("emmerik") .setResolution("OPEN") .setStatus("OPEN") @@ -88,7 +88,7 @@ public class IssueStorageTest extends AbstractDaoTestCase { // updated fields .setLine(5000) - .setTechnicalDebt(WorkDayDuration.of(10, 0, 0)) + .setTechnicalDebt(new WorkUnit.Builder().setMinutes(10).build()) .setChecksum("FFFFF") .setAuthorLogin("simon") .setAssignee("loic") diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtManagerTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtManagerTest.java index a69517c7c99..69877f81898 100644 --- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtManagerTest.java +++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtManagerTest.java @@ -89,8 +89,8 @@ public class DefaultTechnicalDebtManagerTest { assertThat(result.rootId()).isEqualTo(1); assertThat(result.ruleKey()).isEqualTo(RuleKey.of("repo", "key")); assertThat(result.function()).isEqualTo("linear"); - assertThat(result.factor()).isEqualTo(WorkUnit.create(30.0, WorkUnit.MINUTES)); - assertThat(result.offset()).isEqualTo(WorkUnit.create()); + assertThat(result.factor()).isEqualTo(new WorkUnit.Builder().setMinutes(30).build()); + assertThat(result.offset()).isEqualTo(new WorkUnit.Builder().setDays(0).build()); } @Test @@ -149,8 +149,8 @@ public class DefaultTechnicalDebtManagerTest { assertThat(result.rootId()).isEqualTo(1); assertThat(result.ruleKey()).isEqualTo(RuleKey.of("repo", "key")); assertThat(result.function()).isEqualTo("linear"); - assertThat(result.factor()).isEqualTo(WorkUnit.create(30.0, WorkUnit.MINUTES)); - assertThat(result.offset()).isEqualTo(WorkUnit.create()); + assertThat(result.factor()).isEqualTo(new WorkUnit.Builder().setMinutes(30).build()); + assertThat(result.offset()).isEqualTo(new WorkUnit.Builder().setDays(0).build()); } @Test diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModelTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModelTest.java index dcf61654fc3..4177c144c64 100644 --- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModelTest.java +++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/DefaultTechnicalDebtModelTest.java @@ -92,8 +92,8 @@ public class DefaultTechnicalDebtModelTest { .setCharacteristic(characteristic) .setRuleKey(ruleKey) .setFunction("linear") - .setFactor(WorkUnit.create(2d, WorkUnit.HOURS)) - .setOffset(WorkUnit.create(0d, WorkUnit.HOURS)); + .setFactor(new WorkUnit.Builder().setHours(2).build()) + .setOffset(new WorkUnit.Builder().setHours(0).build()); sqaleModel.addRootCharacteristic(rootCharacteristic); diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtConverterTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtConverterTest.java deleted file mode 100644 index c196320b193..00000000000 --- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtConverterTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.core.technicaldebt; - -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.config.Settings; -import org.sonar.api.issue.internal.WorkDayDuration; -import org.sonar.api.utils.WorkUnit; - -import static org.fest.assertions.Assertions.assertThat; - - -public class TechnicalDebtConverterTest { - - private TechnicalDebtConverter converter; - - @Before - public void before(){ - Settings settings = new Settings(); - settings.setProperty(TechnicalDebtConverter.PROPERTY_HOURS_IN_DAY, "12"); - - converter = new TechnicalDebtConverter(settings); - } - - @Test - public void concert_work_unit_to_minutes() { - assertThat(converter.toMinutes(WorkUnit.create(2.0, WorkUnit.DAYS))).isEqualTo(2 * 12 * 60L); - assertThat(converter.toMinutes(WorkUnit.create(6.0, WorkUnit.HOURS))).isEqualTo(6 * 60L); - assertThat(converter.toMinutes(WorkUnit.create(60.0, WorkUnit.MINUTES))).isEqualTo(60L); - } - - @Test - public void convert_simple_values() { - checkValues(converter.fromMinutes(15L), 15, 0, 0); - checkValues(converter.fromMinutes(120L), 0, 2, 0); - checkValues(converter.fromMinutes(720L), 0, 0, 1); - } - - @Test - public void convert_complex_values() { - checkValues(converter.fromMinutes(70L), 10, 1, 0); - checkValues(converter.fromMinutes(730L), 10, 0, 1); - checkValues(converter.fromMinutes(790L), 10, 1, 1); - } - - @Test - public void convert_technical_debt_to_days() { - assertThat(converter.toDays(WorkDayDuration.of(0, 0, 6))).isEqualTo(6.0); - assertThat(converter.toDays(WorkDayDuration.of(0, 6, 0))).isEqualTo(0.5); - assertThat(converter.toDays(WorkDayDuration.of(360, 0, 0))).isEqualTo(0.5); - assertThat(converter.toDays(WorkDayDuration.of(45, 0, 0))).isEqualTo(0.0625); - - assertThat(converter.toDays(WorkDayDuration.of(45, 6, 1))).isEqualTo(1.5625); - } - - @Test - public void return_zero_if_null_when_convert_technical_debt_to_days() { - assertThat(converter.toDays((WorkDayDuration) null)).isEqualTo(0.0); - } - - private void checkValues(WorkDayDuration technicalDebt, int expectedMinutes, int expectedHours, int expectedDays) { - assertThat(technicalDebt.minutes()).isEqualTo(expectedMinutes); - assertThat(technicalDebt.hours()).isEqualTo(expectedHours); - assertThat(technicalDebt.days()).isEqualTo(expectedDays); - } - -} diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizerTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizerTest.java index e037a48280f..17e4bee2e8a 100644 --- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizerTest.java +++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizerTest.java @@ -142,7 +142,7 @@ public class TechnicalDebtModelSynchronizerTest { RuleKey ruleKey = RuleKey.of("checkstyle", "import"); when(ruleCache.getByRuleKey(ruleKey)).thenReturn(rule); new DefaultRequirement().setRuleKey(ruleKey) - .setFunction("linear").setFactor(WorkUnit.create(30.0, WorkUnit.MINUTES)).setCharacteristic(javaCharacteristic).setRootCharacteristic(javaRootCharacteristic); + .setFunction("linear").setFactor(new WorkUnit.Builder().setMinutes(30).build()).setCharacteristic(javaCharacteristic).setRootCharacteristic(javaRootCharacteristic); Reader javaModelReader = mock(Reader.class); when(xmlImporter.importXML(eq(javaModelReader), any(ValidationMessages.class), eq(ruleCache))).thenReturn(javaModel); @@ -195,7 +195,7 @@ public class TechnicalDebtModelSynchronizerTest { // New requirement new DefaultRequirement().setRuleKey(ruleKey2) - .setFunction("linear").setFactor(WorkUnit.create(1.0, WorkUnit.HOURS)).setCharacteristic(javaCharacteristic).setRootCharacteristic(javaRootCharacteristic); + .setFunction("linear").setFactor(new WorkUnit.Builder().setHours(1).build()).setCharacteristic(javaCharacteristic).setRootCharacteristic(javaRootCharacteristic); Reader javaModelReader = mock(Reader.class); when(technicalDebtModelRepository.createReaderForXMLFile("java")).thenReturn(javaModelReader); diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporterTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporterTest.java index 1670422d435..d614d7588a0 100644 --- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporterTest.java +++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporterTest.java @@ -189,7 +189,7 @@ public class TechnicalDebtXMLImporterTest { } private void checkXmlCorrectlyImported(DefaultTechnicalDebtModel sqale, ValidationMessages messages) { - checkXmlCorrectlyImported(sqale, WorkUnit.create(), messages); + checkXmlCorrectlyImported(sqale, new WorkUnit.Builder().setDays(0).build(), messages); } private void checkXmlCorrectlyImported(DefaultTechnicalDebtModel sqale, WorkUnit offset, ValidationMessages messages) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java index d0a38dbdc84..5e0795830d0 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java @@ -543,4 +543,9 @@ public interface CoreProperties { * @since 4.2 */ String CORE_AUTHENTICATOR_LOCAL_USERS = "sonar.security.localUsers"; + + /** + * @since 4.0 + */ + String HOURS_IN_DAY = "sonar.technicalDebt.hoursInDay"; } 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 10155076963..dcb7e1a7beb 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.WorkUnit; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -58,7 +59,7 @@ public class DefaultIssue implements Issue { private String message; private Integer line; private Double effortToFix; - private WorkDayDuration technicalDebt; + private WorkUnit technicalDebt; private String status; private String resolution; private String reporter; @@ -195,11 +196,11 @@ public class DefaultIssue implements Issue { * Elapsed time to fix the issue */ @CheckForNull - public WorkDayDuration technicalDebt() { + public WorkUnit technicalDebt() { return technicalDebt; } - public DefaultIssue setTechnicalDebt(@Nullable WorkDayDuration t) { + public DefaultIssue setTechnicalDebt(@Nullable WorkUnit t) { this.technicalDebt = t; return this; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/WorkDayDuration.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/WorkDayDuration.java deleted file mode 100644 index a2acecf56f8..00000000000 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/WorkDayDuration.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.api.issue.internal; - -import java.io.Serializable; - -public class WorkDayDuration implements Serializable { - - private static final int DAY = 10000; - private static final int HOUR = 100; - private static final int MINUTE = 1; - - private int days; - private int hours; - private int minutes; - - private WorkDayDuration(int minutes, int hours, int days) { - this.minutes = minutes; - this.hours = hours; - this.days = days; - } - - private WorkDayDuration(long durationInLong) { - long time = durationInLong; - Long currentTime = time / DAY; - if (currentTime > 0) { - this.days = currentTime.intValue(); - time = time - (currentTime * DAY); - } - - currentTime = time / HOUR; - if (currentTime > 0) { - this.hours = currentTime.intValue(); - time = time - (currentTime * HOUR); - } - - currentTime = time / MINUTE; - if (currentTime > 0) { - this.minutes = currentTime.intValue(); - } - } - - public static WorkDayDuration of(int minutes, int hours, int days) { - return new WorkDayDuration(minutes, hours, days); - } - - public static WorkDayDuration fromLong(long durationInLong) { - return new WorkDayDuration(durationInLong); - } - - public long toLong() { - return days * DAY + hours * HOUR + minutes * MINUTE; - } - - public int days() { - return days; - } - - public int hours() { - return hours; - } - - public int minutes() { - return minutes; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - WorkDayDuration workDayDuration = (WorkDayDuration) o; - if (days != workDayDuration.days) { - return false; - } - if (hours != workDayDuration.hours) { - return false; - } - if (minutes != workDayDuration.minutes) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int result = Integer.valueOf(days).hashCode(); - result = 29 * result + Integer.valueOf(hours).hashCode(); - result = 27 * result + Integer.valueOf(minutes).hashCode(); - return result; - } - -} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/internal/DefaultRequirement.java b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/internal/DefaultRequirement.java index 66f4778c5e4..8f80e3fc8ec 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/internal/DefaultRequirement.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/internal/DefaultRequirement.java @@ -47,8 +47,8 @@ public class DefaultRequirement implements Requirement { private Date updatedAt; public DefaultRequirement() { - this.factor = WorkUnit.create(0d, WorkUnit.DEFAULT_UNIT); - this.offset = WorkUnit.create(0d, WorkUnit.DEFAULT_UNIT); + this.factor = new WorkUnit.Builder().setDays(0).build(); + this.offset = new WorkUnit.Builder().setDays(0).build(); } public Integer id() { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/WorkUnit.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/WorkUnit.java index 61a5aeec2a7..2686663765c 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/WorkUnit.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/WorkUnit.java @@ -26,30 +26,42 @@ import org.apache.commons.lang.builder.ToStringStyle; import javax.annotation.Nullable; -public final class WorkUnit { +import java.io.Serializable; + +/** + * @since 4.0 + */ +public final class WorkUnit implements Serializable { public static final String DAYS = "d"; public static final String MINUTES = "mn"; public static final String HOURS = "h"; public static final String DEFAULT_UNIT = DAYS; - private static final String[] UNITS = {DAYS, MINUTES, HOURS}; public static final double DEFAULT_VALUE = 0.0; - private double value = 0d; - private String unit = DEFAULT_UNIT; + private static final String[] UNITS = {DAYS, MINUTES, HOURS}; - WorkUnit(double value, String unit) { - this.value = value; - this.unit = unit; - } + private static final int DAY = 10000; + private static final int HOUR = 100; + private static final int MINUTE = 1; - public double getValue() { - return value; + private int days; + private int hours; + private int minutes; + + private WorkUnit(int days, int hours, int minutes) { + this.minutes = minutes; + this.hours = hours; + this.days = days; } - public String getUnit() { - return unit; + /** + * @deprecated since 4.2. + */ + @Deprecated + public static WorkUnit create() { + return create(0d, DEFAULT_UNIT); } public static WorkUnit create(@Nullable Double value, @Nullable String unit) { @@ -57,15 +69,114 @@ public final class WorkUnit { if (!ArrayUtils.contains(UNITS, defaultIfEmptyUnit)) { throw new IllegalArgumentException("Unit can not be: " + defaultIfEmptyUnit + ". Possible values are " + ArrayUtils.toString(UNITS)); } - double d = value != null ? value : DEFAULT_VALUE; + Double d = value != null ? value : DEFAULT_VALUE; if (d < 0.0) { throw new IllegalArgumentException("Value can not be negative: " + d); } - return new WorkUnit(d, defaultIfEmptyUnit); + + int days = 0; + int hours = 0; + int minutes = 0; + if (DAYS.equals(unit)) { + days = d.intValue(); + } else if (HOURS.equals(unit)) { + hours = d.intValue(); + } else if (MINUTES.equals(unit)) { + minutes = d.intValue(); + } + return new WorkUnit(days, hours, minutes); } - public static WorkUnit create() { - return create(0d, DEFAULT_UNIT); + public double getValue() { + if (days > 0) { + return days + (hours / 24) + (minutes / 60 / 24); + } else if (hours > 0) { + return hours + (minutes / 60); + } else { + return minutes; + } + } + + public String getUnit() { + if (days > 0) { + return DAYS; + } else if (hours > 0) { + return HOURS; + } else { + return MINUTES; + } + } + + /** + * @since 4.2 + */ + public int days() { + return days; + } + + /** + * @since 4.2 + */ + public int hours() { + return hours; + } + + /** + * @since 4.2 + */ + public int minutes() { + return minutes; + } + + /** + * + * @since 4.2 + */ + public static WorkUnit fromLong(long durationInLong) { + Builder builder = new Builder(); + + long time = durationInLong; + Long currentTime = time / DAY; + if (currentTime > 0) { + builder.setDays(currentTime.intValue()); + time = time - (currentTime * DAY); + } + + currentTime = time / HOUR; + if (currentTime > 0) { + builder.setHours(currentTime.intValue()); + time = time - (currentTime * HOUR); + } + + currentTime = time / MINUTE; + if (currentTime > 0) { + builder.setMinutes(currentTime.intValue()); + } + + return builder.build(); + } + + /** + * Return the duration using the following format DDHHMM, where DD is the number of days, HH is the number of months, and MM the number of minutes. + * For instance, 5 days and 2 hours will return 050200. + * + * @since 4.2 + */ + public long toLong() { + return days * DAY + hours * HOUR + minutes * MINUTE; + } + + /** + * Return the duration in number of days. + * For instance, 5 days and 4 hours will return 5.5 hours (if hoursIndDay is 8). + * + * @since 4.2 + */ + public double toDays(int hoursInDay) { + double resultDays = days; + resultDays += (double) hours / hoursInDay; + resultDays += (double) minutes / (hoursInDay * 60.0); + return resultDays; } @Override @@ -76,26 +187,24 @@ public final class WorkUnit { if (o == null || getClass() != o.getClass()) { return false; } - - WorkUnit workUnit = (WorkUnit) o; - - if (Double.compare(workUnit.value, value) != 0) { + WorkUnit workDayDuration = (WorkUnit) o; + if (days != workDayDuration.days) { return false; } - if (!unit.equals(workUnit.unit)) { + if (hours != workDayDuration.hours) { + return false; + } + if (minutes != workDayDuration.minutes) { return false; } - return true; } @Override public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(value); - result = (int) (temp ^ (temp >>> 32)); - result = 31 * result + unit.hashCode(); + int result = Integer.valueOf(days).hashCode(); + result = 29 * result + Integer.valueOf(hours).hashCode(); + result = 27 * result + Integer.valueOf(minutes).hashCode(); return result; } @@ -103,4 +212,32 @@ public final class WorkUnit { public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); } + + /** + * @since 4.2 + */ + public static class Builder { + private int days; + private int hours; + private int minutes; + + public Builder setDays(int days) { + this.days = days; + return this; + } + + public Builder setHours(int hours) { + this.hours = hours; + return this; + } + + public Builder setMinutes(int minutes) { + this.minutes = minutes; + return this; + } + + public WorkUnit build() { + return new WorkUnit(days, hours, minutes); + } + } } 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 0e464d40ec5..1b385766ac4 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.WorkUnit; import java.text.SimpleDateFormat; import java.util.List; @@ -49,7 +50,7 @@ public class DefaultIssueTest { .setMessage("a message") .setLine(7) .setEffortToFix(1.2d) - .setTechnicalDebt(WorkDayDuration.of(1, 0, 0)) + .setTechnicalDebt(new WorkUnit.Builder().setDays(1).build()) .setActionPlanKey("BCDE") .setStatus(Issue.STATUS_CLOSED) .setResolution(Issue.RESOLUTION_FIXED) @@ -77,7 +78,7 @@ public class DefaultIssueTest { assertThat(issue.message()).isEqualTo("a message"); assertThat(issue.line()).isEqualTo(7); assertThat(issue.effortToFix()).isEqualTo(1.2d); - assertThat(issue.technicalDebt()).isEqualTo(WorkDayDuration.of(1, 0, 0)); + assertThat(issue.technicalDebt()).isEqualTo(new WorkUnit.Builder().setDays(1).build()); assertThat(issue.actionPlanKey()).isEqualTo("BCDE"); assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED); assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FIXED); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/WorkDayDurationTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/WorkDayDurationTest.java deleted file mode 100644 index ccff88c8624..00000000000 --- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/WorkDayDurationTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.api.issue.internal; - -import org.junit.Test; - -import static org.fest.assertions.Assertions.assertThat; - -public class WorkDayDurationTest { - - @Test - public void from_long_on_simple_values(){ - checkTimes(WorkDayDuration.fromLong(1L), 0, 0, 1); - checkTimes(WorkDayDuration.fromLong(100L), 0, 1, 0); - checkTimes(WorkDayDuration.fromLong(10000L), 1, 0, 0); - } - - @Test - public void from_long_on_complex_values(){ - checkTimes(WorkDayDuration.fromLong(10101L), 1, 1, 1); - checkTimes(WorkDayDuration.fromLong(101L), 0, 1, 1); - checkTimes(WorkDayDuration.fromLong(10001L), 1, 0, 1); - checkTimes(WorkDayDuration.fromLong(10100L), 1, 1, 0); - - checkTimes(WorkDayDuration.fromLong(112233L), 11, 22, 33); - } - - @Test - public void to_long(){ - assertThat(WorkDayDuration.of(1, 1, 1).toLong()).isEqualTo(10101L); - } - - @Test - public void test_equals_and_hashCode() throws Exception { - WorkDayDuration oneMinute = WorkDayDuration.fromLong(1L); - WorkDayDuration oneHours = WorkDayDuration.fromLong(100L); - WorkDayDuration oneDay = WorkDayDuration.fromLong(10000L); - - assertThat(oneMinute).isEqualTo(oneMinute); - assertThat(oneMinute).isEqualTo(WorkDayDuration.fromLong(1L)); - assertThat(oneHours).isEqualTo(WorkDayDuration.fromLong(100L)); - assertThat(oneDay).isEqualTo(WorkDayDuration.fromLong(10000L)); - - assertThat(oneMinute).isNotEqualTo(oneHours); - assertThat(oneHours).isNotEqualTo(oneDay); - - assertThat(oneMinute.hashCode()).isEqualTo(oneMinute.hashCode()); - } - - private void checkTimes(WorkDayDuration technicalDebt, int expectedDays, int expectedHours, int expectedMinutes){ - assertThat(technicalDebt.days()).isEqualTo(expectedDays); - assertThat(technicalDebt.hours()).isEqualTo(expectedHours); - assertThat(technicalDebt.minutes()).isEqualTo(expectedMinutes); - } - -} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/technicaldebt/batch/internal/DefaultRequirementTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/technicaldebt/batch/internal/DefaultRequirementTest.java index 3c8c246165f..f3bda181038 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/technicaldebt/batch/internal/DefaultRequirementTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/technicaldebt/batch/internal/DefaultRequirementTest.java @@ -47,8 +47,8 @@ public class DefaultRequirementTest { .setCharacteristic(characteristic) .setRootCharacteristic(root) .setFunction("linear_offset") - .setFactor(WorkUnit.create(2.0, "mn")) - .setOffset(WorkUnit.create(1.0, "h")) + .setFactor(new WorkUnit.Builder().setMinutes(2).build()) + .setOffset(new WorkUnit.Builder().setHours(1).build()) .setCreatedAt(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19")) .setUpdatedAt(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19")); @@ -57,8 +57,8 @@ public class DefaultRequirementTest { assertThat(requirement.characteristic()).isEqualTo(characteristic); assertThat(requirement.rootCharacteristic()).isEqualTo(root); assertThat(requirement.function()).isEqualTo("linear_offset"); - assertThat(requirement.factor()).isEqualTo(WorkUnit.create(2.0, "mn")); - assertThat(requirement.offset()).isEqualTo(WorkUnit.create(1.0, "h")); + assertThat(requirement.factor()).isEqualTo(new WorkUnit.Builder().setMinutes(2).build()); + assertThat(requirement.offset()).isEqualTo(new WorkUnit.Builder().setHours(1).build()); assertThat(requirement.createdAt()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19")); assertThat(requirement.updatedAt()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19")); } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/technicaldebt/server/internal/DefaultCharacteristicTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/technicaldebt/server/internal/DefaultCharacteristicTest.java index 3217dde1a7b..72835e0705b 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/technicaldebt/server/internal/DefaultCharacteristicTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/technicaldebt/server/internal/DefaultCharacteristicTest.java @@ -56,8 +56,8 @@ public class DefaultCharacteristicTest { .setId(1) .setRuleKey(RuleKey.of("repo", "rule")) .setFunction("linear_offset") - .setFactor(WorkUnit.create(2.0, "mn")) - .setOffset(WorkUnit.create(1.0, "h")) + .setFactor(new WorkUnit.Builder().setMinutes(2).build()) + .setOffset(new WorkUnit.Builder().setHours(1).build()) .setRootId(3) .setParentId(2); @@ -67,8 +67,8 @@ public class DefaultCharacteristicTest { assertThat(requirement.order()).isNull(); assertThat(requirement.ruleKey()).isEqualTo(RuleKey.of("repo", "rule")); assertThat(requirement.function()).isEqualTo("linear_offset"); - assertThat(requirement.factor()).isEqualTo(WorkUnit.create(2.0, "mn")); - assertThat(requirement.offset()).isEqualTo(WorkUnit.create(1.0, "h")); + assertThat(requirement.factor()).isEqualTo(new WorkUnit.Builder().setMinutes(2).build()); + assertThat(requirement.offset()).isEqualTo(new WorkUnit.Builder().setHours(1).build()); assertThat(requirement.parentId()).isEqualTo(2); assertThat(requirement.rootId()).isEqualTo(3); } @@ -92,8 +92,8 @@ public class DefaultCharacteristicTest { .setId(1) .setRuleKey(RuleKey.of("repo", "rule")) .setFunction("linear_offset") - .setFactor(WorkUnit.create(2.0, "mn")) - .setOffset(WorkUnit.create(1.0, "h")) + .setFactor(new WorkUnit.Builder().setMinutes(2).build()) + .setOffset(new WorkUnit.Builder().setHours(1).build()) .setRootId(3) .setParentId(2); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/WorkUnitTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/WorkUnitTest.java index 108f7aa57c2..c77e0b8bbf6 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/WorkUnitTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/WorkUnitTest.java @@ -36,18 +36,10 @@ public class WorkUnitTest { @Test public void create_default() throws Exception { WorkUnit workUnit = WorkUnit.create(); - assertThat(workUnit.getUnit()).isEqualTo("d"); assertThat(workUnit.getValue()).isEqualTo(0.0); } @Test - public void test_equals() throws Exception { - assertThat(WorkUnit.create(2.0, "mn")).isEqualTo(WorkUnit.create(2.0, "mn")); - assertThat(WorkUnit.create(3.0, "mn")).isNotEqualTo(WorkUnit.create(2.0, "mn")); - assertThat(WorkUnit.create(2.0, "h")).isNotEqualTo(WorkUnit.create(2.0, "mn")); - } - - @Test public void fail_with_bad_unit() throws Exception { try { WorkUnit.create(2.0, "z"); @@ -65,4 +57,49 @@ public class WorkUnitTest { } } + @Test + public void from_long_on_simple_values() { + checkTimes(WorkUnit.fromLong(1L), 0, 0, 1); + checkTimes(WorkUnit.fromLong(100L), 0, 1, 0); + checkTimes(WorkUnit.fromLong(10000L), 1, 0, 0); + } + + @Test + public void from_long_on_complex_values() { + checkTimes(WorkUnit.fromLong(10101L), 1, 1, 1); + checkTimes(WorkUnit.fromLong(101L), 0, 1, 1); + checkTimes(WorkUnit.fromLong(10001L), 1, 0, 1); + checkTimes(WorkUnit.fromLong(10100L), 1, 1, 0); + + checkTimes(WorkUnit.fromLong(112233L), 11, 22, 33); + } + + @Test + public void to_long() { + assertThat(new WorkUnit.Builder().setDays(1).setHours(1).setMinutes(1).build().toLong()).isEqualTo(10101L); + } + + @Test + public void test_equals_and_hashCode() throws Exception { + WorkUnit oneMinute = WorkUnit.fromLong(1L); + WorkUnit oneHours = WorkUnit.fromLong(100L); + WorkUnit oneDay = WorkUnit.fromLong(10000L); + + assertThat(oneMinute).isEqualTo(oneMinute); + assertThat(oneMinute).isEqualTo(WorkUnit.fromLong(1L)); + assertThat(oneHours).isEqualTo(WorkUnit.fromLong(100L)); + assertThat(oneDay).isEqualTo(WorkUnit.fromLong(10000L)); + + assertThat(oneMinute).isNotEqualTo(oneHours); + assertThat(oneHours).isNotEqualTo(oneDay); + + assertThat(oneMinute.hashCode()).isEqualTo(oneMinute.hashCode()); + } + + private void checkTimes(WorkUnit technicalDebt, int expectedDays, int expectedHours, int expectedMinutes) { + assertThat(technicalDebt.days()).isEqualTo(expectedDays); + assertThat(technicalDebt.hours()).isEqualTo(expectedHours); + assertThat(technicalDebt.minutes()).isEqualTo(expectedMinutes); + } + } 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 56d38affc15..d92ea0f1cee 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 @@ -21,7 +21,7 @@ package org.sonar.server.issue; import org.sonar.api.ServerComponent; import org.sonar.api.issue.internal.FieldDiffs; -import org.sonar.api.issue.internal.WorkDayDuration; +import org.sonar.api.utils.WorkUnit; import org.sonar.core.i18n.DefaultI18n; import org.sonar.core.issue.IssueUpdater; import org.sonar.server.technicaldebt.DebtFormatter; @@ -74,10 +74,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 = debtFormatter.format(locale, WorkDayDuration.fromLong(Long.parseLong(newValueString))); + newValueString = debtFormatter.format(locale, WorkUnit.fromLong(Long.parseLong(newValueString))); } if (oldValueString != null) { - oldValueString = debtFormatter.format(locale, WorkDayDuration.fromLong(Long.parseLong(oldValueString))); + oldValueString = debtFormatter.format(locale, WorkUnit.fromLong(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 856a4a38c51..096ca6e2255 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 @@ -25,13 +25,13 @@ import org.sonar.api.issue.*; import org.sonar.api.issue.action.Action; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.FieldDiffs; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.RequestHandler; 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.WorkUnit; import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.web.UserRole; import org.sonar.core.issue.workflow.Transition; @@ -47,6 +47,7 @@ import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import java.util.Arrays; import java.util.Date; import java.util.List; @@ -100,7 +101,7 @@ public class IssueShowWsHandler implements RequestHandler { Component project = result.project(issue); String actionPlanKey = issue.actionPlanKey(); ActionPlan actionPlan = result.actionPlan(issue); - WorkDayDuration technicalDebt = issue.technicalDebt(); + WorkUnit technicalDebt = issue.technicalDebt(); Date updateDate = issue.updateDate(); Date closeDate = issue.closeDate(); @@ -114,7 +115,7 @@ public class IssueShowWsHandler implements RequestHandler { .prop("rule", issue.ruleKey().toString()) .prop("ruleName", result.rule(issue).getName()) .prop("line", issue.line()) - .prop("message",issue.message()) + .prop("message", issue.message()) .prop("resolution", issue.resolution()) .prop("status", issue.status()) .prop("severity", issue.severity()) @@ -147,7 +148,7 @@ public class IssueShowWsHandler implements RequestHandler { } @CheckForNull - private Characteristic findCharacteristicById(@Nullable Integer id){ + private Characteristic findCharacteristicById(@Nullable Integer id) { if (id != null) { return technicalDebtManager.findCharacteristicById(id); } diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index 513be22291e..85bc93c8f53 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -58,7 +58,10 @@ import org.sonar.core.profiling.Profiling; import org.sonar.core.purge.PurgeProfiler; import org.sonar.core.resource.DefaultResourcePermissions; import org.sonar.core.rule.DefaultRuleFinder; -import org.sonar.core.technicaldebt.*; +import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager; +import org.sonar.core.technicaldebt.TechnicalDebtModelRepository; +import org.sonar.core.technicaldebt.TechnicalDebtModelSynchronizer; +import org.sonar.core.technicaldebt.TechnicalDebtXMLImporter; import org.sonar.core.test.TestPlanPerspectiveLoader; import org.sonar.core.test.TestablePerspectiveLoader; import org.sonar.core.timemachine.Periods; @@ -365,7 +368,6 @@ public final class Platform { servicesContainer.addSingleton(TechnicalDebtModelSynchronizer.class); servicesContainer.addSingleton(TechnicalDebtModelRepository.class); servicesContainer.addSingleton(TechnicalDebtXMLImporter.class); - servicesContainer.addSingleton(TechnicalDebtConverter.class); servicesContainer.addSingleton(DebtFormatter.class); servicesContainer.addSingleton(DefaultTechnicalDebtManager.class); diff --git a/sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtFormatter.java b/sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtFormatter.java index 5a44a0f432e..faa2f8b7175 100644 --- a/sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtFormatter.java +++ b/sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtFormatter.java @@ -21,7 +21,7 @@ package org.sonar.server.technicaldebt; import org.sonar.api.ServerComponent; -import org.sonar.api.issue.internal.WorkDayDuration; +import org.sonar.api.utils.WorkUnit; import org.sonar.core.i18n.DefaultI18n; import java.util.Locale; @@ -34,7 +34,7 @@ public class DebtFormatter implements ServerComponent { this.defaultI18n = defaultI18n; } - public String format(Locale locale, WorkDayDuration technicalDebt) { + public String format(Locale locale, WorkUnit technicalDebt) { StringBuilder message = new StringBuilder(); if (technicalDebt.days() > 0) { message.append(defaultI18n.message(locale, "issue.technical_debt.x_days", null, technicalDebt.days())); diff --git a/sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtService.java b/sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtService.java index fe89ec33b3b..cb079c12dae 100644 --- a/sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtService.java +++ b/sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtService.java @@ -21,8 +21,8 @@ package org.sonar.server.technicaldebt; import org.sonar.api.ServerComponent; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.technicaldebt.server.Characteristic; +import org.sonar.api.utils.WorkUnit; import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager; import org.sonar.server.user.UserSession; @@ -40,12 +40,12 @@ public class DebtService implements ServerComponent { this.finder = finder; } - public String format(WorkDayDuration technicalDebt) { + public String format(WorkUnit technicalDebt) { return debtFormatter.format(UserSession.get().locale(), technicalDebt); } - public WorkDayDuration toTechnicalDebt(String technicalDebtInLong) { - return WorkDayDuration.fromLong(Long.parseLong(technicalDebtInLong)); + public WorkUnit toTechnicalDebt(String technicalDebtInLong) { + return WorkUnit.fromLong(Long.parseLong(technicalDebtInLong)); } public List<Characteristic> findRootCharacteristics() { 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 9b43022c7d1..754a06a61b5 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 @@ -28,10 +28,10 @@ import org.sonar.api.issue.Issue; import org.sonar.api.issue.IssueQuery; import org.sonar.api.issue.IssueQueryResult; import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.rules.Rule; import org.sonar.api.user.User; import org.sonar.api.user.UserFinder; +import org.sonar.api.utils.WorkUnit; import org.sonar.core.component.ComponentDto; import org.sonar.core.issue.DefaultActionPlan; import org.sonar.core.issue.db.IssueChangeDao; @@ -270,7 +270,7 @@ public class DefaultIssueFinderTest { when(userFinder.findByLogins(anyListOf(String.class))).thenReturn(Lists.<User>newArrayList( new DefaultUser().setLogin("perceval").setName("Perceval"), new DefaultUser().setLogin("arthur").setName("Roi Arthur") - )); + )); IssueQuery query = IssueQuery.builder().build(); @@ -322,7 +322,7 @@ public class DefaultIssueFinderTest { assertThat(results.issues()).hasSize(1); DefaultIssue result = (DefaultIssue) results.issues().iterator().next(); - assertThat(result.technicalDebt()).isEqualTo(WorkDayDuration.of(10, 0, 0)); + assertThat(result.technicalDebt()).isEqualTo(new WorkUnit.Builder().setMinutes(10).build()); } } 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 ff90a7cc425..bb84b7a2b8f 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 @@ -25,7 +25,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.issue.internal.FieldDiffs; -import org.sonar.api.issue.internal.WorkDayDuration; +import org.sonar.api.utils.WorkUnit; import org.sonar.core.i18n.DefaultI18n; import org.sonar.server.technicaldebt.DebtFormatter; @@ -130,8 +130,8 @@ public class IssueChangelogFormatterTest { FieldDiffs diffs = new FieldDiffs(); diffs.setDiff("technicalDebt", "500", "10000"); - when(debtFormatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 5, 0))).thenReturn("5 hours"); - when(debtFormatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 0, 1))).thenReturn("1 days"); + when(debtFormatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setHours(5).build())).thenReturn("5 hours"); + when(debtFormatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setDays(1).build())).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"); @@ -148,7 +148,7 @@ public class IssueChangelogFormatterTest { FieldDiffs diffs = new FieldDiffs(); diffs.setDiff("technicalDebt", null, "10000"); - when(debtFormatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 0, 1))).thenReturn("1 days"); + when(debtFormatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setDays(1).build())).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 aa2ec681d0b..73bdd1ba656 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 @@ -35,7 +35,6 @@ import org.sonar.api.issue.action.Action; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.DefaultIssueComment; import org.sonar.api.issue.internal.FieldDiffs; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; import org.sonar.api.server.ws.WsTester; @@ -43,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.WorkUnit; import org.sonar.api.web.UserRole; import org.sonar.core.issue.DefaultActionPlan; import org.sonar.core.issue.DefaultIssueQueryResult; @@ -209,9 +209,8 @@ public class IssueShowWsHandlerTest { @Test public void show_issue_with_technical_debt() throws Exception { - WorkDayDuration technicalDebt = WorkDayDuration.of(1, 2, 0); - Issue issue = createStandardIssue() - .setTechnicalDebt(technicalDebt); + WorkUnit technicalDebt = new WorkUnit.Builder().setHours(2).setMinutes(1).build(); + Issue issue = createStandardIssue().setTechnicalDebt(technicalDebt); issues.add(issue); when(debtFormatter.format(any(Locale.class), eq(technicalDebt))).thenReturn("2 hours 1 minutes"); @@ -223,9 +222,9 @@ public class IssueShowWsHandlerTest { @Test public void show_issue_with_characteristics() throws Exception { - WorkDayDuration technicalDebt = WorkDayDuration.of(1, 2, 0); - Issue issue = createStandardIssue() - .setTechnicalDebt(technicalDebt); + WorkUnit technicalDebt = new WorkUnit.Builder().setHours(2).setMinutes(1).build(); + ; + Issue issue = createStandardIssue().setTechnicalDebt(technicalDebt); issues.add(issue); Characteristic requirement = new DefaultCharacteristic().setId(5).setParentId(2).setRootId(1); diff --git a/sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtFormatterTest.java b/sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtFormatterTest.java index f7249fb21b5..c9f12afd90e 100644 --- a/sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtFormatterTest.java +++ b/sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtFormatterTest.java @@ -21,7 +21,7 @@ package org.sonar.server.technicaldebt; import org.junit.Test; -import org.sonar.api.issue.internal.WorkDayDuration; +import org.sonar.api.utils.WorkUnit; import org.sonar.core.i18n.DefaultI18n; import java.util.Locale; @@ -43,14 +43,13 @@ public class DebtFormatterTest { when(i18n.message(DEFAULT_LOCALE, "issue.technical_debt.x_hours", null, 2)).thenReturn("2 hours"); when(i18n.message(DEFAULT_LOCALE, "issue.technical_debt.x_minutes", null, 1)).thenReturn("1 minutes"); - assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 0, 5))).isEqualTo("5 days"); - assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 2, 0))).isEqualTo("2 hours"); - assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(1, 0, 0))).isEqualTo("1 minutes"); + assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setDays(5).build())).isEqualTo("5 days"); + assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setHours(2).build())).isEqualTo("2 hours"); + assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setMinutes(1).build())).isEqualTo("1 minutes"); - assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(0, 2, 5))).isEqualTo("5 days 2 hours"); - assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(1, 2, 0))).isEqualTo("2 hours 1 minutes"); - assertThat(formatter.format(DEFAULT_LOCALE, WorkDayDuration.of(1, 2, 5))).isEqualTo("5 days 2 hours"); + assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setDays(5).setHours(2).build())).isEqualTo("5 days 2 hours"); + assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setHours(2).setMinutes(1).build())).isEqualTo("2 hours 1 minutes"); + assertThat(formatter.format(DEFAULT_LOCALE, new WorkUnit.Builder().setDays(5).setHours(2).setMinutes(10).build())).isEqualTo("5 days 2 hours"); } - } diff --git a/sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtServiceTest.java b/sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtServiceTest.java index 586444b6f5e..75684adf2db 100644 --- a/sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtServiceTest.java @@ -20,9 +20,9 @@ package org.sonar.server.technicaldebt; import org.junit.Test; -import org.sonar.api.issue.internal.WorkDayDuration; import org.sonar.api.technicaldebt.server.Characteristic; import org.sonar.api.technicaldebt.server.internal.DefaultCharacteristic; +import org.sonar.api.utils.WorkUnit; import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager; import java.util.List; @@ -42,14 +42,14 @@ public class DebtServiceTest { @Test public void format() { - WorkDayDuration technicalDebt = WorkDayDuration.of(5, 0, 0); + WorkUnit technicalDebt = new WorkUnit.Builder().setMinutes(5).build(); service.format(technicalDebt); verify(debtFormatter).format(any(Locale.class), eq(technicalDebt)); } @Test public void to_technical_debt() { - assertThat(service.toTechnicalDebt("500")).isEqualTo(WorkDayDuration.of(0, 5, 0)); + assertThat(service.toTechnicalDebt("500")).isEqualTo(new WorkUnit.Builder().setHours(5).build()); } @Test |