package org.sonar.server.computation.task.projectanalysis.issue;
import com.google.common.base.MoreObjects;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ListMultimap;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.core.issue.DefaultIssue;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.issue.IssueChangeDto;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.measure.Measure;
import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository;
import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT_KEY;
import static org.sonar.api.measures.CoreMetrics.NEW_TECHNICAL_DEBT_KEY;
+import static org.sonar.api.utils.DateUtils.truncateToSeconds;
/**
* Compute new effort related measures :
*/
public class NewEffortAggregator extends IssueVisitor {
- private final NewEffortCalculator calculator;
private final PeriodHolder periodHolder;
- private final DbClient dbClient;
private final MeasureRepository measureRepository;
private final Metric newMaintainabilityEffortMetric;
private final Metric newReliabilityEffortMetric;
private final Metric newSecurityEffortMetric;
- private ListMultimap<String, IssueChangeDto> changesByIssueUuid = ArrayListMultimap.create();
private Map<Integer, NewEffortCounter> counterByComponentRef = new HashMap<>();
private NewEffortCounter counter = null;
- public NewEffortAggregator(NewEffortCalculator calculator, PeriodHolder periodHolder, DbClient dbClient,
- MetricRepository metricRepository, MeasureRepository measureRepository) {
- this.calculator = calculator;
+ public NewEffortAggregator(PeriodHolder periodHolder, MetricRepository metricRepository, MeasureRepository measureRepository) {
this.periodHolder = periodHolder;
- this.dbClient = dbClient;
this.measureRepository = measureRepository;
this.newMaintainabilityEffortMetric = metricRepository.getByKey(NEW_TECHNICAL_DEBT_KEY);
@Override
public void beforeComponent(Component component) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- List<IssueChangeDto> changes = dbClient.issueChangeDao().selectChangelogOfNonClosedIssuesByComponent(dbSession, component.getUuid());
- for (IssueChangeDto change : changes) {
- changesByIssueUuid.put(change.getIssueKey(), change);
- }
- }
-
- counter = new NewEffortCounter(calculator);
+ counter = new NewEffortCounter();
counterByComponentRef.put(component.getReportAttributes().getRef(), counter);
for (Component child : component.getChildren()) {
NewEffortCounter childSum = counterByComponentRef.remove(child.getReportAttributes().getRef());
@Override
public void onIssue(Component component, DefaultIssue issue) {
if (issue.resolution() == null && issue.effortInMinutes() != null && periodHolder.hasPeriod()) {
- List<IssueChangeDto> changelog = changesByIssueUuid.get(issue.key());
- counter.add(issue, periodHolder.getPeriod(), changelog);
+ counter.add(issue, periodHolder.getPeriod());
}
}
computeMeasure(component, newMaintainabilityEffortMetric, counter.maintainabilitySum);
computeMeasure(component, newReliabilityEffortMetric, counter.reliabilitySum);
computeMeasure(component, newSecurityEffortMetric, counter.securitySum);
- changesByIssueUuid.clear();
counter = null;
}
}
private static class NewEffortCounter {
- private final NewEffortCalculator calculator;
-
private final EffortSum maintainabilitySum = new EffortSum();
private final EffortSum reliabilitySum = new EffortSum();
private final EffortSum securitySum = new EffortSum();
- public NewEffortCounter(NewEffortCalculator calculator) {
- this.calculator = calculator;
- }
-
void add(NewEffortCounter otherCounter) {
maintainabilitySum.add(otherCounter.maintainabilitySum);
reliabilitySum.add(otherCounter.reliabilitySum);
securitySum.add(otherCounter.securitySum);
}
- void add(DefaultIssue issue, Period period, List<IssueChangeDto> changelog) {
- long newEffort = calculator.calculate(issue, changelog, period);
+ void add(DefaultIssue issue, Period period) {
+ long newEffort = calculate(issue, period);
switch (issue.type()) {
case CODE_SMELL:
maintainabilitySum.add(newEffort);
throw new IllegalStateException(String.format("Unknown type '%s'", issue.type()));
}
}
+
+ long calculate(DefaultIssue issue, Period period) {
+ if (issue.creationDate().getTime() > truncateToSeconds(period.getSnapshotDate())) {
+ return MoreObjects.firstNonNull(issue.effortInMinutes(), 0L);
+ }
+ return 0L;
+ }
}
private static class EffortSum {
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.server.computation.task.projectanalysis.issue;
-
-import com.google.common.base.Function;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Ordering;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.FieldDiffs;
-import org.sonar.db.issue.IssueChangeDto;
-import org.sonar.server.computation.task.projectanalysis.period.Period;
-import org.sonar.server.issue.IssueFieldsSetter;
-
-import static com.google.common.collect.FluentIterable.from;
-import static org.sonar.api.utils.DateUtils.truncateToSeconds;
-
-/**
- * Gets the issue debt that was introduced on a period. The algorithm
- * is based on the issue changelog.
- */
-public class NewEffortCalculator {
-
- /**
- * Changelog have to be sorted from newest to oldest.
- * Null date should be the first as this happen when technical debt has changed since previous analysis.
- */
- private static final Comparator<FieldDiffs> CHANGE_ORDERING = Ordering.natural().reverse().nullsFirst().onResultOf((Function<FieldDiffs, Date>) dto -> dto.creationDate());
-
- public long calculate(DefaultIssue issue, Collection<IssueChangeDto> debtChangelog, Period period) {
- if (issue.creationDate().getTime() > truncateToSeconds(period.getSnapshotDate())) {
- return MoreObjects.firstNonNull(issue.effortInMinutes(), 0L);
- }
- return calculateFromChangelog(issue, debtChangelog, period.getSnapshotDate());
- }
-
- private static long calculateFromChangelog(DefaultIssue issue, Collection<IssueChangeDto> debtChangelog, long periodDate) {
- List<FieldDiffs> debtDiffs = from(debtChangelog).transform(ToFieldDiffs.INSTANCE).filter(HasDebtChange.INSTANCE).toSortedList(CHANGE_ORDERING);
- FieldDiffs currentChange = issue.currentChange();
- if (currentChange != null && HasDebtChange.INSTANCE.apply(currentChange)) {
- debtDiffs = Lists.newArrayList(debtDiffs);
- debtDiffs.add(currentChange);
- }
- long newDebt = issue.effortInMinutes();
-
- for (Iterator<FieldDiffs> it = debtDiffs.iterator(); it.hasNext();) {
- FieldDiffs diffs = it.next();
- Date date = diffs.creationDate();
- // TODO use longs
- if (isBeforeOrEqual(date, new Date(periodDate))) {
- // return new value from the change that is just before the period date
- return subtract(newDebt, debtDiff(diffs).newValueLong());
- }
- if (!it.hasNext()) {
- // return old value from the change that is just after the period date when there's no more element in changelog
- return subtract(newDebt, debtDiff(diffs).oldValueLong());
- }
- }
- // no changelog
- return 0L;
- }
-
- /**
- * SONAR-5059
- */
- @CheckForNull
- private static long subtract(long newDebt, @Nullable Long with) {
- if (with != null) {
- return Math.max(0L, newDebt - with);
- }
- return newDebt;
- }
-
- private static boolean isBeforeOrEqual(@Nullable Date changeDate, Date periodDate) {
- return (changeDate != null) && (truncateToSeconds(changeDate.getTime()) <= truncateToSeconds(periodDate.getTime()));
- }
-
- private static FieldDiffs.Diff debtDiff(FieldDiffs diffs) {
- return diffs.diffs().get(IssueFieldsSetter.TECHNICAL_DEBT);
- }
-
- private enum ToFieldDiffs implements Function<IssueChangeDto, FieldDiffs> {
- INSTANCE;
- @Override
- public FieldDiffs apply(@Nonnull IssueChangeDto dto) {
- return dto.toFieldDiffs();
- }
- }
-
- private enum HasDebtChange implements Predicate<FieldDiffs> {
- INSTANCE;
- @Override
- public boolean apply(@Nonnull FieldDiffs diffs) {
- return diffs.diffs().containsKey(IssueFieldsSetter.TECHNICAL_DEBT);
- }
- }
-}
*/
package org.sonar.server.computation.task.projectanalysis.issue;
+import java.util.Date;
+import java.util.Random;
import org.junit.Test;
-import org.mockito.Mockito;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.Duration;
import org.sonar.core.issue.DefaultIssue;
-import org.sonar.db.DbClient;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
import org.sonar.server.computation.task.projectanalysis.measure.Measure;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.guava.api.Assertions.assertThat;
-import static org.mockito.Matchers.anyList;
-import static org.mockito.Matchers.same;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT;
import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT_KEY;
public class NewEffortAggregatorTest {
private static final Period PERIOD = new Period(LEAK_PERIOD_MODE_PREVIOUS_ANALYSIS, null, 1_500_000_000L, "U1");
+ private static final long[] OLD_ISSUES_DATES = new long[] {
+ PERIOD.getSnapshotDate(),
+ PERIOD.getSnapshotDate() - 1,
+ PERIOD.getSnapshotDate() - 1_200_000L,
+ };
private static final Component FILE = ReportComponent.builder(Component.Type.FILE, 1).setUuid("FILE").build();
private static final Component PROJECT = ReportComponent.builder(Component.Type.PROJECT, 2).addChildren(FILE).build();
- private NewEffortCalculator calculator = mock(NewEffortCalculator.class);
-
@org.junit.Rule
public PeriodHolderRule periodsHolder = new PeriodHolderRule();
-
@org.junit.Rule
public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
.add(NEW_TECHNICAL_DEBT)
.add(NEW_RELIABILITY_REMEDIATION_EFFORT)
.add(NEW_SECURITY_REMEDIATION_EFFORT);
-
@org.junit.Rule
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create();
- private DbClient dbClient = mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS);
-
- private NewEffortAggregator underTest = new NewEffortAggregator(calculator, periodsHolder, dbClient, metricRepository, measureRepository);
+ private final Random random = new Random();
+ private NewEffortAggregator underTest = new NewEffortAggregator(periodsHolder, metricRepository, measureRepository);
@Test
public void sum_new_maintainability_effort_of_issues() {
periodsHolder.setPeriod(PERIOD);
DefaultIssue unresolved1 = newCodeSmellIssue(10L);
+ DefaultIssue old1 = oldCodeSmellIssue(100L);
DefaultIssue unresolved2 = newCodeSmellIssue(30L);
+ DefaultIssue old2 = oldCodeSmellIssue(300L);
DefaultIssue unresolvedWithoutDebt = newCodeSmellIssueWithoutEffort();
DefaultIssue resolved = newCodeSmellIssue(50L).setResolution(RESOLUTION_FIXED);
- when(calculator.calculate(same(unresolved1), anyList(), same(PERIOD))).thenReturn(4L);
- when(calculator.calculate(same(unresolved2), anyList(), same(PERIOD))).thenReturn(3L);
- verifyNoMoreInteractions(calculator);
-
underTest.beforeComponent(FILE);
underTest.onIssue(FILE, unresolved1);
+ underTest.onIssue(FILE, old1);
underTest.onIssue(FILE, unresolved2);
+ underTest.onIssue(FILE, old2);
underTest.onIssue(FILE, unresolvedWithoutDebt);
underTest.onIssue(FILE, resolved);
underTest.afterComponent(FILE);
- assertVariation(FILE, NEW_TECHNICAL_DEBT_KEY, 3 + 4);
+ assertVariation(FILE, NEW_TECHNICAL_DEBT_KEY, 10 + 30);
}
@Test
public void new_maintainability_effort_is_only_computed_using_code_smell_issues() {
periodsHolder.setPeriod(PERIOD);
DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
+ DefaultIssue oldSmellIssue = oldCodeSmellIssue(100);
// Issues of type BUG and VULNERABILITY should be ignored
DefaultIssue bugIssue = newBugIssue(15);
+ DefaultIssue oldBugIssue = oldBugIssue(150);
DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
-
- when(calculator.calculate(same(codeSmellIssue), anyList(), same(PERIOD))).thenReturn(4L);
- when(calculator.calculate(same(bugIssue), anyList(), same(PERIOD))).thenReturn(3L);
- when(calculator.calculate(same(vulnerabilityIssue), anyList(), same(PERIOD))).thenReturn(5L);
+ DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
underTest.beforeComponent(FILE);
underTest.onIssue(FILE, codeSmellIssue);
+ underTest.onIssue(FILE, oldSmellIssue);
underTest.onIssue(FILE, bugIssue);
+ underTest.onIssue(FILE, oldBugIssue);
underTest.onIssue(FILE, vulnerabilityIssue);
+ underTest.onIssue(FILE, oldVulnerabilityIssue);
underTest.afterComponent(FILE);
// Only effort of CODE SMELL issue is used
- assertVariation(FILE, NEW_TECHNICAL_DEBT_KEY, 4);
+ assertVariation(FILE, NEW_TECHNICAL_DEBT_KEY, 10);
}
@Test
public void sum_new_reliability_effort_of_issues() {
periodsHolder.setPeriod(PERIOD);
DefaultIssue unresolved1 = newBugIssue(10L);
+ DefaultIssue old1 = oldBugIssue(100L);
DefaultIssue unresolved2 = newBugIssue(30L);
+ DefaultIssue old2 = oldBugIssue(300L);
DefaultIssue unresolvedWithoutDebt = newBugIssueWithoutEffort();
DefaultIssue resolved = newBugIssue(50L).setResolution(RESOLUTION_FIXED);
- when(calculator.calculate(same(unresolved1), anyList(), same(PERIOD))).thenReturn(4L);
- when(calculator.calculate(same(unresolved2), anyList(), same(PERIOD))).thenReturn(3L);
- verifyNoMoreInteractions(calculator);
-
underTest.beforeComponent(FILE);
underTest.onIssue(FILE, unresolved1);
+ underTest.onIssue(FILE, old1);
underTest.onIssue(FILE, unresolved2);
+ underTest.onIssue(FILE, old2);
underTest.onIssue(FILE, unresolvedWithoutDebt);
underTest.onIssue(FILE, resolved);
underTest.afterComponent(FILE);
- assertVariation(FILE, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 3 + 4);
+ assertVariation(FILE, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 10 + 30);
}
@Test
public void new_reliability_effort_is_only_computed_using_bug_issues() {
periodsHolder.setPeriod(PERIOD);
DefaultIssue bugIssue = newBugIssue(15);
+ DefaultIssue oldBugIssue = oldBugIssue(150);
// Issues of type CODE SMELL and VULNERABILITY should be ignored
DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
+ DefaultIssue oldCodeSmellIssue = oldCodeSmellIssue(100);
DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
-
- when(calculator.calculate(same(bugIssue), anyList(), same(PERIOD))).thenReturn(3L);
- when(calculator.calculate(same(codeSmellIssue), anyList(), same(PERIOD))).thenReturn(4L);
- when(calculator.calculate(same(vulnerabilityIssue), anyList(), same(PERIOD))).thenReturn(5L);
+ DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
underTest.beforeComponent(FILE);
underTest.onIssue(FILE, bugIssue);
+ underTest.onIssue(FILE, oldBugIssue);
underTest.onIssue(FILE, codeSmellIssue);
+ underTest.onIssue(FILE, oldCodeSmellIssue);
underTest.onIssue(FILE, vulnerabilityIssue);
+ underTest.onIssue(FILE, oldVulnerabilityIssue);
underTest.afterComponent(FILE);
// Only effort of BUG issue is used
- assertVariation(FILE, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 3);
+ assertVariation(FILE, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 15);
}
@Test
- public void sum_new_securtiy_effort_of_issues() {
+ public void sum_new_vulnerability_effort_of_issues() {
periodsHolder.setPeriod(PERIOD);
DefaultIssue unresolved1 = newVulnerabilityIssue(10L);
+ DefaultIssue old1 = oldVulnerabilityIssue(100L);
DefaultIssue unresolved2 = newVulnerabilityIssue(30L);
+ DefaultIssue old2 = oldVulnerabilityIssue(300L);
DefaultIssue unresolvedWithoutDebt = newVulnerabilityIssueWithoutEffort();
DefaultIssue resolved = newVulnerabilityIssue(50L).setResolution(RESOLUTION_FIXED);
-
- when(calculator.calculate(same(unresolved1), anyList(), same(PERIOD))).thenReturn(4L);
- when(calculator.calculate(same(unresolved2), anyList(), same(PERIOD))).thenReturn(3L);
- verifyNoMoreInteractions(calculator);
+ DefaultIssue oldResolved = oldVulnerabilityIssue(500L).setResolution(RESOLUTION_FIXED);
underTest.beforeComponent(FILE);
underTest.onIssue(FILE, unresolved1);
+ underTest.onIssue(FILE, old1);
underTest.onIssue(FILE, unresolved2);
+ underTest.onIssue(FILE, old2);
underTest.onIssue(FILE, unresolvedWithoutDebt);
underTest.onIssue(FILE, resolved);
+ underTest.onIssue(FILE, oldResolved);
underTest.afterComponent(FILE);
- assertVariation(FILE, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 3 + 4);
+ assertVariation(FILE, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 10 + 30);
}
@Test
public void new_security_effort_is_only_computed_using_vulnerability_issues() {
periodsHolder.setPeriod(PERIOD);
DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
+ DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
// Issues of type CODE SMELL and BUG should be ignored
DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
+ DefaultIssue oldCodeSmellIssue = oldCodeSmellIssue(100);
DefaultIssue bugIssue = newBugIssue(15);
-
- when(calculator.calculate(same(vulnerabilityIssue), anyList(), same(PERIOD))).thenReturn(5L);
- when(calculator.calculate(same(codeSmellIssue), anyList(), same(PERIOD))).thenReturn(4L);
- when(calculator.calculate(same(bugIssue), anyList(), same(PERIOD))).thenReturn(3L);
+ DefaultIssue oldBugIssue = oldBugIssue(150);
underTest.beforeComponent(FILE);
underTest.onIssue(FILE, codeSmellIssue);
+ underTest.onIssue(FILE, oldCodeSmellIssue);
underTest.onIssue(FILE, bugIssue);
+ underTest.onIssue(FILE, oldBugIssue);
underTest.onIssue(FILE, vulnerabilityIssue);
+ underTest.onIssue(FILE, oldVulnerabilityIssue);
underTest.afterComponent(FILE);
// Only effort of VULNERABILITY issue is used
- assertVariation(FILE, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 5);
+ assertVariation(FILE, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 12);
}
@Test
periodsHolder.setPeriod(PERIOD);
DefaultIssue codeSmellIssue = newCodeSmellIssue(10);
- when(calculator.calculate(same(codeSmellIssue), anyList(), same(PERIOD))).thenReturn(4L);
+ DefaultIssue oldCodeSmellIssue = oldCodeSmellIssue(100);
DefaultIssue bugIssue = newBugIssue(8);
- when(calculator.calculate(same(bugIssue), anyList(), same(PERIOD))).thenReturn(3L);
+ DefaultIssue oldBugIssue = oldBugIssue(80);
DefaultIssue vulnerabilityIssue = newVulnerabilityIssue(12);
- when(calculator.calculate(same(vulnerabilityIssue), anyList(), same(PERIOD))).thenReturn(6L);
+ DefaultIssue oldVulnerabilityIssue = oldVulnerabilityIssue(120);
DefaultIssue codeSmellProjectIssue = newCodeSmellIssue(30);
- when(calculator.calculate(same(codeSmellProjectIssue), anyList(), same(PERIOD))).thenReturn(1L);
+ DefaultIssue oldCodeSmellProjectIssue = oldCodeSmellIssue(300);
DefaultIssue bugProjectIssue = newBugIssue(28);
- when(calculator.calculate(same(bugProjectIssue), anyList(), same(PERIOD))).thenReturn(2L);
+ DefaultIssue oldBugProjectIssue = oldBugIssue(280);
DefaultIssue vulnerabilityProjectIssue = newVulnerabilityIssue(32);
- when(calculator.calculate(same(vulnerabilityProjectIssue), anyList(), same(PERIOD))).thenReturn(4L);
+ DefaultIssue oldVulnerabilityProjectIssue = oldVulnerabilityIssue(320);
underTest.beforeComponent(FILE);
underTest.onIssue(FILE, codeSmellIssue);
+ underTest.onIssue(FILE, oldCodeSmellIssue);
underTest.onIssue(FILE, bugIssue);
+ underTest.onIssue(FILE, oldBugIssue);
underTest.onIssue(FILE, vulnerabilityIssue);
+ underTest.onIssue(FILE, oldVulnerabilityIssue);
underTest.afterComponent(FILE);
underTest.beforeComponent(PROJECT);
underTest.onIssue(PROJECT, codeSmellProjectIssue);
+ underTest.onIssue(PROJECT, oldCodeSmellProjectIssue);
underTest.onIssue(PROJECT, bugProjectIssue);
+ underTest.onIssue(PROJECT, oldBugProjectIssue);
underTest.onIssue(PROJECT, vulnerabilityProjectIssue);
+ underTest.onIssue(PROJECT, oldVulnerabilityProjectIssue);
underTest.afterComponent(PROJECT);
- assertVariation(PROJECT, NEW_TECHNICAL_DEBT_KEY, 4 + 1);
- assertVariation(PROJECT, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 3 + 2);
- assertVariation(PROJECT, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 6 + 4);
+ assertVariation(PROJECT, NEW_TECHNICAL_DEBT_KEY, 10 + 30);
+ assertVariation(PROJECT, NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, 8 + 28);
+ assertVariation(PROJECT, NEW_SECURITY_REMEDIATION_EFFORT_KEY, 12 + 32);
}
@Test
public void no_measures_if_no_periods() {
periodsHolder.setPeriod(null);
- DefaultIssue unresolved = new DefaultIssue().setEffort(Duration.create(10));
- verifyZeroInteractions(calculator);
+ DefaultIssue unresolved = newCodeSmellIssue(10);
underTest.beforeComponent(FILE);
underTest.onIssue(FILE, unresolved);
}
private static DefaultIssue newCodeSmellIssue(long effort) {
- return newCodeSmellIssueWithoutEffort().setEffort(Duration.create(effort)).setType(RuleType.CODE_SMELL);
+ return newCodeSmellIssueWithoutEffort()
+ .setEffort(Duration.create(effort))
+ .setType(RuleType.CODE_SMELL)
+ .setCreationDate(new Date(PERIOD.getSnapshotDate() + 10_000L));
+ }
+
+ private DefaultIssue oldCodeSmellIssue(long effort) {
+ return newCodeSmellIssueWithoutEffort()
+ .setEffort(Duration.create(effort))
+ .setType(RuleType.CODE_SMELL)
+ .setCreationDate(new Date(OLD_ISSUES_DATES[random.nextInt(OLD_ISSUES_DATES.length)]));
}
private static DefaultIssue newBugIssue(long effort) {
- return newCodeSmellIssueWithoutEffort().setEffort(Duration.create(effort)).setType(RuleType.BUG);
+ return newCodeSmellIssueWithoutEffort()
+ .setEffort(Duration.create(effort))
+ .setType(RuleType.BUG)
+ .setCreationDate(new Date(PERIOD.getSnapshotDate() + 10_000L));
+ }
+
+ private DefaultIssue oldBugIssue(long effort) {
+ return newCodeSmellIssueWithoutEffort()
+ .setEffort(Duration.create(effort))
+ .setType(RuleType.BUG)
+ .setCreationDate(new Date(OLD_ISSUES_DATES[random.nextInt(OLD_ISSUES_DATES.length)]));
}
private static DefaultIssue newVulnerabilityIssue(long effort) {
- return newCodeSmellIssueWithoutEffort().setEffort(Duration.create(effort)).setType(RuleType.VULNERABILITY);
+ return newCodeSmellIssueWithoutEffort()
+ .setEffort(Duration.create(effort))
+ .setType(RuleType.VULNERABILITY)
+ .setCreationDate(new Date(PERIOD.getSnapshotDate() + 10_000L));
+ }
+
+ private DefaultIssue oldVulnerabilityIssue(long effort) {
+ return newCodeSmellIssueWithoutEffort()
+ .setEffort(Duration.create(effort))
+ .setType(RuleType.VULNERABILITY)
+ .setCreationDate(new Date(OLD_ISSUES_DATES[random.nextInt(OLD_ISSUES_DATES.length)]));
}
private static DefaultIssue newCodeSmellIssueWithoutEffort() {
- return new DefaultIssue().setType(CODE_SMELL);
+ return new DefaultIssue()
+ .setType(CODE_SMELL)
+ .setCreationDate(new Date(PERIOD.getSnapshotDate() + 10_000L));
}
private static DefaultIssue newBugIssueWithoutEffort() {
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.server.computation.task.projectanalysis.issue;
-
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import javax.annotation.Nullable;
-import org.junit.Test;
-import org.sonar.api.utils.Duration;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.FieldDiffs;
-import org.sonar.db.issue.IssueChangeDto;
-import org.sonar.server.computation.task.projectanalysis.period.Period;
-
-import static java.util.Collections.emptyList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.core.config.CorePropertyDefinitions.LEAK_PERIOD_MODE_PREVIOUS_VERSION;
-
-public class NewEffortCalculatorTest {
-
- private static final int HOURS_IN_DAY = 8;
- private static final Duration ONE_DAY = Duration.create(HOURS_IN_DAY * 60 * 60L);
- private static final Duration TWO_DAYS = Duration.create(2 * HOURS_IN_DAY * 60 * 60L);
- private static final Duration FOUR_DAYS = Duration.create(4 * HOURS_IN_DAY * 60 * 60L);
- private static final Duration FIVE_DAYS = Duration.create(5 * HOURS_IN_DAY * 60 * 60L);
- private static final Duration TEN_DAYS = Duration.create(10 * HOURS_IN_DAY * 60 * 60L);
- private static final long PERIOD_DATE = 150000000L;
- private static final String ANALYSIS_UUID = "u1";
- private static final Period PERIOD = new Period(LEAK_PERIOD_MODE_PREVIOUS_VERSION, null, PERIOD_DATE, ANALYSIS_UUID);
-
- DefaultIssue issue = new DefaultIssue();
- NewEffortCalculator underTest = new NewEffortCalculator();
-
- /**
- * New debt is the value of the debt when issue is created during the period
- */
- @Test
- public void total_debt_if_issue_created_during_period() {
- issue.setEffort(TWO_DAYS).setCreationDate(new Date(PERIOD_DATE + 10000));
-
- long newDebt = underTest.calculate(issue, emptyList(), PERIOD);
-
- assertThat(newDebt).isEqualTo(TWO_DAYS.toMinutes());
- }
-
- @Test
- public void new_debt_if_issue_created_before_period() {
- // creation: 1d
- // before period: increased to 2d
- // after period: increased to 5d, decreased to 4d then increased to 10d
- // -> new debt is 10d - 2d = 8d
- issue.setEffort(TEN_DAYS).setCreationDate(new Date(PERIOD_DATE - 10000));
- List<IssueChangeDto> changelog = Arrays.asList(
- newDebtChangelog(ONE_DAY.toMinutes(), TWO_DAYS.toMinutes(), PERIOD_DATE - 9000),
- newDebtChangelog(TWO_DAYS.toMinutes(), FIVE_DAYS.toMinutes(), PERIOD_DATE + 10000),
- newDebtChangelog(FIVE_DAYS.toMinutes(), FOUR_DAYS.toMinutes(), PERIOD_DATE + 20000),
- newDebtChangelog(FOUR_DAYS.toMinutes(), TEN_DAYS.toMinutes(), PERIOD_DATE + 30000)
- );
-
- long newDebt = underTest.calculate(issue, changelog, PERIOD);
-
- assertThat(newDebt).isEqualTo(TEN_DAYS.toMinutes() - TWO_DAYS.toMinutes());
- }
-
- @Test
- public void new_debt_is_positive() {
- // creation: 1d
- // before period: increased to 10d
- // after period: decreased to 2d
- // -> new debt is 2d - 10d = -8d -> 0d
- issue.setEffort(TWO_DAYS).setCreationDate(new Date(PERIOD_DATE - 10000));
- List<IssueChangeDto> changelog = Arrays.asList(
- newDebtChangelog(ONE_DAY.toMinutes(), TEN_DAYS.toMinutes(), PERIOD_DATE - 9000),
- newDebtChangelog(TEN_DAYS.toMinutes(), TWO_DAYS.toMinutes(), PERIOD_DATE + 30000)
- );
-
- long newDebt = underTest.calculate(issue, changelog, PERIOD);
-
- assertThat(newDebt).isEqualTo(0L);
- }
-
- @Test
- public void guess_initial_debt_when_first_change_is_after_period() {
- // creation: 1d
- // after period: increased to 2d, then to 5d
- // -> new debt is 5d - 1d = 4d
- issue.setEffort(FIVE_DAYS).setCreationDate(new Date(PERIOD_DATE - 10000));
- List<IssueChangeDto> changelog = Arrays.asList(
- newDebtChangelog(ONE_DAY.toMinutes(), TWO_DAYS.toMinutes(), PERIOD_DATE + 20000),
- newDebtChangelog(TWO_DAYS.toMinutes(), FIVE_DAYS.toMinutes(), PERIOD_DATE + 30000)
- );
-
- long newDebt = underTest.calculate(issue, changelog, PERIOD);
-
- assertThat(newDebt).isEqualTo(FIVE_DAYS.toMinutes() - ONE_DAY.toMinutes());
- }
-
-
- private static IssueChangeDto newDebtChangelog(long previousValue, long value, @Nullable Long date) {
- FieldDiffs diffs = new FieldDiffs().setDiff("technicalDebt", previousValue, value);
- if (date != null) {
- diffs.setCreationDate(new Date(date));
- }
- return new IssueChangeDto().setIssueChangeCreationDate(date).setChangeData(diffs.toString());
- }
-
-}