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;
NewFalsePositiveNotificationDispatcher.newMetadata(),
// technical debt
- TechnicalDebtConverter.class,
TechnicalDebtDecorator.class,
NewTechnicalDebtDecorator.class,
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;
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;
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);
}
}
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;
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) {
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
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) {
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;
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;
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) {
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;
+ }
}
}
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")
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;
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;
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
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;
@Mock
DecoratorContext context;
- @Mock
- TechnicalDebtConverter technicalDebtConverter;
-
Date rightNow;
Date elevenDaysAgo;
Date tenDaysAgo;
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);
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
verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)));
}
- private Long fromWorkDayDuration(WorkDayDuration workDayDuration){
+ private Long fromWorkDayDuration(WorkUnit workDayDuration) {
return workDayDuration.toLong();
}
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;
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;
@Mock
Resource resource;
- @Mock
- TechnicalDebtConverter converter;
-
@Mock
TechnicalDebtModel defaultTechnicalDebtModel;
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
@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));
verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, null, requirement, 1.0)));
}
+ @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));
@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);
@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");
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;
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;
// technical debt
TechnicalDebtModelLoader.class,
- TechnicalDebtConverter.class,
TechnicalDebtCalculator.class,
new TechnicalDebtModelProvider(),
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;
}
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();
+ }
}
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;
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;
.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());
}
}
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 {
@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
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
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
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
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
when(model.requirementsByRule(ruleKey)).thenReturn(null);
assertThat(remediationCostCalculator.calculTechnicalDebt(issue)).isNull();
- verify(converter, never()).fromMinutes(anyLong());
}
@Test
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);
}
}
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());
}
}
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;
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())) {
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);
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);
}
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())) {
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;
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);
+++ /dev/null
-/*
- * 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);
- }
-
-}
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;
@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();
}
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;
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");
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");
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;
.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")
// updated fields
.setLine(5000)
- .setTechnicalDebt(WorkDayDuration.of(10, 0, 0))
+ .setTechnicalDebt(new WorkUnit.Builder().setMinutes(10).build())
.setChecksum("FFFFF")
.setAuthorLogin("simon")
.setAssignee("loic")
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
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
.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);
+++ /dev/null
-/*
- * 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);
- }
-
-}
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);
// 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);
}
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) {
* @since 4.2
*/
String CORE_AUTHENTICATOR_LOCAL_USERS = "sonar.security.localUsers";
+
+ /**
+ * @since 4.0
+ */
+ String HOURS_IN_DAY = "sonar.technicalDebt.hoursInDay";
}
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;
private String message;
private Integer line;
private Double effortToFix;
- private WorkDayDuration technicalDebt;
+ private WorkUnit technicalDebt;
private String status;
private String resolution;
private String reporter;
* 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;
}
+++ /dev/null
-/*
- * 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;
- }
-
-}
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() {
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) {
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
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;
}
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);
+ }
+ }
}
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;
.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)
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);
+++ /dev/null
-/*
- * 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);
- }
-
-}
.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"));
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"));
}
.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);
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);
}
.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);
@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 {
}
}
+ @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);
+ }
+
}
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;
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);
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;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+
import java.util.Arrays;
import java.util.Date;
import java.util.List;
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();
.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())
}
@CheckForNull
- private Characteristic findCharacteristicById(@Nullable Integer id){
+ private Characteristic findCharacteristicById(@Nullable Integer id) {
if (id != null) {
return technicalDebtManager.findCharacteristicById(id);
}
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;
servicesContainer.addSingleton(TechnicalDebtModelSynchronizer.class);
servicesContainer.addSingleton(TechnicalDebtModelRepository.class);
servicesContainer.addSingleton(TechnicalDebtXMLImporter.class);
- servicesContainer.addSingleton(TechnicalDebtConverter.class);
servicesContainer.addSingleton(DebtFormatter.class);
servicesContainer.addSingleton(DefaultTechnicalDebtManager.class);
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;
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()));
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;
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() {
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;
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();
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());
}
}
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;
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");
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");
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;
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;
@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");
@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);
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;
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");
}
-
}
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;
@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