diff options
author | Julien Lancelot <julien.lancelot@gmail.com> | 2013-10-01 21:56:16 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@gmail.com> | 2013-10-01 21:56:16 +0200 |
commit | c66ed023d5ef8de630557081b582dbde562eb3ee (patch) | |
tree | 40b6becce3d1ca9fcf2cbef1d9c35d6780c18f3d /plugins | |
parent | 4887d9c4787f4bb4b15ba232d636df5a78e657a6 (diff) | |
download | sonarqube-c66ed023d5ef8de630557081b582dbde562eb3ee.tar.gz sonarqube-c66ed023d5ef8de630557081b582dbde562eb3ee.zip |
SONAR-4716 Add remediation cost when creating issues
Diffstat (limited to 'plugins')
4 files changed, 2 insertions, 300 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculator.java deleted file mode 100644 index 6c188e93584..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculator.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.plugins.core.technicaldebt; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Maps; -import org.slf4j.LoggerFactory; -import org.sonar.api.BatchExtension; -import org.sonar.api.batch.DecoratorContext; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.MeasuresFilters; -import org.sonar.api.measures.Metric; -import org.sonar.api.rules.Violation; -import org.sonar.core.technicaldebt.TechnicalDebtCharacteristic; -import org.sonar.core.technicaldebt.TechnicalDebtModel; -import org.sonar.core.technicaldebt.TechnicalDebtRequirement; -import org.sonar.core.technicaldebt.functions.Functions; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * Computes the remediation cost based on the quality and analysis models. - */ -public class TechnicalDebtCalculator implements BatchExtension { - - private double total = 0.0; - private Map<TechnicalDebtCharacteristic, Double> characteristicCosts = Maps.newHashMap(); - private Map<TechnicalDebtRequirement, Double> requirementCosts = Maps.newHashMap(); - - private Functions functions; - private TechnicalDebtModel technicalDebtModel; - - public TechnicalDebtCalculator(TechnicalDebtModel technicalDebtModel, Functions functions) { - this.technicalDebtModel = technicalDebtModel; - this.functions = functions; - } - - public void compute(DecoratorContext context) { - reset(); - - // group violations by requirement - ListMultimap<TechnicalDebtRequirement, Violation> violationsByRequirement = groupViolations(context); - - // the total cost is: cost(violations) - for (TechnicalDebtRequirement requirement : technicalDebtModel.getAllRequirements()) { - List<Violation> violations = violationsByRequirement.get(requirement); - double allViolationsCost = computeRemediationCost(CoreMetrics.TECHNICAL_DEBT, context, requirement, violations); - updateRequirementCosts(requirement, allViolationsCost); - } - } - - public double getTotal() { - return total; - } - - public Map<TechnicalDebtCharacteristic, Double> getCharacteristicCosts() { - return characteristicCosts; - } - - public Map<TechnicalDebtRequirement, Double> getRequirementCosts() { - return requirementCosts; - } - - @VisibleForTesting - protected ListMultimap<TechnicalDebtRequirement, Violation> groupViolations(DecoratorContext context) { - ListMultimap<TechnicalDebtRequirement, Violation> violationsByRequirement = ArrayListMultimap.create(); - for (Violation violation : context.getViolations()) { - String repositoryKey = violation.getRule().getRepositoryKey(); - String key = violation.getRule().getKey(); - TechnicalDebtRequirement requirement = technicalDebtModel.getRequirementByRule(repositoryKey, key); - if (requirement == null) { - LoggerFactory.getLogger(getClass()).debug("No technical debt requirement for: " + repositoryKey + "/" + key); - } else { - violationsByRequirement.put(requirement, violation); - } - } - return violationsByRequirement; - } - - @VisibleForTesting - protected void updateRequirementCosts(TechnicalDebtRequirement requirement, double cost) { - requirementCosts.put(requirement, cost); - total += cost; - propagateCostInParents(requirement.getParent(), cost); - } - - private double computeRemediationCost(Metric metric, DecoratorContext context, TechnicalDebtRequirement requirement, Collection<Violation> violations) { - double cost = 0.0; - if (violations != null) { - cost = functions.calculateCost(requirement, violations); - } - - for (Measure measure : context.getChildrenMeasures(MeasuresFilters.characteristic(metric, requirement.toCharacteristic()))) { - if (measure.getCharacteristic() != null && measure.getCharacteristic().equals(requirement.toCharacteristic()) && measure.getValue() != null) { - cost += measure.getValue(); - } - } - return cost; - } - - private void reset() { - total = 0.0; - characteristicCosts.clear(); - requirementCosts.clear(); - } - - private void propagateCostInParents(TechnicalDebtCharacteristic characteristic, double cost) { - if (characteristic != null) { - Double parentCost = characteristicCosts.get(characteristic); - if (parentCost == null) { - characteristicCosts.put(characteristic, cost); - } else { - characteristicCosts.put(characteristic, cost + parentCost); - } - propagateCostInParents(characteristic.getParent(), cost); - } - } - -} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java index 86374222608..fcc2deb96c6 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java @@ -30,6 +30,7 @@ import org.sonar.api.measures.PersistenceMode; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; +import org.sonar.core.technicaldebt.TechnicalDebtCalculator; import org.sonar.core.technicaldebt.TechnicalDebtCharacteristic; import org.sonar.core.technicaldebt.TechnicalDebtRequirement; import org.sonar.core.technicaldebt.WorkUnitConverter; diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculatorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculatorTest.java deleted file mode 100644 index 7eea344975d..00000000000 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculatorTest.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.plugins.core.technicaldebt; - -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Lists; -import org.apache.commons.lang.time.DateUtils; -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.batch.DecoratorContext; -import org.sonar.api.measures.MeasuresFilter; -import org.sonar.api.qualitymodel.Characteristic; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.Violation; -import org.sonar.core.technicaldebt.TechnicalDebtCharacteristic; -import org.sonar.core.technicaldebt.TechnicalDebtModel; -import org.sonar.core.technicaldebt.TechnicalDebtRequirement; -import org.sonar.core.technicaldebt.functions.Functions; - -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - -public class TechnicalDebtCalculatorTest { - - private static final Date NOW = new Date(System.currentTimeMillis()); - private static final Date YESTERDAY = DateUtils.addDays(NOW, -1); - private static final Date LAST_MONTH = DateUtils.addMonths(NOW, -1); - - private TechnicalDebtModel technicalDebtModel; - private Functions functions; - private TechnicalDebtCalculator remediationCostCalculator; - - @Before - public void initMocks() { - technicalDebtModel = mock(TechnicalDebtModel.class); - functions = mock(Functions.class); - remediationCostCalculator = new TechnicalDebtCalculator(technicalDebtModel, functions); - } - - @Test - public void group_violations_by_requirement() throws Exception { - - TechnicalDebtRequirement requirement1 = mock(TechnicalDebtRequirement.class); - TechnicalDebtRequirement requirement2 = mock(TechnicalDebtRequirement.class); - - Violation violation1 = buildViolation("rule1", "repo1", NOW); - Violation violation2 = buildViolation("rule1", "repo1", NOW); - Violation violation3 = buildViolation("rule2", "repo2", NOW); - Violation violation4 = buildViolation("unmatchable", "repo2", NOW); - - List<Violation> violations = Lists.newArrayList(violation1, violation2, violation3, violation4); - - stub(technicalDebtModel.getRequirementByRule("repo1", "rule1")).toReturn(requirement1); - stub(technicalDebtModel.getRequirementByRule("repo2", "rule2")).toReturn(requirement2); - - DecoratorContext context = mock(DecoratorContext.class); - when(context.getViolations()).thenReturn(violations); - - ListMultimap<TechnicalDebtRequirement, Violation> groupedViolations = remediationCostCalculator.groupViolations(context); - - assertThat(groupedViolations.keySet().size()).isEqualTo(2); - assertThat(groupedViolations.get(requirement1)).containsExactly(violation1, violation2); - assertThat(groupedViolations.get(requirement2)).containsExactly(violation3); - } - - @Test - public void add_cost_with_no_parent() throws Exception { - - double requirementCost = 1.0; - - TechnicalDebtRequirement requirement = mock(TechnicalDebtRequirement.class); - when(requirement.getParent()).thenReturn(null); - - remediationCostCalculator.updateRequirementCosts(requirement, requirementCost); - - assertThat(remediationCostCalculator.getRequirementCosts().get(requirement)).isEqualTo(requirementCost); - assertThat(remediationCostCalculator.getTotal()).isEqualTo(requirementCost); - } - - @Test - public void add_cost_and_propagate_to_parents() throws Exception { - - double requirementCost = 1.0; - - TechnicalDebtCharacteristic parentCharacteristic = new TechnicalDebtCharacteristic(Characteristic.create()); - - TechnicalDebtCharacteristic characteristic = new TechnicalDebtCharacteristic(Characteristic.create(), parentCharacteristic); - - TechnicalDebtRequirement requirement = mock(TechnicalDebtRequirement.class); - when(requirement.getParent()).thenReturn(characteristic); - - remediationCostCalculator.updateRequirementCosts(requirement, requirementCost); - - assertThat(remediationCostCalculator.getRequirementCosts().get(requirement)).isEqualTo(requirementCost); - assertThat(remediationCostCalculator.getCharacteristicCosts().get(characteristic)).isEqualTo(requirementCost); - assertThat(remediationCostCalculator.getCharacteristicCosts().get(parentCharacteristic)).isEqualTo(requirementCost); - } - - @Test - public void compute_totals_costs() throws Exception { - - TechnicalDebtRequirement requirement1 = mock(TechnicalDebtRequirement.class); - TechnicalDebtRequirement requirement2 = mock(TechnicalDebtRequirement.class); - - Violation violation1 = buildViolation("rule1", "repo1", NOW); - Violation violation2 = buildViolation("rule1", "repo1", NOW); - Violation violation3 = buildViolation("rule2", "repo2", YESTERDAY); - Violation violation4 = buildViolation("rule2", "repo2", LAST_MONTH); - - List<Violation> violations = Lists.newArrayList(violation1, violation2, violation3, violation4); - - stub(technicalDebtModel.getRequirementByRule("repo1", "rule1")).toReturn(requirement1); - stub(technicalDebtModel.getRequirementByRule("repo2", "rule2")).toReturn(requirement2); - stub(technicalDebtModel.getAllRequirements()).toReturn(Lists.newArrayList(requirement1, requirement2)); - - stub(functions.calculateCost(any(TechnicalDebtRequirement.class), any(Collection.class))).toReturn(1.0); - - DecoratorContext context = mock(DecoratorContext.class); - stub(context.getViolations()).toReturn(violations); - stub(context.getChildrenMeasures(any(MeasuresFilter.class))).toReturn(Collections.EMPTY_LIST); - - remediationCostCalculator.compute(context); - -// assertThat(remediationCostCalculator.getTotal()).isEqualTo(2.0); - assertThat(remediationCostCalculator.getRequirementCosts().get(requirement1)).isEqualTo(1.0); - assertThat(remediationCostCalculator.getRequirementCosts().get(requirement2)).isEqualTo(1.0); - } - - private Violation buildViolation(String ruleKey, String repositoryKey, Date creationDate) { - Violation violation = mock(Violation.class); - stub(violation.getRule()).toReturn(Rule.create(repositoryKey, ruleKey)); - stub(violation.getCreatedAt()).toReturn(creationDate); - return violation; - } -} - diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java index 65bfafefa10..96988f33f0d 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java @@ -28,6 +28,7 @@ import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.resources.Qualifiers; import org.sonar.api.test.IsMeasure; +import org.sonar.core.technicaldebt.TechnicalDebtCalculator; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.any; |