aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@gmail.com>2013-10-01 21:56:16 +0200
committerJulien Lancelot <julien.lancelot@gmail.com>2013-10-01 21:56:16 +0200
commitc66ed023d5ef8de630557081b582dbde562eb3ee (patch)
tree40b6becce3d1ca9fcf2cbef1d9c35d6780c18f3d /plugins
parent4887d9c4787f4bb4b15ba232d636df5a78e657a6 (diff)
downloadsonarqube-c66ed023d5ef8de630557081b582dbde562eb3ee.tar.gz
sonarqube-c66ed023d5ef8de630557081b582dbde562eb3ee.zip
SONAR-4716 Add remediation cost when creating issues
Diffstat (limited to 'plugins')
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculator.java141
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java1
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculatorTest.java159
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java1
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;