summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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/TechnicalDebtDecoratorTest.java1
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java6
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java48
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java13
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtCalculator.java (renamed from plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculator.java)16
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModel.java11
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtRequirement.java3
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/WorkUnitConverter.java17
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/AbstractFunction.java9
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/ConstantFunction.java7
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/Function.java5
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/Functions.java9
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearFunction.java8
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearWithOffsetFunction.java4
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearWithThresholdFunction.java4
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml8
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql1
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl1
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueBuilderTest.java4
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/db/IssueDtoTest.java4
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/db/IssueMapperTest.java4
-rw-r--r--sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtCalculatorTest.java (renamed from plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculatorTest.java)46
-rw-r--r--sonar-core/src/test/java/org/sonar/core/technicaldebt/WorkUnitConverterTest.java14
-rw-r--r--sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/ConstantFunctionTest.java21
-rw-r--r--sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/FunctionsTest.java2
-rw-r--r--sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearFunctionTest.java38
-rw-r--r--sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearWithOffsetFunctionTest.java6
-rw-r--r--sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearWithThresholdFunctionTest.java8
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testInsert-result.xml1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate.xml1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/updateBeforeSelectedDate_with_conflict-result.xml1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/updateBeforeSelectedDate_with_conflict.xml1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates-result.xml20
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates.xml20
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues-result.xml1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues.xml1
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues-result.xml10
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues.xml10
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues-result.xml10
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues.xml10
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java7
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java18
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java8
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb3
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/db/migrate/442_add_remediation_cost_to_issue.rb32
-rw-r--r--sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issue.java4
-rw-r--r--sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssue.java5
-rw-r--r--sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/IssueJsonParserTest.java3
-rw-r--r--sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/search.json1
53 files changed, 343 insertions, 147 deletions
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/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;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
index 7bef92697a9..a3f695151f0 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
@@ -26,6 +26,7 @@ import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.ActiveRule;
import org.sonar.api.rules.Violation;
import org.sonar.core.issue.DefaultIssueBuilder;
+import org.sonar.core.technicaldebt.TechnicalDebtCalculator;
import javax.annotation.Nullable;
@@ -38,12 +39,14 @@ public class ModuleIssues {
private final IssueCache cache;
private final Project project;
private final IssueFilters filters;
+ private final TechnicalDebtCalculator technicalDebtCalculator;
- public ModuleIssues(RulesProfile qProfile, IssueCache cache, Project project, IssueFilters filters) {
+ public ModuleIssues(RulesProfile qProfile, IssueCache cache, Project project, IssueFilters filters, TechnicalDebtCalculator technicalDebtCalculator) {
this.qProfile = qProfile;
this.cache = cache;
this.project = project;
this.filters = filters;
+ this.technicalDebtCalculator = technicalDebtCalculator;
}
public boolean initAndAddIssue(DefaultIssue issue) {
@@ -79,6 +82,7 @@ public class ModuleIssues {
if (issue.severity() == null) {
issue.setSeverity(activeRule.getSeverity().name());
}
+ issue.setRemediationCost(technicalDebtCalculator.cost(issue));
if (filters.accept(issue, violation)) {
cache.put(issue);
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
index 2fc2f8d2543..0097856a211 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
@@ -34,6 +34,7 @@ import org.sonar.api.rules.ActiveRule;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RulePriority;
import org.sonar.api.rules.Violation;
+import org.sonar.core.technicaldebt.TechnicalDebtCalculator;
import java.util.Calendar;
import java.util.Date;
@@ -42,10 +43,7 @@ import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
public class ModuleIssuesTest {
@@ -55,7 +53,8 @@ public class ModuleIssuesTest {
RulesProfile qProfile = mock(RulesProfile.class);
Project project = mock(Project.class);
IssueFilters filters = mock(IssueFilters.class);
- ModuleIssues moduleIssues = new ModuleIssues(qProfile, cache, project, filters);
+ TechnicalDebtCalculator technicalDebtCalculator = mock(TechnicalDebtCalculator.class);
+ ModuleIssues moduleIssues = new ModuleIssues(qProfile, cache, project, filters, technicalDebtCalculator);
@Before
public void setUp() {
@@ -64,7 +63,7 @@ public class ModuleIssuesTest {
}
@Test
- public void should_ignore_null_active_rule() throws Exception {
+ public void ignore_null_active_rule() throws Exception {
when(qProfile.getActiveRule(anyString(), anyString())).thenReturn(null);
DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY);
@@ -75,7 +74,7 @@ public class ModuleIssuesTest {
}
@Test
- public void should_ignore_null_rule_of_active_rule() throws Exception {
+ public void ignore_null_rule_of_active_rule() throws Exception {
ActiveRule activeRule = mock(ActiveRule.class);
when(activeRule.getRule()).thenReturn(null);
when(qProfile.getActiveRule(anyString(), anyString())).thenReturn(activeRule);
@@ -88,7 +87,7 @@ public class ModuleIssuesTest {
}
@Test
- public void should_add_issue_to_cache() throws Exception {
+ public void add_issue_to_cache() throws Exception {
Rule rule = Rule.create("squid", "AvoidCycle");
ActiveRule activeRule = mock(ActiveRule.class);
when(activeRule.getRule()).thenReturn(rule);
@@ -114,7 +113,7 @@ public class ModuleIssuesTest {
}
@Test
- public void should_use_severity_from_active_rule_if_no_severity() throws Exception {
+ public void use_severity_from_active_rule_if_no_severity() throws Exception {
Rule rule = Rule.create("squid", "AvoidCycle");
ActiveRule activeRule = mock(ActiveRule.class);
when(activeRule.getRule()).thenReturn(rule);
@@ -135,7 +134,7 @@ public class ModuleIssuesTest {
}
@Test
- public void should_add_deprecated_violation() throws Exception {
+ public void add_deprecated_violation() throws Exception {
Rule rule = Rule.create("squid", "AvoidCycle");
Resource resource = new JavaFile("org.struts.Action").setEffectiveKey("struts:org.struts.Action");
Violation violation = new Violation(rule, resource);
@@ -164,7 +163,7 @@ public class ModuleIssuesTest {
}
@Test
- public void should_filter_issue() throws Exception {
+ public void filter_issue() throws Exception {
Rule rule = Rule.create("squid", "AvoidCycle");
ActiveRule activeRule = mock(ActiveRule.class);
when(activeRule.getRule()).thenReturn(rule);
@@ -184,4 +183,31 @@ public class ModuleIssuesTest {
verifyZeroInteractions(cache);
}
+ @Test
+ public void set_remediation_cost() throws Exception {
+ Rule rule = Rule.create("squid", "AvoidCycle");
+ ActiveRule activeRule = mock(ActiveRule.class);
+ when(activeRule.getRule()).thenReturn(rule);
+ when(activeRule.getSeverity()).thenReturn(RulePriority.INFO);
+ when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule);
+
+ Date analysisDate = new Date();
+ when(project.getAnalysisDate()).thenReturn(analysisDate);
+
+
+ DefaultIssue issue = new DefaultIssue()
+ .setKey("ABCDE")
+ .setRuleKey(SQUID_RULE_KEY)
+ .setSeverity(Severity.CRITICAL);
+
+ when(technicalDebtCalculator.cost(issue)).thenReturn(10L);
+ 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().remediationCost()).isEqualTo(10L);
+ }
+
}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java
index b86a7b6a15a..e251095ede5 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java
@@ -29,7 +29,6 @@ import org.sonar.api.utils.KeyValueFormat;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-
import java.io.Serializable;
import java.util.Date;
@@ -48,6 +47,7 @@ public final class IssueDto implements Serializable {
private String message;
private Integer line;
private Double effortToFix;
+ private Long remediationCost;
private String status;
private String resolution;
private String checksum;
@@ -181,6 +181,16 @@ public final class IssueDto implements Serializable {
return this;
}
+ @CheckForNull
+ public Long getRemediationCost() {
+ return remediationCost;
+ }
+
+ public IssueDto setRemediationCost(@Nullable Long remediationCost) {
+ this.remediationCost = remediationCost;
+ return this;
+ }
+
public String getStatus() {
return status;
}
@@ -408,6 +418,7 @@ public final class IssueDto implements Serializable {
issue.setResolution(resolution);
issue.setMessage(message);
issue.setEffortToFix(effortToFix);
+ issue.setRemediationCost(remediationCost);
issue.setLine(line);
issue.setSeverity(severity);
issue.setReporter(reporter);
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
index e49c6615aed..f8c8211981e 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
@@ -33,7 +33,7 @@ import java.util.List;
*/
public class DatabaseVersion implements BatchComponent, ServerComponent {
- public static final int LAST_VERSION = 441;
+ public static final int LAST_VERSION = 442;
public static enum Status {
UP_TO_DATE, REQUIRES_UPGRADE, REQUIRES_DOWNGRADE, FRESH_INSTALL
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculator.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtCalculator.java
index 6c188e93584..fb540c66419 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculator.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtCalculator.java
@@ -17,7 +17,7 @@
* 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;
+package org.sonar.core.technicaldebt;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ArrayListMultimap;
@@ -26,14 +26,12 @@ 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.issue.Issue;
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;
@@ -57,6 +55,14 @@ public class TechnicalDebtCalculator implements BatchExtension {
this.functions = functions;
}
+ public Long cost(Issue issue) {
+ TechnicalDebtRequirement requirement = technicalDebtModel.getRequirementByRule(issue.ruleKey().repository(), issue.ruleKey().rule());
+ if (requirement != null) {
+ return functions.costInMinutes(requirement, issue);
+ }
+ return null;
+ }
+
public void compute(DecoratorContext context) {
reset();
@@ -109,7 +115,7 @@ public class TechnicalDebtCalculator implements BatchExtension {
private double computeRemediationCost(Metric metric, DecoratorContext context, TechnicalDebtRequirement requirement, Collection<Violation> violations) {
double cost = 0.0;
if (violations != null) {
- cost = functions.calculateCost(requirement, violations);
+ cost = functions.costInHours(requirement, violations);
}
for (Measure measure : context.getChildrenMeasures(MeasuresFilters.characteristic(metric, requirement.toCharacteristic()))) {
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModel.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModel.java
index fbcee9db691..bcc58262889 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModel.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModel.java
@@ -19,8 +19,6 @@
*/
package org.sonar.core.technicaldebt;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.BatchComponent;
@@ -30,10 +28,14 @@ import org.sonar.api.rules.Rule;
import org.sonar.api.utils.SonarException;
import org.sonar.api.utils.TimeProfiler;
+import javax.annotation.CheckForNull;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
+
public class TechnicalDebtModel implements BatchComponent {
private static final Logger LOGGER = LoggerFactory.getLogger(TechnicalDebtModel.class);
@@ -41,8 +43,8 @@ public class TechnicalDebtModel implements BatchComponent {
// FIXME Use the same as in RegisterTechnicalDebtModel
public static final String MODEL_NAME = "TECHNICAL_DEBT";
- private List<TechnicalDebtCharacteristic> characteristics = Lists.newArrayList();
- private Map<Rule, TechnicalDebtRequirement> requirementsByRule = Maps.newHashMap();
+ private List<TechnicalDebtCharacteristic> characteristics = newArrayList();
+ private Map<Rule, TechnicalDebtRequirement> requirementsByRule = newHashMap();
public TechnicalDebtModel(ModelFinder modelFinder) {
TimeProfiler profiler = new TimeProfiler(LOGGER).start("Loading technical debt model");
@@ -98,6 +100,7 @@ public class TechnicalDebtModel implements BatchComponent {
return requirementsByRule.values();
}
+ @CheckForNull
public TechnicalDebtRequirement getRequirementByRule(String repositoryKey, String key) {
return requirementsByRule.get(Rule.create().setUniqueKey(repositoryKey, key));
}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtRequirement.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtRequirement.java
index 93799c5878d..b497b78b9f0 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtRequirement.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtRequirement.java
@@ -19,6 +19,7 @@
*/
package org.sonar.core.technicaldebt;
+import org.sonar.api.qualitymodel.Characteristic;
import org.sonar.api.rules.Rule;
import org.sonar.core.technicaldebt.functions.LinearFunction;
import org.sonar.core.technicaldebt.functions.LinearWithOffsetFunction;
@@ -37,7 +38,7 @@ public class TechnicalDebtRequirement implements Characteristicable {
private WorkUnit factor;
private WorkUnit offset;
- public TechnicalDebtRequirement(org.sonar.api.qualitymodel.Characteristic requirement, TechnicalDebtCharacteristic parent) {
+ public TechnicalDebtRequirement(Characteristic requirement, TechnicalDebtCharacteristic parent) {
this.characteristic = requirement;
this.rule = requirement.getRule();
this.parent = parent;
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/WorkUnitConverter.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/WorkUnitConverter.java
index b83dc4cbfd7..8523b9a3ecb 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/WorkUnitConverter.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/WorkUnitConverter.java
@@ -55,4 +55,21 @@ public final class WorkUnitConverter implements BatchComponent {
}
return result;
}
+
+ public long toMinutes(WorkUnit factor) {
+ long result;
+ if (StringUtils.equals(WorkUnit.DAYS, factor.getUnit())) {
+ result = Double.valueOf(factor.getValue() * hoursInDay * 60d).longValue();
+
+ } else if (StringUtils.equals(WorkUnit.HOURS, factor.getUnit())) {
+ result = Double.valueOf(factor.getValue() * 60d).longValue();
+
+ } else if (StringUtils.equals(WorkUnit.MINUTES, factor.getUnit())) {
+ result = Double.valueOf(factor.getValue()).longValue();
+
+ } else {
+ throw new IllegalArgumentException("Unknown remediation factor unit: " + factor.getUnit());
+ }
+ return result;
+ }
}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/AbstractFunction.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/AbstractFunction.java
index dd2ea2d8dfe..c07a7c4b181 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/AbstractFunction.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/AbstractFunction.java
@@ -19,6 +19,7 @@
*/
package org.sonar.core.technicaldebt.functions;
+import org.sonar.api.issue.Issue;
import org.sonar.api.rules.Violation;
import org.sonar.core.technicaldebt.TechnicalDebtRequirement;
import org.sonar.core.technicaldebt.WorkUnitConverter;
@@ -39,6 +40,12 @@ public abstract class AbstractFunction implements Function {
public abstract String getKey();
- public abstract double calculateCost(TechnicalDebtRequirement requirement, Collection<Violation> violations);
+ public abstract double costInHours(TechnicalDebtRequirement requirement, Collection<Violation> violations);
+
+ public abstract long costInMinutes(TechnicalDebtRequirement requirement, Issue issue);
+
+ protected long factorInMinutes(TechnicalDebtRequirement requirement) {
+ return getConverter().toMinutes(requirement.getRemediationFactor());
+ }
}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/ConstantFunction.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/ConstantFunction.java
index 4c54129258d..b347c6838bd 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/ConstantFunction.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/ConstantFunction.java
@@ -19,6 +19,7 @@
*/
package org.sonar.core.technicaldebt.functions;
+import org.sonar.api.issue.Issue;
import org.sonar.api.rules.Violation;
import org.sonar.core.technicaldebt.TechnicalDebtRequirement;
import org.sonar.core.technicaldebt.WorkUnitConverter;
@@ -37,7 +38,7 @@ public final class ConstantFunction extends AbstractFunction {
return FUNCTION_CONSTANT_RESOURCE;
}
- public double calculateCost(TechnicalDebtRequirement requirement, Collection<Violation> violations) {
+ public double costInHours(TechnicalDebtRequirement requirement, Collection<Violation> violations) {
double cost = 0.0;
if (!violations.isEmpty()) {
cost = getConverter().toDays(requirement.getRemediationFactor());
@@ -45,4 +46,8 @@ public final class ConstantFunction extends AbstractFunction {
return cost;
}
+ public long costInMinutes(TechnicalDebtRequirement requirement, Issue issue) {
+ return factorInMinutes(requirement);
+ }
+
}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/Function.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/Function.java
index cb54e529e4e..65a37918499 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/Function.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/Function.java
@@ -20,6 +20,7 @@
package org.sonar.core.technicaldebt.functions;
import org.sonar.api.BatchComponent;
+import org.sonar.api.issue.Issue;
import org.sonar.api.rules.Violation;
import org.sonar.core.technicaldebt.TechnicalDebtRequirement;
@@ -29,6 +30,8 @@ public interface Function extends BatchComponent {
String getKey();
- double calculateCost(TechnicalDebtRequirement requirement, Collection<Violation> violations);
+ double costInHours(TechnicalDebtRequirement requirement, Collection<Violation> violations);
+
+ long costInMinutes(TechnicalDebtRequirement requirement, Issue issue);
}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/Functions.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/Functions.java
index 99263df6ef1..da43d06a508 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/Functions.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/Functions.java
@@ -21,6 +21,7 @@ package org.sonar.core.technicaldebt.functions;
import com.google.common.collect.Maps;
import org.sonar.api.BatchComponent;
+import org.sonar.api.issue.Issue;
import org.sonar.api.rules.Violation;
import org.sonar.core.technicaldebt.TechnicalDebtRequirement;
@@ -45,8 +46,12 @@ public class Functions implements BatchComponent {
return getFunction(requirement.getRemediationFunction());
}
- public double calculateCost(TechnicalDebtRequirement requirement, Collection<Violation> violations) {
- return getFunction(requirement).calculateCost(requirement, violations);
+ public double costInHours(TechnicalDebtRequirement requirement, Collection<Violation> violations) {
+ return getFunction(requirement).costInHours(requirement, violations);
+ }
+
+ public long costInMinutes(TechnicalDebtRequirement requirement, Issue issue) {
+ return getFunction(requirement).costInMinutes(requirement, issue);
}
}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearFunction.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearFunction.java
index 727168c8dd9..cac450e9509 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearFunction.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearFunction.java
@@ -19,6 +19,7 @@
*/
package org.sonar.core.technicaldebt.functions;
+import org.sonar.api.issue.Issue;
import org.sonar.api.rules.Violation;
import org.sonar.core.technicaldebt.TechnicalDebtRequirement;
import org.sonar.core.technicaldebt.WorkUnitConverter;
@@ -39,11 +40,16 @@ public class LinearFunction extends AbstractFunction {
return FUNCTION_LINEAR;
}
- public double calculateCost(TechnicalDebtRequirement requirement, Collection<Violation> violations) {
+ public double costInHours(TechnicalDebtRequirement requirement, Collection<Violation> violations) {
double points = 0.0;
for (Violation violation : violations) {
points += (violation.getCost() != null ? violation.getCost() : DEFAULT_VIOLATION_COST);
}
return points * getConverter().toDays(requirement.getRemediationFactor());
}
+
+ public long costInMinutes(TechnicalDebtRequirement requirement, Issue issue) {
+ double points = (issue.effortToFix() != null ? issue.effortToFix() : DEFAULT_VIOLATION_COST);
+ return Double.valueOf(points * factorInMinutes(requirement)).longValue();
+ }
}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearWithOffsetFunction.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearWithOffsetFunction.java
index 02b3f22d1e0..931ff24798e 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearWithOffsetFunction.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearWithOffsetFunction.java
@@ -37,12 +37,12 @@ public final class LinearWithOffsetFunction extends LinearFunction {
return FUNCTION_LINEAR_WITH_OFFSET;
}
- public double calculateCost(TechnicalDebtRequirement requirement, Collection<Violation> violations) {
+ public double costInHours(TechnicalDebtRequirement requirement, Collection<Violation> violations) {
if (violations.isEmpty()) {
return 0.0;
}
double minimunCost = getConverter().toDays(requirement.getOffset());
- return minimunCost + super.calculateCost(requirement, violations);
+ return minimunCost + super.costInHours(requirement, violations);
}
}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearWithThresholdFunction.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearWithThresholdFunction.java
index cac84ce45c7..9530b11843f 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearWithThresholdFunction.java
+++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/functions/LinearWithThresholdFunction.java
@@ -37,12 +37,12 @@ public final class LinearWithThresholdFunction extends LinearFunction {
return FUNCTION_LINEAR_WITH_THRESHOLD;
}
- public double calculateCost(TechnicalDebtRequirement requirement, Collection<Violation> violations) {
+ public double costInHours(TechnicalDebtRequirement requirement, Collection<Violation> violations) {
if (violations.isEmpty()) {
return 0.0;
}
double thresholdCost = getConverter().toDays(requirement.getOffset());
- double violationsCost = super.calculateCost(requirement, violations);
+ double violationsCost = super.costInHours(requirement, violations);
return violationsCost > thresholdCost ? violationsCost : thresholdCost;
}
diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml
index 2aca03357aa..80e9e375cb1 100644
--- a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml
+++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml
@@ -16,6 +16,7 @@
i.message as message,
i.line as line,
i.effort_to_fix as effortToFix,
+ i.remediation_cost as remediationCost,
i.status as status,
i.resolution as resolution,
i.checksum as checksum,
@@ -61,11 +62,11 @@
<insert id="insert" parameterType="Issue" useGeneratedKeys="false" keyProperty="id">
INSERT INTO issues (kee, component_id, root_component_id, rule_id, action_plan_key, severity, manual_severity,
- message, line, effort_to_fix, status,
+ message, line, effort_to_fix, remediation_cost, status,
resolution, checksum, reporter, assignee, author_login, issue_attributes, issue_creation_date, issue_update_date,
issue_close_date, created_at, updated_at)
VALUES (#{kee}, #{componentId}, #{rootComponentId}, #{ruleId}, #{actionPlanKey}, #{severity}, #{manualSeverity},
- #{message}, #{line}, #{effortToFix}, #{status},
+ #{message}, #{line}, #{effortToFix}, #{remediationCost}, #{status},
#{resolution}, #{checksum}, #{reporter}, #{assignee}, #{authorLogin}, #{issueAttributes}, #{issueCreationDate},
#{issueUpdateDate}, #{issueCloseDate}, #{createdAt}, #{updatedAt})
</insert>
@@ -81,6 +82,7 @@
message=#{message},
line=#{line},
effort_to_fix=#{effortToFix},
+ remediation_cost=#{remediationCost},
status=#{status},
resolution=#{resolution},
checksum=#{checksum},
@@ -106,6 +108,7 @@
message=#{message},
line=#{line},
effort_to_fix=#{effortToFix},
+ remediation_cost=#{remediationCost},
status=#{status},
resolution=#{resolution},
checksum=#{checksum},
@@ -145,6 +148,7 @@
i.message as message,
i.line as line,
i.effort_to_fix as effortToFix,
+ i.remediation_cost as remediationCost,
i.status as status,
i.resolution as resolution,
i.checksum as checksum,
diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
index 2ee8f0fd611..709c308c6d6 100644
--- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
+++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
@@ -179,6 +179,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('432');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('433');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('440');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('441');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('442');
INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '2011-09-26 22:27:48.0', '2011-09-26 22:27:48.0', null, null);
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
index 52355a26659..4f201802db7 100644
--- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
+++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
@@ -477,6 +477,7 @@ CREATE TABLE "ISSUES" (
"MESSAGE" VARCHAR(4000),
"LINE" INTEGER,
"EFFORT_TO_FIX" DOUBLE,
+ "REMEDIATION_COST" INTEGER,
"STATUS" VARCHAR(20),
"RESOLUTION" VARCHAR(20),
"CHECKSUM" VARCHAR(1000),
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueBuilderTest.java b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueBuilderTest.java
index 178161dae4a..e97d7cdd53d 100644
--- a/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueBuilderTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueBuilderTest.java
@@ -30,7 +30,7 @@ import static org.fest.assertions.Assertions.assertThat;
public class DefaultIssueBuilderTest {
@Test
- public void should_build_new_issue() throws Exception {
+ public void build_new_issue() throws Exception {
String componentKey = "org.apache.struts:struts-core:Action.java";
DefaultIssue issue = (DefaultIssue) new DefaultIssueBuilder()
.componentKey(componentKey)
@@ -62,7 +62,7 @@ public class DefaultIssueBuilderTest {
}
@Test
- public void should_not_set_default_severity() {
+ public void not_set_default_severity() {
DefaultIssue issue = (DefaultIssue) new DefaultIssueBuilder()
.componentKey("Action.java")
.ruleKey(RuleKey.of("squid", "NullDereference"))
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDtoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDtoTest.java
index 91ffce8461d..4add1b46497 100644
--- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDtoTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueDtoTest.java
@@ -49,7 +49,7 @@ public class IssueDtoTest {
}
@Test
- public void should_set_issue_fields() {
+ public void set_issue_fields() {
Date createdAt = DateUtils.addDays(new Date(), -5);
Date updatedAt = DateUtils.addDays(new Date(), -3);
Date closedAt = DateUtils.addDays(new Date(), -1);
@@ -65,6 +65,7 @@ public class IssueDtoTest {
.setStatus(Issue.STATUS_CLOSED)
.setResolution(Issue.RESOLUTION_FALSE_POSITIVE)
.setEffortToFix(15.0)
+ .setRemediationCost(500L)
.setLine(6)
.setSeverity("BLOCKER")
.setMessage("message")
@@ -85,6 +86,7 @@ public class IssueDtoTest {
assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED);
assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FALSE_POSITIVE);
assertThat(issue.effortToFix()).isEqualTo(15.0);
+ assertThat(issue.remediationCost()).isEqualTo(500L);
assertThat(issue.line()).isEqualTo(6);
assertThat(issue.severity()).isEqualTo("BLOCKER");
assertThat(issue.message()).isEqualTo("message");
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueMapperTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueMapperTest.java
index 199c198737b..3397cd4e4a2 100644
--- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueMapperTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueMapperTest.java
@@ -54,6 +54,7 @@ public class IssueMapperTest extends AbstractDaoTestCase {
dto.setKee("ABCDE");
dto.setLine(500);
dto.setEffortToFix(3.14);
+ dto.setRemediationCost(10L);
dto.setResolution("FIXED");
dto.setStatus("RESOLVED");
dto.setSeverity("BLOCKER");
@@ -88,6 +89,7 @@ public class IssueMapperTest extends AbstractDaoTestCase {
dto.setKee("ABCDE");
dto.setLine(500);
dto.setEffortToFix(3.14);
+ dto.setRemediationCost(10L);
dto.setResolution("FIXED");
dto.setStatus("RESOLVED");
dto.setSeverity("BLOCKER");
@@ -122,6 +124,7 @@ public class IssueMapperTest extends AbstractDaoTestCase {
dto.setKee("ABCDE");
dto.setLine(500);
dto.setEffortToFix(3.14);
+ dto.setRemediationCost(10L);
dto.setResolution("FIXED");
dto.setStatus("RESOLVED");
dto.setSeverity("BLOCKER");
@@ -159,6 +162,7 @@ public class IssueMapperTest extends AbstractDaoTestCase {
dto.setKee("ABCDE");
dto.setLine(500);
dto.setEffortToFix(3.14);
+ dto.setRemediationCost(10L);
dto.setResolution("FIXED");
dto.setStatus("RESOLVED");
dto.setSeverity("BLOCKER");
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculatorTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtCalculatorTest.java
index 7eea344975d..eed0940f08c 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtCalculatorTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtCalculatorTest.java
@@ -17,7 +17,7 @@
* 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;
+package org.sonar.core.technicaldebt;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
@@ -25,13 +25,13 @@ 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.issue.Issue;
+import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.measures.MeasuresFilter;
import org.sonar.api.qualitymodel.Characteristic;
+import org.sonar.api.rule.RuleKey;
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;
@@ -73,8 +73,8 @@ public class TechnicalDebtCalculatorTest {
List<Violation> violations = Lists.newArrayList(violation1, violation2, violation3, violation4);
- stub(technicalDebtModel.getRequirementByRule("repo1", "rule1")).toReturn(requirement1);
- stub(technicalDebtModel.getRequirementByRule("repo2", "rule2")).toReturn(requirement2);
+ when(technicalDebtModel.getRequirementByRule("repo1", "rule1")).thenReturn(requirement1);
+ when(technicalDebtModel.getRequirementByRule("repo2", "rule2")).thenReturn(requirement2);
DecoratorContext context = mock(DecoratorContext.class);
when(context.getViolations()).thenReturn(violations);
@@ -132,23 +132,43 @@ public class TechnicalDebtCalculatorTest {
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));
+ when(technicalDebtModel.getRequirementByRule("repo1", "rule1")).thenReturn(requirement1);
+ when(technicalDebtModel.getRequirementByRule("repo2", "rule2")).thenReturn(requirement2);
+ when(technicalDebtModel.getAllRequirements()).thenReturn(Lists.newArrayList(requirement1, requirement2));
- stub(functions.calculateCost(any(TechnicalDebtRequirement.class), any(Collection.class))).toReturn(1.0);
+ when(functions.costInHours(any(TechnicalDebtRequirement.class), any(Collection.class))).thenReturn(1.0);
DecoratorContext context = mock(DecoratorContext.class);
- stub(context.getViolations()).toReturn(violations);
- stub(context.getChildrenMeasures(any(MeasuresFilter.class))).toReturn(Collections.EMPTY_LIST);
+ when(context.getViolations()).thenReturn(violations);
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Collections.EMPTY_LIST);
remediationCostCalculator.compute(context);
-// assertThat(remediationCostCalculator.getTotal()).isEqualTo(2.0);
+ assertThat(remediationCostCalculator.getTotal()).isEqualTo(2.0);
assertThat(remediationCostCalculator.getRequirementCosts().get(requirement1)).isEqualTo(1.0);
assertThat(remediationCostCalculator.getRequirementCosts().get(requirement2)).isEqualTo(1.0);
}
+ @Test
+ public void cost_from_one_issue() throws Exception {
+ DefaultIssue issue = new DefaultIssue().setKey("ABCDE").setRuleKey(RuleKey.of("squid", "AvoidCycle"));
+ TechnicalDebtRequirement requirement = mock(TechnicalDebtRequirement.class);
+ stub(technicalDebtModel.getRequirementByRule("squid", "AvoidCycle")).toReturn(requirement);
+
+ when(functions.costInMinutes(eq(requirement), eq(issue))).thenReturn(10L);
+
+ assertThat(remediationCostCalculator.cost(issue)).isEqualTo(10L);
+ }
+
+ @Test
+ public void no_cost_from_one_issue_if_reauirement_not_found() throws Exception {
+ DefaultIssue issue = new DefaultIssue().setKey("ABCDE").setRuleKey(RuleKey.of("squid", "AvoidCycle"));
+ stub(technicalDebtModel.getRequirementByRule("squid", "AvoidCycle")).toReturn(null);
+
+ assertThat(remediationCostCalculator.cost(issue)).isNull();
+ verify(functions, never()).costInMinutes(any(TechnicalDebtRequirement.class), any(Issue.class));
+ }
+
private Violation buildViolation(String ruleKey, String repositoryKey, Date creationDate) {
Violation violation = mock(Violation.class);
stub(violation.getRule()).toReturn(Rule.create(repositoryKey, ruleKey));
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/WorkUnitConverterTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/WorkUnitConverterTest.java
index 9f5473dfa91..be225db71fc 100644
--- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/WorkUnitConverterTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/WorkUnitConverterTest.java
@@ -28,7 +28,7 @@ import static org.junit.Assert.assertThat;
public class WorkUnitConverterTest {
@Test
- public void concert_value_to_days() {
+ public void concert_to_days() {
Settings settings = new Settings();
settings.setProperty(WorkUnitConverter.PROPERTY_HOURS_IN_DAY, "12");
@@ -39,4 +39,16 @@ public class WorkUnitConverterTest {
assertThat(converter.toDays(WorkUnit.create(60.0, WorkUnit.MINUTES)), is(1.0 / 12.0));
}
+ @Test
+ public void concert_to_minutes() {
+ Settings settings = new Settings();
+ settings.setProperty(WorkUnitConverter.PROPERTY_HOURS_IN_DAY, "12");
+
+ WorkUnitConverter converter = new WorkUnitConverter(settings);
+
+ assertThat(converter.toMinutes(WorkUnit.create(2.0, WorkUnit.DAYS)), is(2 * 12 * 60L));
+ assertThat(converter.toMinutes(WorkUnit.create(6.0, WorkUnit.HOURS)), is(6 * 60L));
+ assertThat(converter.toMinutes(WorkUnit.create(60.0, WorkUnit.MINUTES)), is(60L));
+ }
+
}
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/ConstantFunctionTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/ConstantFunctionTest.java
index 2a46394aa21..9bd97a1a6a3 100644
--- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/ConstantFunctionTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/ConstantFunctionTest.java
@@ -26,6 +26,7 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.sonar.api.config.Settings;
+import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.Violation;
import org.sonar.core.technicaldebt.TechnicalDebtRequirement;
@@ -35,6 +36,9 @@ import org.sonar.core.technicaldebt.WorkUnitConverter;
import java.util.Collection;
import java.util.Collections;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
public class ConstantFunctionTest {
private TechnicalDebtRequirement requirement;
@@ -48,19 +52,26 @@ public class ConstantFunctionTest {
}
@Test
- public void zeroIfNoViolations() {
- Assert.assertThat(function.calculateCost(requirement, Collections.<Violation>emptyList()), Is.is(0.0));
+ public void zero_if_no_violations() {
+ Assert.assertThat(function.costInHours(requirement, Collections.<Violation>emptyList()), Is.is(0.0));
}
@Test
- public void countAsIfSingleViolation() {
+ public void count_as_if_single_violation() {
Collection<Violation> violations = Lists.newArrayList();
Rule rule = Rule.create("checkstyle", "foo", "Foo");
violations.add(new Violation(rule));
- Assert.assertThat(function.calculateCost(requirement, violations), Is.is(3.14));
+ Assert.assertThat(function.costInHours(requirement, violations), Is.is(3.14));
violations.add(new Violation(rule));
- Assert.assertThat(function.calculateCost(requirement, violations), Is.is(3.14));
+ Assert.assertThat(function.costInHours(requirement, violations), Is.is(3.14));
+ }
+
+ @Test
+ public void cost_in_minutes() {
+ when(requirement.getRemediationFactor()).thenReturn(WorkUnit.create(10d, WorkUnit.MINUTES));
+ DefaultIssue issue = new DefaultIssue().setKey("ABCDE");
+ assertThat(function.costInMinutes(requirement, issue)).isEqualTo(10L);
}
}
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/FunctionsTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/FunctionsTest.java
index ada1862aa87..e829e8e15e8 100644
--- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/FunctionsTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/FunctionsTest.java
@@ -26,7 +26,7 @@ import static org.fest.assertions.Assertions.assertThat;
public class FunctionsTest {
@Test
- public void registerFunctions() {
+ public void register_functions() {
Functions functions = new Functions(new Function[]{new LinearFunction(null), new LinearWithOffsetFunction(null),
new ConstantFunction(null)});
assertThat(functions.getFunction(LinearFunction.FUNCTION_LINEAR)).isInstanceOf(LinearFunction.class);
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearFunctionTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearFunctionTest.java
index 6f3e2cb9106..077ebe8f6aa 100644
--- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearFunctionTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearFunctionTest.java
@@ -20,11 +20,10 @@
package org.sonar.core.technicaldebt.functions;
import com.google.common.collect.Lists;
-import org.hamcrest.core.Is;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.config.Settings;
+import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.Violation;
import org.sonar.core.technicaldebt.TechnicalDebtRequirement;
@@ -34,45 +33,64 @@ import org.sonar.core.technicaldebt.WorkUnitConverter;
import java.util.Collection;
import java.util.Collections;
+import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class LinearFunctionTest {
private TechnicalDebtRequirement requirement;
- private Function function = new LinearFunction(new WorkUnitConverter(new Settings()));
+ private Function function;
@Before
public void before() {
+ Settings settings = new Settings();
+ settings.setProperty(WorkUnitConverter.PROPERTY_HOURS_IN_DAY, 8);
+ function = new LinearFunction(new WorkUnitConverter(settings));
+
requirement = mock(TechnicalDebtRequirement.class);
when(requirement.getRemediationFactor()).thenReturn(WorkUnit.createInDays(3.14));
}
@Test
- public void zeroIfNoViolations() {
- Assert.assertThat(function.calculateCost(requirement, Collections.<Violation>emptyList()), Is.is(0.0));
+ public void zero_if_no_violations() {
+ assertThat(function.costInHours(requirement, Collections.<Violation>emptyList())).isEqualTo(0.0);
}
@Test
- public void countEveryViolation() {
+ public void count_every_violation() {
Collection<Violation> violations = Lists.newArrayList();
Rule rule = Rule.create("checkstyle", "foo", "Foo");
violations.add(new Violation(rule));
- Assert.assertThat(function.calculateCost(requirement, violations), Is.is(3.14));
+ assertThat(function.costInHours(requirement, violations)).isEqualTo(3.14);
violations.add(new Violation(rule));
- Assert.assertThat(function.calculateCost(requirement, violations), Is.is(3.14 * 2));
+ assertThat(function.costInHours(requirement, violations)).isEqualTo(3.14 * 2);
}
@Test
- public void usePointsWhenAvailable() {
+ public void use_points_when_available() {
Collection<Violation> violations = Lists.newArrayList();
Rule rule = Rule.create("checkstyle", "foo", "Foo");
violations.add(new Violation(rule).setCost(20.5));
violations.add(new Violation(rule).setCost(3.8));
violations.add(new Violation(rule));
- Assert.assertThat(function.calculateCost(requirement, violations), Is.is(3.14 * (20.5 + 3.8 + 1)));
+ assertThat(function.costInHours(requirement, violations)).isEqualTo(3.14 * (20.5 + 3.8 + 1));
+ }
+
+ @Test
+ public void cost_in_minutes() {
+ when(requirement.getRemediationFactor()).thenReturn(WorkUnit.create(10d, WorkUnit.MINUTES));
+ DefaultIssue issue = new DefaultIssue().setKey("ABCDE").setEffortToFix(2.0);
+ assertThat(function.costInMinutes(requirement, issue)).isEqualTo(20L);
+ }
+
+ @Test
+ public void cost_in_minutes_use_default_cost_when_no_effort_to_fix_on_issue() {
+ when(requirement.getRemediationFactor()).thenReturn(WorkUnit.create(10d, WorkUnit.MINUTES));
+ DefaultIssue issue = new DefaultIssue().setKey("ABCDE").setEffortToFix(null);
+ assertThat(function.costInMinutes(requirement, issue)).isEqualTo(10L);
}
}
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearWithOffsetFunctionTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearWithOffsetFunctionTest.java
index 0bcaeca8043..b4aa70153a2 100644
--- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearWithOffsetFunctionTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearWithOffsetFunctionTest.java
@@ -49,7 +49,7 @@ public class LinearWithOffsetFunctionTest {
@Test
public void zeroIfNoViolations() {
- Assert.assertThat(function.calculateCost(requirement, Collections.<Violation>emptyList()), Is.is(0.0));
+ Assert.assertThat(function.costInHours(requirement, Collections.<Violation>emptyList()), Is.is(0.0));
}
@Test
@@ -58,9 +58,9 @@ public class LinearWithOffsetFunctionTest {
Rule rule = Rule.create("checkstyle", "foo", "Foo");
violations.add(new Violation(rule));
- Assert.assertThat(function.calculateCost(requirement, violations), Is.is(2.12 + 3.14));
+ Assert.assertThat(function.costInHours(requirement, violations), Is.is(2.12 + 3.14));
violations.add(new Violation(rule));
- Assert.assertThat(function.calculateCost(requirement, violations), Is.is(2.12 + 3.14 * 2));
+ Assert.assertThat(function.costInHours(requirement, violations), Is.is(2.12 + 3.14 * 2));
}
}
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearWithThresholdFunctionTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearWithThresholdFunctionTest.java
index 0248de5072f..7445fad7f92 100644
--- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearWithThresholdFunctionTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/functions/LinearWithThresholdFunctionTest.java
@@ -49,7 +49,7 @@ public class LinearWithThresholdFunctionTest {
@Test
public void zeroIfNoViolations() {
- Assert.assertThat(function.calculateCost(requirement, Collections.<Violation>emptyList()), Is.is(0.0));
+ Assert.assertThat(function.costInHours(requirement, Collections.<Violation>emptyList()), Is.is(0.0));
}
@Test
@@ -58,12 +58,12 @@ public class LinearWithThresholdFunctionTest {
Rule rule = Rule.create("checkstyle", "foo", "Foo");
violations.add(new Violation(rule));
- Assert.assertThat(function.calculateCost(requirement, violations), Is.is(5.0));
+ Assert.assertThat(function.costInHours(requirement, violations), Is.is(5.0));
violations.add(new Violation(rule));
- Assert.assertThat(function.calculateCost(requirement, violations), Is.is(5.0));
+ Assert.assertThat(function.costInHours(requirement, violations), Is.is(5.0));
violations.add(new Violation(rule));
- Assert.assertThat(function.calculateCost(requirement, violations), Is.is(6.0));
+ Assert.assertThat(function.costInHours(requirement, violations), Is.is(6.0));
}
}
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testInsert-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testInsert-result.xml
index 34e96a79ae9..4e0b18c0de6 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testInsert-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testInsert-result.xml
@@ -10,6 +10,7 @@
message="the message"
line="500"
effort_to_fix="3.14"
+ remediation_cost="10"
status="RESOLVED"
resolution="FIXED"
checksum="123456789"
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml
index 171c7a3b4b4..4dbc84fdd59 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml
@@ -10,6 +10,7 @@
message="the message"
line="500"
effort_to_fix="3.14"
+ remediation_cost="10"
status="RESOLVED"
resolution="FIXED"
checksum="123456789"
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate.xml
index a4df3d603dd..00a1b052c40 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate.xml
@@ -10,6 +10,7 @@
message="old"
line="[null]"
effort_to_fix="[null]"
+ remediation_cost="[null]"
status="OPEN"
resolution="[null]"
checksum="[null]"
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/updateBeforeSelectedDate_with_conflict-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/updateBeforeSelectedDate_with_conflict-result.xml
index 3bd12af6cf8..027e5f65c5b 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/updateBeforeSelectedDate_with_conflict-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/updateBeforeSelectedDate_with_conflict-result.xml
@@ -11,6 +11,7 @@
message="old"
line="[null]"
effort_to_fix="[null]"
+ remediation_cost="[null]"
status="OPEN"
resolution="[null]"
checksum="[null]"
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/updateBeforeSelectedDate_with_conflict.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/updateBeforeSelectedDate_with_conflict.xml
index d598d02536e..fb218f4ab13 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/updateBeforeSelectedDate_with_conflict.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/updateBeforeSelectedDate_with_conflict.xml
@@ -10,6 +10,7 @@
message="old"
line="[null]"
effort_to_fix="[null]"
+ remediation_cost="[null]"
status="OPEN"
resolution="[null]"
checksum="[null]"
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml
index 6ae5005cdf9..af3b4371b49 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml
@@ -4,6 +4,7 @@
author_login="[null]"
checksum="[null]"
effort_to_fix="[null]"
+ remediation_cost="[null]"
message="[null]"
line="5000"
component_id="100"
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates-result.xml
index 0564fd93ae4..9c2a8fcbee9 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates-result.xml
@@ -1,22 +1,3 @@
-<!--
- ~ 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.
- -->
<dataset>
<rules id="200" name="Avoid Cycles" plugin_rule_key="AvoidCycles"
plugin_config_key="[null]" plugin_name="squid"/>
@@ -34,6 +15,7 @@
author_login="[null]"
checksum="FFFFF"
effort_to_fix="[null]"
+ remediation_cost="[null]"
message="[null]"
line="444"
component_id="100"
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates.xml
index c37f8367898..a31dd9baa6e 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates.xml
@@ -1,22 +1,3 @@
-<!--
- ~ 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.
- -->
<dataset>
<rules id="200" name="Avoid Cycles" plugin_rule_key="AvoidCycles"
@@ -36,6 +17,7 @@
author_login="[null]"
checksum="FFFFF"
effort_to_fix="[null]"
+ remediation_cost="[null]"
message="[null]"
line="1"
component_id="100"
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues-result.xml
index d03b9cd7523..8d700c2f865 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues-result.xml
@@ -9,6 +9,7 @@
author_login="simon"
checksum="FFFFF"
effort_to_fix="[null]"
+ remediation_cost="[null]"
message="[null]"
line="5000"
component_id="100"
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues.xml
index 6d3ac5aa2d8..8f73107f0e9 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues.xml
@@ -9,6 +9,7 @@
author_login="simon"
checksum="FFFFF"
effort_to_fix="[null]"
+ remediation_cost="[null]"
message="[null]"
line="3000"
component_id="100"
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues-result.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues-result.xml
index 49fe2baa8ed..354b35fcf13 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues-result.xml
@@ -18,7 +18,7 @@
status="CLOSED"
issue_close_date="2010-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="1" kee="[null]" issue_key="ISSUE-1" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -28,7 +28,7 @@
status="CLOSED"
issue_close_date="2010-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="2" kee="[null]" issue_key="ISSUE-2" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
-->
@@ -40,7 +40,7 @@
status="OPEN"
issue_close_date="[null]"
resolution="[null]" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="3" kee="[null]" issue_key="ISSUE-3" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -51,7 +51,7 @@
status="OPEN"
issue_close_date="[null]"
resolution="[null]" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="4" kee="[null]" issue_key="ISSUE-4" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -62,7 +62,7 @@
status="CLOSED"
issue_close_date="2025-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="5" kee="[null]" issue_key="ISSUE-5" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
-->
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues.xml
index ba7573a6080..9ab34cc61fe 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues.xml
@@ -12,7 +12,7 @@
status="CLOSED"
issue_close_date="2010-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="1" kee="[null]" issue_key="ISSUE-1" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -22,7 +22,7 @@
status="CLOSED"
issue_close_date="2010-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="2" kee="[null]" issue_key="ISSUE-2" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -34,7 +34,7 @@
status="OPEN"
issue_close_date="[null]"
resolution="[null]" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="3" kee="[null]" issue_key="ISSUE-3" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -45,7 +45,7 @@
status="OPEN"
issue_close_date="[null]"
resolution="[null]" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="4" kee="[null]" issue_key="ISSUE-4" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -55,7 +55,7 @@
status="CLOSED"
issue_close_date="2025-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="5" kee="[null]" issue_key="ISSUE-5" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues-result.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues-result.xml
index ce95f4372ee..d931b3848fd 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues-result.xml
@@ -13,7 +13,7 @@
status="CLOSED"
issue_close_date="2010-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="1" kee="[null]" issue_key="ISSUE-1" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -23,7 +23,7 @@
status="CLOSED"
issue_close_date="2010-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="2" kee="[null]" issue_key="ISSUE-2" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
-->
@@ -35,7 +35,7 @@
status="OPEN"
issue_close_date="[null]"
resolution="[null]" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="3" kee="[null]" issue_key="ISSUE-3" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -46,7 +46,7 @@
status="OPEN"
issue_close_date="[null]"
resolution="[null]" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="4" kee="[null]" issue_key="ISSUE-4" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -56,7 +56,7 @@
status="CLOSED"
issue_close_date="2025-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="5" kee="[null]" issue_key="ISSUE-5" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues.xml
index a50ca73937f..27af8b156b0 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues.xml
@@ -12,7 +12,7 @@
status="CLOSED"
issue_close_date="2010-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="1" kee="[null]" issue_key="ISSUE-1" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -22,7 +22,7 @@
status="CLOSED"
issue_close_date="2010-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="2" kee="[null]" issue_key="ISSUE-2" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -34,7 +34,7 @@
status="OPEN"
issue_close_date="[null]"
resolution="[null]" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="3" kee="[null]" issue_key="ISSUE-3" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -45,7 +45,7 @@
status="OPEN"
issue_close_date="[null]"
resolution="[null]" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="4" kee="[null]" issue_key="ISSUE-4" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
@@ -55,7 +55,7 @@
status="CLOSED"
issue_close_date="2025-01-01"
resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]"
- message="[null]" action_plan_key="[null]" effort_to_fix="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
+ message="[null]" action_plan_key="[null]" effort_to_fix="[null]" remediation_cost="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]"
updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/>
<issue_changes id="5" kee="[null]" issue_key="ISSUE-5" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java
index 177e8eca2d4..847d1d65eca 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java
@@ -23,7 +23,6 @@ import com.google.common.collect.ImmutableList;
import org.sonar.api.rule.RuleKey;
import javax.annotation.CheckForNull;
-
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@@ -103,6 +102,12 @@ public interface Issue extends Serializable {
Double effortToFix();
/**
+ * Elapsed time in minutes to fix the issue
+ */
+ @CheckForNull
+ Long remediationCost();
+
+ /**
* See constant values in {@link Issue}.
*/
String status();
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java
index 2214acfe5f5..a195ab4ea80 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java
@@ -37,13 +37,8 @@ import org.sonar.api.rule.Severity;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-
import java.io.Serializable;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
/**
* PLUGINS MUST NOT BE USED THIS CLASS, EXCEPT FOR UNIT TESTING.
@@ -61,6 +56,7 @@ public class DefaultIssue implements Issue {
private String message;
private Integer line;
private Double effortToFix;
+ private Long remediationCost;
private String status;
private String resolution;
private String reporter;
@@ -190,6 +186,16 @@ public class DefaultIssue implements Issue {
return this;
}
+ @CheckForNull
+ public Long remediationCost() {
+ return remediationCost;
+ }
+
+ public DefaultIssue setRemediationCost(@Nullable Long r) {
+ this.remediationCost = r;
+ return this;
+ }
+
public String status() {
return status;
}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java
index 40646371131..32f164eb4e7 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java
@@ -48,6 +48,7 @@ public class DefaultIssueTest {
.setMessage("a message")
.setLine(7)
.setEffortToFix(1.2d)
+ .setRemediationCost(1L)
.setActionPlanKey("BCDE")
.setStatus(Issue.STATUS_CLOSED)
.setResolution(Issue.RESOLUTION_FIXED)
@@ -75,6 +76,7 @@ public class DefaultIssueTest {
assertThat(issue.message()).isEqualTo("a message");
assertThat(issue.line()).isEqualTo(7);
assertThat(issue.effortToFix()).isEqualTo(1.2d);
+ assertThat(issue.remediationCost()).isEqualTo(1L);
assertThat(issue.actionPlanKey()).isEqualTo("BCDE");
assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED);
assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FIXED);
@@ -94,7 +96,7 @@ public class DefaultIssueTest {
}
@Test
- public void should_set_empty_dates() throws Exception {
+ public void set_empty_dates() throws Exception {
issue
.setCreationDate(null)
.setUpdateDate(null)
@@ -134,7 +136,7 @@ public class DefaultIssueTest {
}
@Test
- public void should_fail_on_empty_status() {
+ public void fail_on_empty_status() {
try {
issue.setStatus("");
fail();
@@ -144,7 +146,7 @@ public class DefaultIssueTest {
}
@Test
- public void should_fail_on_bad_severity() {
+ public void fail_on_bad_severity() {
try {
issue.setSeverity("FOO");
fail();
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb
index 49436c1582e..4b908bcc8e9 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb
@@ -32,6 +32,7 @@ class Issue
hash[:message] = issue.message if issue.message
hash[:line] = issue.line.to_i if issue.line
hash[:effortToFix] = issue.effortToFix.to_f if issue.effortToFix
+ hash[:remediationCost] = issue.remediationCost.to_i if issue.remediationCost
hash[:reporter] = issue.reporter if issue.reporter
hash[:assignee] = issue.assignee if issue.assignee
hash[:author] = issue.authorLogin if issue.authorLogin
@@ -55,4 +56,4 @@ class Issue
}
end
-end \ No newline at end of file
+end
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/442_add_remediation_cost_to_issue.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/442_add_remediation_cost_to_issue.rb
new file mode 100644
index 00000000000..2a5825554f4
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/442_add_remediation_cost_to_issue.rb
@@ -0,0 +1,32 @@
+#
+# Sonar, entreprise quality control 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.
+#
+
+#
+# Sonar 4.0
+# SONAR-4716
+#
+
+class AddRemediationCostToIssue < ActiveRecord::Migration
+
+ def self.up
+ add_column 'issues', 'remediation_cost', :integer, :null => true
+ end
+
+end
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issue.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issue.java
index 93aec19c148..c88a9a228e0 100644
--- a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issue.java
+++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/Issue.java
@@ -20,7 +20,6 @@
package org.sonar.wsclient.issue;
import javax.annotation.CheckForNull;
-
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -52,6 +51,9 @@ public interface Issue {
@CheckForNull
Double effortToFix();
+ @CheckForNull
+ Long remediationCost();
+
String status();
/**
diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssue.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssue.java
index 36f25bc8d30..2f83632ab17 100644
--- a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssue.java
+++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/internal/DefaultIssue.java
@@ -75,6 +75,11 @@ public class DefaultIssue implements Issue {
return JsonUtils.getDouble(json, "effortToFix");
}
+ @CheckForNull
+ public Long remediationCost() {
+ return JsonUtils.getLong(json, "remediationCost");
+ }
+
public String status() {
return JsonUtils.getString(json, "status");
}
diff --git a/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/IssueJsonParserTest.java b/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/IssueJsonParserTest.java
index 725405444ab..4e95e9dcbe0 100644
--- a/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/IssueJsonParserTest.java
+++ b/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/IssueJsonParserTest.java
@@ -27,7 +27,6 @@ import org.sonar.wsclient.issue.*;
import org.sonar.wsclient.user.User;
import java.util.List;
-import java.util.TimeZone;
import static org.fest.assertions.Assertions.assertThat;
@@ -52,6 +51,7 @@ public class IssueJsonParserTest {
assertThat(first.assignee()).isEqualTo("karadoc");
assertThat(first.message()).isEqualTo("the message");
assertThat(first.effortToFix()).isEqualTo(4.2);
+ assertThat(first.remediationCost()).isEqualTo(10L);
assertThat(first.reporter()).isEqualTo("perceval");
assertThat(first.author()).isEqualTo("pirlouis");
assertThat(first.actionPlan()).isEqualTo("9450b10c-e725-48b8-bf01-acdec751c491");
@@ -67,6 +67,7 @@ public class IssueJsonParserTest {
assertThat(second.key()).isEqualTo("FGHIJ");
assertThat(second.line()).isNull();
assertThat(second.effortToFix()).isNull();
+ assertThat(second.remediationCost()).isNull();
assertThat(second.reporter()).isNull();
assertThat(second.author()).isNull();
assertThat(second.attribute("JIRA")).isNull();
diff --git a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/search.json b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/search.json
index 8c8505f5d73..e1f131c2f96 100644
--- a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/search.json
+++ b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/search.json
@@ -11,6 +11,7 @@
"status": "OPEN",
"assignee": "karadoc",
"effortToFix": 4.2,
+ "remediationCost": 10,
"message": "the message",
"title": "the title",
"reporter": "perceval",