Skip to content

Commit

Permalink
SONAR-7337 "sqale_index" and "new_technical_debt" only use "Code Smel…
Browse files Browse the repository at this point in the history
…l" issues
  • Loading branch information
julienlancelot committed Mar 7, 2016
1 parent 566344e commit 42edb96
Show file tree
Hide file tree
Showing 14 changed files with 340 additions and 193 deletions.
Expand Up @@ -39,18 +39,18 @@
import org.sonar.server.computation.issue.CloseIssuesOnRemovedComponentsVisitor; import org.sonar.server.computation.issue.CloseIssuesOnRemovedComponentsVisitor;
import org.sonar.server.computation.issue.ComponentIssuesRepositoryImpl; import org.sonar.server.computation.issue.ComponentIssuesRepositoryImpl;
import org.sonar.server.computation.issue.ComponentsWithUnprocessedIssues; import org.sonar.server.computation.issue.ComponentsWithUnprocessedIssues;
import org.sonar.server.computation.issue.DebtAggregator;
import org.sonar.server.computation.issue.DebtCalculator; import org.sonar.server.computation.issue.DebtCalculator;
import org.sonar.server.computation.issue.DefaultAssignee; import org.sonar.server.computation.issue.DefaultAssignee;
import org.sonar.server.computation.issue.EffortAggregator;
import org.sonar.server.computation.issue.IntegrateIssuesVisitor; import org.sonar.server.computation.issue.IntegrateIssuesVisitor;
import org.sonar.server.computation.issue.IssueAssigner; import org.sonar.server.computation.issue.IssueAssigner;
import org.sonar.server.computation.issue.IssueCache; import org.sonar.server.computation.issue.IssueCache;
import org.sonar.server.computation.issue.IssueCounter; import org.sonar.server.computation.issue.IssueCounter;
import org.sonar.server.computation.issue.IssueLifecycle; import org.sonar.server.computation.issue.IssueLifecycle;
import org.sonar.server.computation.issue.IssueVisitors; import org.sonar.server.computation.issue.IssueVisitors;
import org.sonar.server.computation.issue.LoadComponentUuidsHavingOpenIssuesVisitor; import org.sonar.server.computation.issue.LoadComponentUuidsHavingOpenIssuesVisitor;
import org.sonar.server.computation.issue.NewDebtAggregator; import org.sonar.server.computation.issue.NewEffortAggregator;
import org.sonar.server.computation.issue.NewDebtCalculator; import org.sonar.server.computation.issue.NewEffortCalculator;
import org.sonar.server.computation.issue.RuleRepositoryImpl; import org.sonar.server.computation.issue.RuleRepositoryImpl;
import org.sonar.server.computation.issue.RuleTagsCopier; import org.sonar.server.computation.issue.RuleTagsCopier;
import org.sonar.server.computation.issue.RuleTypeCopier; import org.sonar.server.computation.issue.RuleTypeCopier;
Expand Down Expand Up @@ -176,9 +176,9 @@ private static List componentClasses() {
RuleTypeCopier.class, RuleTypeCopier.class,
RuleTagsCopier.class, RuleTagsCopier.class,
DebtCalculator.class, DebtCalculator.class,
DebtAggregator.class, EffortAggregator.class,
NewDebtCalculator.class, NewEffortCalculator.class,
NewDebtAggregator.class, NewEffortAggregator.class,
IssueAssigner.class, IssueAssigner.class,
IssueCounter.class, IssueCounter.class,


Expand Down
Expand Up @@ -25,6 +25,7 @@
import javax.annotation.CheckForNull; import javax.annotation.CheckForNull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.rules.RuleType;
import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.DefaultIssue;
import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.measure.Measure; import org.sonar.server.computation.measure.Measure;
Expand All @@ -34,16 +35,20 @@


import static com.google.common.collect.Maps.newHashMap; import static com.google.common.collect.Maps.newHashMap;


public class DebtAggregator extends IssueVisitor { /**
* Compute effort related measures :
* {@link CoreMetrics#TECHNICAL_DEBT_KEY}
*/
public class EffortAggregator extends IssueVisitor {


private final RuleRepository ruleRepository; private final RuleRepository ruleRepository;
private final MetricRepository metricRepository; private final MetricRepository metricRepository;
private final MeasureRepository measureRepository; private final MeasureRepository measureRepository;


private final Map<Integer, Debt> debtsByComponentRef = new HashMap<>(); private final Map<Integer, Effort> effortsByComponentRef = new HashMap<>();
private Debt currentDebt; private Effort effort;


public DebtAggregator(RuleRepository ruleRepository, public EffortAggregator(RuleRepository ruleRepository,
MetricRepository metricRepository, MeasureRepository measureRepository) { MetricRepository metricRepository, MeasureRepository measureRepository) {
this.ruleRepository = ruleRepository; this.ruleRepository = ruleRepository;
this.metricRepository = metricRepository; this.metricRepository = metricRepository;
Expand All @@ -52,23 +57,23 @@ public DebtAggregator(RuleRepository ruleRepository,


@Override @Override
public void beforeComponent(Component component) { public void beforeComponent(Component component) {
currentDebt = new Debt(); effort = new Effort();
debtsByComponentRef.put(component.getReportAttributes().getRef(), currentDebt); effortsByComponentRef.put(component.getReportAttributes().getRef(), effort);


// aggregate children counters // aggregate children counters
for (Component child : component.getChildren()) { for (Component child : component.getChildren()) {
// no need to keep the children in memory. They can be garbage-collected. // no need to keep the children in memory. They can be garbage-collected.
Debt childDebt = debtsByComponentRef.remove(child.getReportAttributes().getRef()); Effort childEffort = effortsByComponentRef.remove(child.getReportAttributes().getRef());
if (childDebt != null) { if (childEffort != null) {
currentDebt.add(childDebt); effort.add(childEffort);
} }
} }
} }


@Override @Override
public void onIssue(Component component, DefaultIssue issue) { public void onIssue(Component component, DefaultIssue issue) {
if (issue.resolution() == null) { if (issue.resolution() == null) {
currentDebt.add(issue); effort.add(issue);
} }
} }


Expand All @@ -77,36 +82,38 @@ public void afterComponent(Component component) {
Metric metric = metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY); Metric metric = metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY);


// total value // total value
measureRepository.add(component, metric, Measure.newMeasureBuilder().create(this.currentDebt.minutes)); measureRepository.add(component, metric, Measure.newMeasureBuilder().create(this.effort.maintainabilityEffort));


// TODO delete following lines when working on SONAR-7425
// distribution by rule // distribution by rule
for (Map.Entry<Integer, Long> entry : currentDebt.minutesByRuleId.entrySet()) { for (Map.Entry<Integer, Long> entry : effort.minutesByRuleId.entrySet()) {
int ruleId = entry.getKey(); int ruleId = entry.getKey();
long ruleDebt = entry.getValue(); long ruleDebt = entry.getValue();
// debt can't be zero. // debt can't be zero.
measureRepository.add(component, metric, Measure.newMeasureBuilder().forRule(ruleId).create(ruleDebt)); measureRepository.add(component, metric, Measure.newMeasureBuilder().forRule(ruleId).create(ruleDebt));
} }


this.currentDebt = null; this.effort = null;
} }


private class Debt { private class Effort {
private long minutes = 0L; private long maintainabilityEffort = 0L;
private final SumMap<Integer> minutesByRuleId = new SumMap<>(); private final SumMap<Integer> minutesByRuleId = new SumMap<>();


void add(DefaultIssue issue) { void add(DefaultIssue issue) {
Long issueMinutes = issue.debtInMinutes(); Long issueEffort = issue.debtInMinutes();
if (issueMinutes != null && issueMinutes != 0L) { if (issueEffort != null && issueEffort != 0L) {
this.minutes += issueMinutes; if (issue.type().equals(RuleType.CODE_SMELL)) {

this.maintainabilityEffort += issueEffort;
Rule rule = ruleRepository.getByKey(issue.ruleKey()); Rule rule = ruleRepository.getByKey(issue.ruleKey());
this.minutesByRuleId.add(rule.getId(), issueMinutes); this.minutesByRuleId.add(rule.getId(), issueEffort);
}
} }
} }


public void add(Debt debt) { public void add(Effort effort) {
this.minutes += debt.minutes; this.maintainabilityEffort += effort.maintainabilityEffort;
this.minutesByRuleId.add(debt.minutesByRuleId); this.minutesByRuleId.add(effort.minutesByRuleId);
} }
} }


Expand Down
Expand Up @@ -27,6 +27,7 @@
import java.util.Map; import java.util.Map;
import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.CoreMetrics;
import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.DbClient;
import org.sonar.db.issue.IssueChangeDto; import org.sonar.db.issue.IssueChangeDto;
import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.measure.Measure; import org.sonar.server.computation.measure.Measure;
Expand All @@ -36,22 +37,27 @@
import org.sonar.server.computation.metric.MetricRepository; import org.sonar.server.computation.metric.MetricRepository;
import org.sonar.server.computation.period.Period; import org.sonar.server.computation.period.Period;
import org.sonar.server.computation.period.PeriodsHolder; import org.sonar.server.computation.period.PeriodsHolder;
import org.sonar.db.DbClient;


public class NewDebtAggregator extends IssueVisitor { import static org.sonar.api.rules.RuleType.CODE_SMELL;


private final NewDebtCalculator calculator; /**
* Compute new effort related measures :
* {@link CoreMetrics#NEW_TECHNICAL_DEBT_KEY}
*/
public class NewEffortAggregator extends IssueVisitor {

private final NewEffortCalculator calculator;
private final PeriodsHolder periodsHolder; private final PeriodsHolder periodsHolder;
private final DbClient dbClient; private final DbClient dbClient;
private final MetricRepository metricRepository; private final MetricRepository metricRepository;
private final MeasureRepository measureRepository; private final MeasureRepository measureRepository;


private ListMultimap<String, IssueChangeDto> changesByIssueUuid = ArrayListMultimap.create(); private ListMultimap<String, IssueChangeDto> changesByIssueUuid = ArrayListMultimap.create();
private Map<Integer, DebtSum> sumsByComponentRef = new HashMap<>(); private Map<Integer, EffortSum> sumsByComponentRef = new HashMap<>();
private DebtSum currentSum = null; private EffortSum maintainabilityEffortSum = null;


public NewDebtAggregator(NewDebtCalculator calculator, PeriodsHolder periodsHolder, DbClient dbClient, public NewEffortAggregator(NewEffortCalculator calculator, PeriodsHolder periodsHolder, DbClient dbClient,
MetricRepository metricRepository, MeasureRepository measureRepository) { MetricRepository metricRepository, MeasureRepository measureRepository) {
this.calculator = calculator; this.calculator = calculator;
this.periodsHolder = periodsHolder; this.periodsHolder = periodsHolder;
this.dbClient = dbClient; this.dbClient = dbClient;
Expand All @@ -61,16 +67,16 @@ public NewDebtAggregator(NewDebtCalculator calculator, PeriodsHolder periodsHold


@Override @Override
public void beforeComponent(Component component) { public void beforeComponent(Component component) {
currentSum = new DebtSum(); maintainabilityEffortSum = new EffortSum();
sumsByComponentRef.put(component.getReportAttributes().getRef(), currentSum); sumsByComponentRef.put(component.getReportAttributes().getRef(), maintainabilityEffortSum);
List<IssueChangeDto> changes = dbClient.issueChangeDao().selectChangelogOfNonClosedIssuesByComponent(component.getUuid()); List<IssueChangeDto> changes = dbClient.issueChangeDao().selectChangelogOfNonClosedIssuesByComponent(component.getUuid());
for (IssueChangeDto change : changes) { for (IssueChangeDto change : changes) {
changesByIssueUuid.put(change.getIssueKey(), change); changesByIssueUuid.put(change.getIssueKey(), change);
} }
for (Component child : component.getChildren()) { for (Component child : component.getChildren()) {
DebtSum childSum = sumsByComponentRef.remove(child.getReportAttributes().getRef()); EffortSum childSum = sumsByComponentRef.remove(child.getReportAttributes().getRef());
if (childSum != null) { if (childSum != null) {
currentSum.add(childSum); maintainabilityEffortSum.add(childSum);
} }
} }
} }
Expand All @@ -80,34 +86,36 @@ public void onIssue(Component component, DefaultIssue issue) {
if (issue.resolution() == null && issue.debtInMinutes() != null && !periodsHolder.getPeriods().isEmpty()) { if (issue.resolution() == null && issue.debtInMinutes() != null && !periodsHolder.getPeriods().isEmpty()) {
List<IssueChangeDto> changelog = changesByIssueUuid.get(issue.key()); List<IssueChangeDto> changelog = changesByIssueUuid.get(issue.key());
for (Period period : periodsHolder.getPeriods()) { for (Period period : periodsHolder.getPeriods()) {
long newDebt = calculator.calculate(issue, changelog, period); if (issue.type().equals(CODE_SMELL)) {
currentSum.add(period.getIndex(), newDebt); long newMaintainabilityEffort = calculator.calculate(issue, changelog, period);
maintainabilityEffortSum.add(period.getIndex(), newMaintainabilityEffort);
}
} }
} }
} }


@Override @Override
public void afterComponent(Component component) { public void afterComponent(Component component) {
if (!currentSum.isEmpty) { if (!maintainabilityEffortSum.isEmpty) {
MeasureVariations variations = new MeasureVariations(currentSum.sums); MeasureVariations variations = new MeasureVariations(maintainabilityEffortSum.sums);
Metric metric = metricRepository.getByKey(CoreMetrics.NEW_TECHNICAL_DEBT_KEY); Metric metric = metricRepository.getByKey(CoreMetrics.NEW_TECHNICAL_DEBT_KEY);
measureRepository.add(component, metric, Measure.newMeasureBuilder().setVariations(variations).createNoValue()); measureRepository.add(component, metric, Measure.newMeasureBuilder().setVariations(variations).createNoValue());
} }
changesByIssueUuid.clear(); changesByIssueUuid.clear();
currentSum = null; maintainabilityEffortSum = null;
} }


private static class DebtSum { private static class EffortSum {
private final Double[] sums = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS]; private final Double[] sums = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS];
private boolean isEmpty = true; private boolean isEmpty = true;


void add(int periodIndex, long newDebt) { void add(int periodIndex, long newEffort) {
double previous = Objects.firstNonNull(sums[periodIndex - 1], 0d); double previous = Objects.firstNonNull(sums[periodIndex - 1], 0d);
sums[periodIndex - 1] = previous + newDebt; sums[periodIndex - 1] = previous + newEffort;
isEmpty = false; isEmpty = false;
} }


void add(DebtSum other) { void add(EffortSum other) {
for (int i = 0; i < sums.length; i++) { for (int i = 0; i < sums.length; i++) {
Double otherValue = other.sums[i]; Double otherValue = other.sums[i];
if (otherValue != null) { if (otherValue != null) {
Expand Down
Expand Up @@ -36,17 +36,17 @@
import org.apache.commons.lang.time.DateUtils; import org.apache.commons.lang.time.DateUtils;
import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.FieldDiffs; import org.sonar.core.issue.FieldDiffs;
import org.sonar.server.issue.IssueUpdater;
import org.sonar.db.issue.IssueChangeDto; import org.sonar.db.issue.IssueChangeDto;
import org.sonar.server.computation.period.Period; import org.sonar.server.computation.period.Period;
import org.sonar.server.issue.IssueUpdater;


import static com.google.common.collect.FluentIterable.from; import static com.google.common.collect.FluentIterable.from;


/** /**
* Gets the issue debt that was introduced on a period. The algorithm * Gets the issue debt that was introduced on a period. The algorithm
* is based on the issue changelog. * is based on the issue changelog.
*/ */
public class NewDebtCalculator { public class NewEffortCalculator {


public long calculate(DefaultIssue issue, Collection<IssueChangeDto> debtChangelog, Period period) { public long calculate(DefaultIssue issue, Collection<IssueChangeDto> debtChangelog, Period period) {
if (issue.creationDate().getTime() > period.getSnapshotDate() + 1000L) { if (issue.creationDate().getTime() > period.getSnapshotDate() + 1000L) {
Expand Down

0 comments on commit 42edb96

Please sign in to comment.