@@ -50,7 +50,7 @@ public class XooRulesDefinition implements RulesDefinition { | |||
.setHtmlDescription("Generate an issue on each line of a file. It requires the metric \"lines\"."); | |||
oneIssuePerLine | |||
.setDebtRemediationFunction(hasTag.debtRemediationFunctions().linear("1min")) | |||
.setEffortToFixDescription("It takes about 1 minute to an experienced software craftsman to remove a line of code"); | |||
.setGapDescription("It takes about 1 minute to an experienced software craftsman to remove a line of code"); | |||
repo.done(); | |||
} | |||
@@ -80,7 +80,7 @@ public class XooRulesDefinition implements RulesDefinition { | |||
.setHtmlDescription("Generate an issue on each line of a file. It requires the metric \"lines\"."); | |||
oneIssuePerLine | |||
.setDebtRemediationFunction(hasTag.debtRemediationFunctions().linear("1min")) | |||
.setEffortToFixDescription("It takes about 1 minute to an experienced software craftsman to remove a line of code"); | |||
.setGapDescription("It takes about 1 minute to an experienced software craftsman to remove a line of code"); | |||
repo.createRule(OneIssueOnDirPerFileSensor.RULE_KEY).setName("One Issue On Dir Per File") | |||
.setHtmlDescription("Generate issues on directories"); | |||
@@ -97,7 +97,7 @@ public class XooRulesDefinition implements RulesDefinition { | |||
.setHtmlDescription("Generate an issue on each module"); | |||
oneIssuePerModule | |||
.setDebtRemediationFunction(hasTag.debtRemediationFunctions().linearWithOffset("25min", "1h")) | |||
.setEffortToFixDescription("A certified architect will need roughly half an hour to start working on removal of modules, " + | |||
.setGapDescription("A certified architect will need roughly half an hour to start working on removal of modules, " + | |||
"then it's about one hour per module."); | |||
repo.createRule(OneBlockerIssuePerFileSensor.RULE_KEY).setName("One Blocker Issue Per File") |
@@ -47,9 +47,9 @@ public class XooRulesDefinitionTest { | |||
RulesDefinition.Rule rule = repo.rule(OneIssuePerLineSensor.RULE_KEY); | |||
assertThat(rule.name()).isNotEmpty(); | |||
assertThat(rule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR); | |||
assertThat(rule.debtRemediationFunction().coefficient()).isEqualTo("1min"); | |||
assertThat(rule.debtRemediationFunction().offset()).isNull(); | |||
assertThat(rule.effortToFixDescription()).isNotEmpty(); | |||
assertThat(rule.debtRemediationFunction().gapMultiplier()).isEqualTo("1min"); | |||
assertThat(rule.debtRemediationFunction().baseEffort()).isNull(); | |||
assertThat(rule.gapDescription()).isNotEmpty(); | |||
} | |||
@Test |
@@ -46,14 +46,16 @@ public class DebtCalculator { | |||
verifyEffortToFix(issue, fn); | |||
Duration debt = Duration.create(0); | |||
if (fn.type().usesCoefficient() && !Strings.isNullOrEmpty(fn.coefficient())) { | |||
String gapMultiplier =fn.gapMultiplier(); | |||
if (fn.type().usesGapMultiplier() && !Strings.isNullOrEmpty(gapMultiplier)) { | |||
int effortToFixValue = Objects.firstNonNull(issue.effortToFix(), 1).intValue(); | |||
// TODO convert to Duration directly in Rule#remediationFunction -> better performance + error handling | |||
debt = durations.decode(fn.coefficient()).multiply(effortToFixValue); | |||
debt = durations.decode(gapMultiplier).multiply(effortToFixValue); | |||
} | |||
if (fn.type().usesOffset() && !Strings.isNullOrEmpty(fn.offset())) { | |||
String baseEffort= fn.baseEffort(); | |||
if (fn.type().usesBaseEffort() && !Strings.isNullOrEmpty(baseEffort)) { | |||
// TODO convert to Duration directly in Rule#remediationFunction -> better performance + error handling | |||
debt = debt.add(durations.decode(fn.offset())); | |||
debt = debt.add(durations.decode(baseEffort)); | |||
} | |||
return debt; | |||
} |
@@ -150,8 +150,8 @@ public class DebtModelBackup { | |||
boolean hasDebtDefinition = remediationFunction != null; | |||
rule.setDefaultRemediationFunction(hasDebtDefinition ? remediationFunction.type().name() : null); | |||
rule.setDefaultRemediationGapMultiplier(hasDebtDefinition ? remediationFunction.coefficient() : null); | |||
rule.setDefaultRemediationBaseEffort(hasDebtDefinition ? remediationFunction.offset() : null); | |||
rule.setDefaultRemediationGapMultiplier(hasDebtDefinition ? remediationFunction.gapMultiplier() : null); | |||
rule.setDefaultRemediationBaseEffort(hasDebtDefinition ? remediationFunction.baseEffort() : null); | |||
} | |||
// Reset overridden debt definitions |
@@ -63,7 +63,7 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions { | |||
.setHtmlDescription("An issue is created on a file as soon as the branch coverage on this file is less than the required threshold." | |||
+ "It gives the number of branches to be covered in order to reach the required threshold.") | |||
.setDebtRemediationFunction(rule.debtRemediationFunctions().linear("5min")) | |||
.setEffortToFixDescription("number of uncovered conditions") | |||
.setGapDescription("number of uncovered conditions") | |||
.setSeverity(Severity.MAJOR); | |||
rule.createParam(CommonRuleKeys.INSUFFICIENT_BRANCH_COVERAGE_PROPERTY) | |||
.setName("The minimum required branch coverage ratio") | |||
@@ -78,7 +78,7 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions { | |||
.setHtmlDescription("An issue is created on a file as soon as the line coverage on this file is less than the required threshold. " + | |||
"It gives the number of lines to be covered in order to reach the required threshold.") | |||
.setDebtRemediationFunction(rule.debtRemediationFunctions().linear("2min")) | |||
.setEffortToFixDescription("number of lines under the coverage threshold") | |||
.setGapDescription("number of lines under the coverage threshold") | |||
.setSeverity(Severity.MAJOR); | |||
rule.createParam(CommonRuleKeys.INSUFFICIENT_LINE_COVERAGE_PROPERTY) | |||
.setName("The minimum required line coverage ratio") | |||
@@ -93,7 +93,7 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions { | |||
.setHtmlDescription("An issue is created on a file as soon as the density of comment lines on this file is less than the required threshold. " + | |||
"The number of comment lines to be written in order to reach the required threshold is provided by each issue message.") | |||
.setDebtRemediationFunction(rule.debtRemediationFunctions().linear("2min")) | |||
.setEffortToFixDescription("number of lines required to meet minimum density") | |||
.setGapDescription("number of lines required to meet minimum density") | |||
.setSeverity(Severity.MAJOR); | |||
rule.createParam(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY) | |||
.setName("The minimum required comment density") | |||
@@ -107,7 +107,7 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions { | |||
.addTags("pitfall") | |||
.setHtmlDescription("An issue is created on a file as soon as there is at least one block of duplicated code on this file") | |||
.setDebtRemediationFunction(rule.debtRemediationFunctions().linearWithOffset("10min", "10min")) | |||
.setEffortToFixDescription("number of duplicate blocks") | |||
.setGapDescription("number of duplicate blocks") | |||
.setSeverity(Severity.MAJOR); | |||
} | |||
@@ -119,7 +119,7 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions { | |||
.setHtmlDescription( | |||
"Test failures or errors generally indicate that regressions have been introduced. Those tests should be handled as soon as possible to reduce the cost to fix the corresponding regressions.") | |||
.setDebtRemediationFunction(rule.debtRemediationFunctions().linear("10min")) | |||
.setEffortToFixDescription("number of failed tests") | |||
.setGapDescription("number of failed tests") | |||
.setSeverity(Severity.MAJOR); | |||
} | |||
@@ -129,7 +129,7 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions { | |||
.addTags("pitfall") | |||
.setHtmlDescription("Skipped unit tests are considered as dead code. Either they should be activated again (and updated) or they should be removed.") | |||
.setDebtRemediationFunction(rule.debtRemediationFunctions().linear("10min")) | |||
.setEffortToFixDescription("number of skipped tests") | |||
.setGapDescription("number of skipped tests") | |||
.setSeverity(Severity.MAJOR); | |||
} | |||
} |
@@ -176,7 +176,7 @@ public class RegisterRules implements Startable { | |||
.setName(ruleDef.name()) | |||
.setSeverity(ruleDef.severity()) | |||
.setStatus(ruleDef.status()) | |||
.setGapDescription(ruleDef.effortToFixDescription()) | |||
.setGapDescription(ruleDef.gapDescription()) | |||
.setSystemTags(ruleDef.tags()) | |||
.setType(RuleType.valueOf(ruleDef.type().name())) | |||
.setCreatedAt(system2.now()) | |||
@@ -257,9 +257,9 @@ public class RegisterRules implements Startable { | |||
if (hasDebt) { | |||
return mergeDebtDefinitions(dto, | |||
debtRemediationFunction.type().name(), | |||
debtRemediationFunction.coefficient(), | |||
debtRemediationFunction.offset(), | |||
def.effortToFixDescription()); | |||
debtRemediationFunction.gapMultiplier(), | |||
debtRemediationFunction.baseEffort(), | |||
def.gapDescription()); | |||
} | |||
return mergeDebtDefinitions(dto, null, null, null, null); | |||
} |
@@ -86,8 +86,8 @@ public class RuleOperations { | |||
} else if (!isRuleDebtSameAsOverriddenValues(ruleDto, newFunction, newCoefficient, newOffset)) { | |||
DefaultDebtRemediationFunction debtRemediationFunction = new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.valueOf(newFunction), newCoefficient, newOffset); | |||
ruleDto.setRemediationFunction(debtRemediationFunction.type().name()); | |||
ruleDto.setRemediationGapMultiplier(debtRemediationFunction.coefficient()); | |||
ruleDto.setRemediationBaseEffort(debtRemediationFunction.offset()); | |||
ruleDto.setRemediationGapMultiplier(debtRemediationFunction.gapMultiplier()); | |||
ruleDto.setRemediationBaseEffort(debtRemediationFunction.baseEffort()); | |||
needUpdate = true; | |||
} | |||
@@ -114,7 +114,8 @@ public class RuleOperations { | |||
private static boolean isRuleDebtSameAsOverriddenValues(RuleDto ruleDto, @Nullable String newFunction, | |||
@Nullable String newCoefficient, @Nullable String newOffset) { | |||
return isSameRemediationFunction(newFunction, newCoefficient, newOffset, ruleDto.getRemediationFunction(), ruleDto.getRemediationGapMultiplier(), ruleDto.getRemediationBaseEffort()); | |||
return isSameRemediationFunction(newFunction, newCoefficient, newOffset, ruleDto.getRemediationFunction(), ruleDto.getRemediationGapMultiplier(), | |||
ruleDto.getRemediationBaseEffort()); | |||
} | |||
private static boolean isSameRemediationFunction(@Nullable String newFunction, @Nullable String newCoefficient, @Nullable String newOffset, |
@@ -196,8 +196,8 @@ public class RuleUpdater { | |||
context.rule.setRemediationBaseEffort(null); | |||
} else { | |||
context.rule.setRemediationFunction(function.type().name()); | |||
context.rule.setRemediationGapMultiplier(function.coefficient()); | |||
context.rule.setRemediationBaseEffort(function.offset()); | |||
context.rule.setRemediationGapMultiplier(function.gapMultiplier()); | |||
context.rule.setRemediationBaseEffort(function.baseEffort()); | |||
} | |||
} | |||
} | |||
@@ -220,8 +220,8 @@ public class RuleUpdater { | |||
private static boolean isSameAsDefaultFunction(DebtRemediationFunction fn, RuleDto rule) { | |||
return new EqualsBuilder() | |||
.append(fn.type().name(), rule.getDefaultRemediationFunction()) | |||
.append(fn.coefficient(), rule.getDefaultRemediationGapMultiplier()) | |||
.append(fn.offset(), rule.getDefaultRemediationBaseEffort()) | |||
.append(fn.gapMultiplier(), rule.getDefaultRemediationGapMultiplier()) | |||
.append(fn.baseEffort(), rule.getDefaultRemediationBaseEffort()) | |||
.isEquals(); | |||
} | |||
@@ -23,11 +23,14 @@ package org.sonar.server.rule.ws; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.FluentIterable; | |||
import java.util.List; | |||
import java.util.Locale; | |||
import java.util.Set; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nonnull; | |||
import org.sonar.api.resources.Language; | |||
import org.sonar.api.resources.Languages; | |||
import org.sonar.api.server.debt.DebtRemediationFunction; | |||
import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; | |||
import org.sonar.db.rule.RuleDto; | |||
import org.sonar.db.rule.RuleParamDto; | |||
import org.sonar.markdown.Markdown; | |||
@@ -130,13 +133,13 @@ public class RuleMapper { | |||
if (shouldReturnField(fieldsToReturn, FIELD_DEFAULT_DEBT_REM_FUNCTION)) { | |||
DebtRemediationFunction defaultDebtRemediationFunction = defaultDebtRemediationFunction(ruleDto); | |||
if (defaultDebtRemediationFunction != null) { | |||
String gapMultiplier = defaultDebtRemediationFunction.coefficient(); | |||
String gapMultiplier = defaultDebtRemediationFunction.gapMultiplier(); | |||
if (gapMultiplier != null) { | |||
ruleResponse.setDefaultRemFnGapMultiplier(gapMultiplier); | |||
// Set deprecated field | |||
ruleResponse.setDefaultDebtRemFnCoeff(gapMultiplier); | |||
} | |||
String baseEffort = defaultDebtRemediationFunction.offset(); | |||
String baseEffort = defaultDebtRemediationFunction.baseEffort(); | |||
if (baseEffort != null) { | |||
ruleResponse.setDefaultRemFnBaseEffort(baseEffort); | |||
// Set deprecated field | |||
@@ -160,13 +163,13 @@ public class RuleMapper { | |||
// Set deprecated field | |||
ruleResponse.setDebtRemFnType(debtRemediationFunction.type().name()); | |||
} | |||
String gapMultiplier = debtRemediationFunction.coefficient(); | |||
String gapMultiplier = debtRemediationFunction.gapMultiplier(); | |||
if (gapMultiplier != null) { | |||
ruleResponse.setRemFnGapMultiplier(gapMultiplier); | |||
// Set deprecated field | |||
ruleResponse.setDebtRemFnCoeff(gapMultiplier); | |||
} | |||
String baseEffort = debtRemediationFunction.offset(); | |||
String baseEffort = debtRemediationFunction.baseEffort(); | |||
if (baseEffort != null) { | |||
ruleResponse.setRemFnBaseEffort(baseEffort); | |||
// Set deprecated field | |||
@@ -298,51 +301,29 @@ public class RuleMapper { | |||
return rule.getRemediationFunction() != null; | |||
} | |||
@CheckForNull | |||
private static DebtRemediationFunction defaultDebtRemediationFunction(final RuleDto ruleDto) { | |||
final String function = ruleDto.getDefaultRemediationFunction(); | |||
if (function == null || function.isEmpty()) { | |||
return null; | |||
} else { | |||
return new DebtRemediationFunction() { | |||
@Override | |||
public Type type() { | |||
return Type.valueOf(function.toUpperCase()); | |||
} | |||
@Override | |||
public String coefficient() { | |||
return ruleDto.getDefaultRemediationGapMultiplier(); | |||
} | |||
@Override | |||
public String offset() { | |||
return ruleDto.getDefaultRemediationBaseEffort(); | |||
} | |||
}; | |||
return new DefaultDebtRemediationFunction( | |||
DebtRemediationFunction.Type.valueOf(function.toUpperCase(Locale.ENGLISH)), | |||
ruleDto.getDefaultRemediationGapMultiplier(), | |||
ruleDto.getDefaultRemediationBaseEffort()); | |||
} | |||
} | |||
@CheckForNull | |||
private static DebtRemediationFunction debtRemediationFunction(final RuleDto ruleDto) { | |||
final String function = ruleDto.getRemediationFunction(); | |||
if (function == null || function.isEmpty()) { | |||
return defaultDebtRemediationFunction(ruleDto); | |||
} else { | |||
return new DebtRemediationFunction() { | |||
@Override | |||
public Type type() { | |||
return Type.valueOf(function.toUpperCase()); | |||
} | |||
@Override | |||
public String coefficient() { | |||
return ruleDto.getRemediationGapMultiplier(); | |||
} | |||
@Override | |||
public String offset() { | |||
return ruleDto.getRemediationBaseEffort(); | |||
} | |||
}; | |||
return new DefaultDebtRemediationFunction( | |||
DebtRemediationFunction.Type.valueOf(function.toUpperCase(Locale.ENGLISH)), | |||
ruleDto.getRemediationGapMultiplier(), | |||
ruleDto.getRemediationBaseEffort()); | |||
} | |||
} | |||
@@ -19,6 +19,9 @@ | |||
*/ | |||
package org.sonar.server.rule; | |||
import java.io.Reader; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import org.junit.Test; | |||
import org.junit.runner.RunWith; | |||
import org.mockito.Mock; | |||
@@ -37,10 +40,6 @@ import org.sonar.server.debt.DebtModelPluginRepository; | |||
import org.sonar.server.debt.DebtModelXMLExporter; | |||
import org.sonar.server.debt.DebtRulesXMLImporter; | |||
import java.io.Reader; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.fail; | |||
@@ -181,8 +180,8 @@ public class DeprecatedRulesDefinitionLoaderTest { | |||
assertThat(rule).isNotNull(); | |||
assertThat(rule.key()).isEqualTo("ConstantName"); | |||
assertThat(rule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET); | |||
assertThat(rule.debtRemediationFunction().coefficient()).isEqualTo("1d"); | |||
assertThat(rule.debtRemediationFunction().offset()).isEqualTo("10min"); | |||
assertThat(rule.debtRemediationFunction().gapMultiplier()).isEqualTo("1d"); | |||
assertThat(rule.debtRemediationFunction().baseEffort()).isEqualTo("10min"); | |||
} | |||
@Test |
@@ -287,7 +287,7 @@ public class RegisterRulesMediumTest { | |||
.setInternalKey("new_internal"); | |||
rule | |||
.setDebtRemediationFunction(rule.debtRemediationFunctions().linearWithOffset("1h", "30min")) | |||
.setEffortToFixDescription("Effort"); | |||
.setGapDescription("Effort"); | |||
} | |||
}); | |||
@@ -365,7 +365,7 @@ public class RegisterRulesTest { | |||
.setTags("tag1", "tag2", "tag3") | |||
.setType(RuleType.CODE_SMELL) | |||
.setStatus(RuleStatus.BETA) | |||
.setEffortToFixDescription("squid.S115.effortToFix"); | |||
.setGapDescription("squid.S115.effortToFix"); | |||
rule1.setDebtRemediationFunction(rule1.debtRemediationFunctions().linearWithOffset("5d", "10h")); | |||
rule1.createParam("param1").setDescription("parameter one").setDefaultValue("default1"); | |||
@@ -396,7 +396,7 @@ public class RegisterRulesTest { | |||
.setTags("tag1", "tag4") | |||
.setType(RuleType.BUG) | |||
.setStatus(RuleStatus.READY) | |||
.setEffortToFixDescription("squid.S115.effortToFix.v2"); | |||
.setGapDescription("squid.S115.effortToFix.v2"); | |||
rule1.setDebtRemediationFunction(rule1.debtRemediationFunctions().linearWithOffset("6d", "2h")); | |||
rule1.createParam("param1").setDescription("parameter one v2").setDefaultValue("default1 v2"); | |||
rule1.createParam("param2").setDescription("parameter two v2").setDefaultValue("default2 v2"); |
@@ -208,7 +208,7 @@ public class RuleOperationsTest { | |||
authorizedUserSession | |||
); | |||
} catch (Exception e) { | |||
assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("Invalid coefficient: foo (Duration 'foo' is invalid, it should use the following sample format : 2d 10h 15min)"); | |||
assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("Invalid gap multiplier: foo (Duration 'foo' is invalid, it should use the following sample format : 2d 10h 15min)"); | |||
} | |||
verify(ruleDao, never()).update(eq(session), any(RuleDto.class)); |
@@ -23,7 +23,7 @@ import javax.annotation.CheckForNull; | |||
/** | |||
* Function used to calculate the remediation cost of an issue. See {@link Type} for details. | |||
* <p>The coefficient and offset involved in the functions are durations. They are defined in hours, minutes and/or | |||
* <p>The gap multiplier and base effort involved in the functions are durations. They are defined in hours, minutes and/or | |||
* seconds. Examples: "5min", "1h 10min". Supported units are "d" (days), "h" (hour), and "min" (minutes).</p> | |||
* | |||
* @since 4.3 | |||
@@ -35,23 +35,23 @@ public interface DebtRemediationFunction { | |||
/** | |||
* The cost to fix an issue of this type depends on the magnitude of the issue. | |||
* For instance, an issue related to file size might be linear, with the total cost-to-fix incrementing | |||
* (by the coefficient amount) for each line of code above the allowed threshold. | |||
* The rule must provide the "effort to fix" value when raising an issue. | |||
* (by the gap multiplier amount) for each line of code above the allowed threshold. | |||
* The rule must provide the "gap" value when raising an issue. | |||
*/ | |||
LINEAR(true, false), | |||
/** | |||
* It takes a certain amount of time to deal with an issue of this type (this is the offset). | |||
* It takes a certain amount of time to deal with an issue of this type (this is the gap multiplier). | |||
* Then, the magnitude of the issue comes in to play. For instance, an issue related to complexity might be linear with offset. | |||
* So the total cost to fix is the time to make the basic analysis (the offset) plus the time required to deal | |||
* So the total cost to fix is the time to make the basic analysis (the base effort) plus the time required to deal | |||
* with each complexity point above the allowed value. | |||
* <p> | |||
* <code>Total remediation cost = offset + (number of noncompliance x coefficient)</code> | |||
* <code>Total remediation cost = base effort + (number of noncompliance x gap multiplier)</code> | |||
* </p> | |||
* <p>The rule must provide the "effort to fix" value when raising an issue. Let’s take as a example the “Paragraphs should not be too complex” rule. | |||
* <p>The rule must provide the "gap" value when raising an issue. Let’s take as a example the “Paragraphs should not be too complex” rule. | |||
* If you set the rule threshold to 20, and you have a paragraph with a complexity of 27, you have 7 points of complexity | |||
* to remove. Internally, this is called the Effort to Fix. In that case, if you use the LINEAR_OFFSET configuration | |||
* with an offset of 4h and a remediation cost of 1mn, the technical debt for this issue related to a | |||
* to remove. Internally, this is called the Gap. In that case, if you use the LINEAR_OFFSET configuration | |||
* with an base effort of 4h and a remediation cost of 1mn, the effort for this issue related to a | |||
* too-complex block of code will be: (7 complexity points x 1min) + 4h = 4h and 7mn | |||
* </p> | |||
*/ | |||
@@ -63,35 +63,79 @@ public interface DebtRemediationFunction { | |||
*/ | |||
CONSTANT_ISSUE(false, true); | |||
private final boolean usesCoefficient; | |||
private final boolean usesOffset; | |||
private final boolean usesGapMultiplier; | |||
private final boolean usesBaseEffort; | |||
Type(boolean usesCoefficient, boolean usesOffset) { | |||
this.usesCoefficient = usesCoefficient; | |||
this.usesOffset = usesOffset; | |||
Type(boolean usesGapMultiplier, boolean usesBaseEffort) { | |||
this.usesGapMultiplier = usesGapMultiplier; | |||
this.usesBaseEffort = usesBaseEffort; | |||
} | |||
/** | |||
* @deprecated since 5.5, replaced by {@link #usesGapMultiplier()} | |||
*/ | |||
@Deprecated | |||
public boolean usesCoefficient() { | |||
return usesCoefficient; | |||
return usesGapMultiplier(); | |||
} | |||
/** | |||
* @since 5.5 | |||
*/ | |||
public boolean usesGapMultiplier() { | |||
return usesGapMultiplier; | |||
} | |||
/** | |||
* @deprecated since 5.5, replaced by {@link #usesBaseEffort()} | |||
*/ | |||
@Deprecated | |||
public boolean usesOffset() { | |||
return usesOffset; | |||
return usesBaseEffort(); | |||
} | |||
/** | |||
* @since 5.5 | |||
*/ | |||
public boolean usesBaseEffort() { | |||
return usesBaseEffort; | |||
} | |||
} | |||
/** | |||
* @since 5.5 | |||
*/ | |||
Type type(); | |||
/** | |||
* Non-null value on {@link Type#LINEAR} and {@link Type#LINEAR_OFFSET} functions, else {@code null}. | |||
* @deprecated since 5.5, replaced by {@link #gapMultiplier()} | |||
*/ | |||
@Deprecated | |||
@CheckForNull | |||
String coefficient(); | |||
/** | |||
* Non-null value on {@link Type#LINEAR_OFFSET} and {@link Type#CONSTANT_ISSUE} functions, else {@code null}. | |||
* Non-null value on {@link Type#LINEAR} and {@link Type#LINEAR_OFFSET} functions, else {@code null}. | |||
* | |||
* @since 5.5 | |||
*/ | |||
@CheckForNull | |||
String gapMultiplier(); | |||
/** | |||
* @deprecated since 5.5, replaced by {@link #baseEffort()} | |||
*/ | |||
@Deprecated | |||
@CheckForNull | |||
String offset(); | |||
/** | |||
* Non-null value on {@link Type#LINEAR_OFFSET} and {@link Type#CONSTANT_ISSUE} functions, else {@code null}. | |||
* | |||
* @since 5.5 | |||
*/ | |||
@CheckForNull | |||
String baseEffort(); | |||
} |
@@ -34,13 +34,13 @@ public class DefaultDebtRemediationFunction implements DebtRemediationFunction { | |||
private static final int HOURS_IN_DAY = 24; | |||
private final Type type; | |||
private final String coefficient; | |||
private final String offset; | |||
private final String gapMultiplier; | |||
private final String baseEffort; | |||
public DefaultDebtRemediationFunction(@Nullable Type type, @Nullable String coefficient, @Nullable String offset) { | |||
public DefaultDebtRemediationFunction(@Nullable Type type, @Nullable String gapMultiplier, @Nullable String baseEffort) { | |||
this.type = type; | |||
this.coefficient = sanitizeValue("coefficient", coefficient); | |||
this.offset = sanitizeValue("offset", offset); | |||
this.gapMultiplier = sanitizeValue("gap multiplier", gapMultiplier); | |||
this.baseEffort = sanitizeValue("base effort", baseEffort); | |||
validate(); | |||
} | |||
@@ -62,29 +62,50 @@ public class DefaultDebtRemediationFunction implements DebtRemediationFunction { | |||
return type; | |||
} | |||
/** | |||
* @deprecated since 5.5, replaced by {@link #gapMultiplier} | |||
*/ | |||
@Override | |||
@CheckForNull | |||
@Deprecated | |||
public String coefficient() { | |||
return coefficient; | |||
return gapMultiplier(); | |||
} | |||
@Override | |||
@CheckForNull | |||
public String gapMultiplier() { | |||
return gapMultiplier; | |||
} | |||
/** | |||
* @deprecated since 5.5, replaced by {@link #baseEffort} | |||
*/ | |||
@Override | |||
@CheckForNull | |||
@Deprecated | |||
public String offset() { | |||
return offset; | |||
return baseEffort(); | |||
} | |||
@Override | |||
public String baseEffort() { | |||
return baseEffort; | |||
} | |||
private void validate() { | |||
checkArgument(type != null, "Remediation function type cannot be null"); | |||
switch (type) { | |||
case LINEAR: | |||
checkArgument(this.coefficient != null && this.offset == null, "Linear functions must only have a non empty coefficient"); | |||
checkArgument(this.gapMultiplier != null && this.baseEffort == null, "Linear functions must only have a non empty gap multiplier"); | |||
break; | |||
case LINEAR_OFFSET: | |||
checkArgument(this.coefficient != null && this.offset != null, "Linear with offset functions must have both non null coefficient and offset"); | |||
checkArgument(this.gapMultiplier != null && this.baseEffort != null, "Linear with offset functions must have both non null gap multiplier and base effort"); | |||
break; | |||
case CONSTANT_ISSUE: | |||
checkArgument(this.coefficient == null && this.offset != null, "Constant/issue functions must only have a non empty offset"); | |||
checkArgument(this.gapMultiplier == null && this.baseEffort != null, "Constant/issue functions must only have a non empty base effort"); | |||
break; | |||
default: | |||
throw new IllegalArgumentException(String.format("Unknown type on %s", this)); | |||
@@ -101,8 +122,8 @@ public class DefaultDebtRemediationFunction implements DebtRemediationFunction { | |||
} | |||
DefaultDebtRemediationFunction other = (DefaultDebtRemediationFunction) o; | |||
return new EqualsBuilder() | |||
.append(coefficient, other.coefficient()) | |||
.append(offset, other.offset()) | |||
.append(gapMultiplier, other.gapMultiplier()) | |||
.append(baseEffort, other.baseEffort()) | |||
.append(type, other.type()) | |||
.isEquals(); | |||
} | |||
@@ -110,8 +131,8 @@ public class DefaultDebtRemediationFunction implements DebtRemediationFunction { | |||
@Override | |||
public int hashCode() { | |||
int result = type.hashCode(); | |||
result = 31 * result + (coefficient != null ? coefficient.hashCode() : 0); | |||
result = 31 * result + (offset != null ? offset.hashCode() : 0); | |||
result = 31 * result + (gapMultiplier != null ? gapMultiplier.hashCode() : 0); | |||
result = 31 * result + (baseEffort != null ? baseEffort.hashCode() : 0); | |||
return result; | |||
} | |||
@@ -119,8 +140,8 @@ public class DefaultDebtRemediationFunction implements DebtRemediationFunction { | |||
public String toString() { | |||
return Objects.toStringHelper(DebtRemediationFunction.class) | |||
.add("type", type) | |||
.add("coefficient", coefficient) | |||
.add("offset", offset) | |||
.add("gap multiplier", gapMultiplier) | |||
.add("base effort", baseEffort) | |||
.toString(); | |||
} | |||
} |
@@ -19,12 +19,11 @@ | |||
*/ | |||
package org.sonar.api.server.rule; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.server.debt.DebtRemediationFunction; | |||
import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; | |||
import org.sonar.api.utils.MessageException; | |||
import javax.annotation.Nullable; | |||
/** | |||
* Factory of {@link org.sonar.api.server.debt.DebtRemediationFunction} that keeps | |||
* a context of rule for better error messages. Used only when declaring rules. | |||
@@ -42,24 +41,24 @@ class DefaultDebtRemediationFunctions implements RulesDefinition.DebtRemediation | |||
} | |||
@Override | |||
public DebtRemediationFunction linear(String coefficient) { | |||
return create(DefaultDebtRemediationFunction.Type.LINEAR, coefficient, null); | |||
public DebtRemediationFunction linear(String gapMultiplier) { | |||
return create(DefaultDebtRemediationFunction.Type.LINEAR, gapMultiplier, null); | |||
} | |||
@Override | |||
public DebtRemediationFunction linearWithOffset(String coefficient, String offset) { | |||
return create(DefaultDebtRemediationFunction.Type.LINEAR_OFFSET, coefficient, offset); | |||
public DebtRemediationFunction linearWithOffset(String gapMultiplier, String baseEffort) { | |||
return create(DefaultDebtRemediationFunction.Type.LINEAR_OFFSET, gapMultiplier, baseEffort); | |||
} | |||
@Override | |||
public DebtRemediationFunction constantPerIssue(String offset) { | |||
return create(DefaultDebtRemediationFunction.Type.CONSTANT_ISSUE, null, offset); | |||
public DebtRemediationFunction constantPerIssue(String baseEffort) { | |||
return create(DefaultDebtRemediationFunction.Type.CONSTANT_ISSUE, null, baseEffort); | |||
} | |||
@Override | |||
public DebtRemediationFunction create(DebtRemediationFunction.Type type, @Nullable String coefficient, @Nullable String offset) { | |||
public DebtRemediationFunction create(DebtRemediationFunction.Type type, @Nullable String gapMultiplier, @Nullable String baseEffort) { | |||
try { | |||
return new DefaultDebtRemediationFunction(type, coefficient, offset); | |||
return new DefaultDebtRemediationFunction(type, gapMultiplier, baseEffort); | |||
} catch (Exception e) { | |||
throw MessageException.of(String.format("The rule '%s:%s' is invalid : %s ", this.repoKey, this.key, e.getMessage())); | |||
} |
@@ -625,33 +625,33 @@ public interface RulesDefinition { | |||
interface DebtRemediationFunctions { | |||
/** | |||
* Shortcut for {@code create(Type.LINEAR, coefficient, null)}. | |||
* @param coefficient the duration to fix one issue. See {@link DebtRemediationFunction} for details about format. | |||
* Shortcut for {@code create(Type.LINEAR, gap multiplier, null)}. | |||
* @param gapMultiplier the duration to fix one issue. See {@link DebtRemediationFunction} for details about format. | |||
* @see org.sonar.api.server.debt.DebtRemediationFunction.Type#LINEAR | |||
*/ | |||
DebtRemediationFunction linear(String coefficient); | |||
DebtRemediationFunction linear(String gapMultiplier); | |||
/** | |||
* Shortcut for {@code create(Type.LINEAR_OFFSET, coefficient, offset)}. | |||
* @param coefficient duration to fix one point of complexity. See {@link DebtRemediationFunction} for details and format. | |||
* @param offset duration to make basic analysis. See {@link DebtRemediationFunction} for details and format. | |||
* Shortcut for {@code create(Type.LINEAR_OFFSET, gap multiplier, base effort)}. | |||
* @param gapMultiplier duration to fix one point of complexity. See {@link DebtRemediationFunction} for details and format. | |||
* @param baseEffort duration to make basic analysis. See {@link DebtRemediationFunction} for details and format. | |||
* @see org.sonar.api.server.debt.DebtRemediationFunction.Type#LINEAR_OFFSET | |||
*/ | |||
DebtRemediationFunction linearWithOffset(String coefficient, String offset); | |||
DebtRemediationFunction linearWithOffset(String gapMultiplier, String baseEffort); | |||
/** | |||
* Shortcut for {@code create(Type.CONSTANT_ISSUE, null, constant)}. | |||
* @param constant cost per issue. See {@link DebtRemediationFunction} for details and format. | |||
* Shortcut for {@code create(Type.CONSTANT_ISSUE, null, base effort)}. | |||
* @param baseEffort cost per issue. See {@link DebtRemediationFunction} for details and format. | |||
* @see org.sonar.api.server.debt.DebtRemediationFunction.Type#CONSTANT_ISSUE | |||
*/ | |||
DebtRemediationFunction constantPerIssue(String constant); | |||
DebtRemediationFunction constantPerIssue(String baseEffort); | |||
/** | |||
* Flexible way to create a {@link DebtRemediationFunction}. An unchecked exception is thrown if | |||
* coefficient and/or offset are not valid according to the given @{code type}. | |||
* @since 5.3 | |||
*/ | |||
DebtRemediationFunction create(DebtRemediationFunction.Type type, @Nullable String coefficient, @Nullable String offset); | |||
DebtRemediationFunction create(DebtRemediationFunction.Type type, @Nullable String gapMultiplier, @Nullable String baseEffort); | |||
} | |||
class NewRule { | |||
@@ -666,7 +666,7 @@ public interface RulesDefinition { | |||
private boolean template; | |||
private RuleStatus status = RuleStatus.defaultStatus(); | |||
private DebtRemediationFunction debtRemediationFunction; | |||
private String effortToFixDescription; | |||
private String gapDescription; | |||
private final Set<String> tags = Sets.newTreeSet(); | |||
private final Map<String, NewParam> paramsByKey = Maps.newHashMap(); | |||
private final DebtRemediationFunctions functions; | |||
@@ -810,17 +810,25 @@ public interface RulesDefinition { | |||
return this; | |||
} | |||
/** | |||
* @deprecated since 5.5, replaced by {@link #setGapDescription(String)} | |||
*/ | |||
@Deprecated | |||
public NewRule setEffortToFixDescription(@Nullable String s) { | |||
return setGapDescription(s); | |||
} | |||
/** | |||
* For rules that use LINEAR or LINEAR_OFFSET remediation functions, the meaning | |||
* of the function parameter (= "effort to fix") must be set. This description | |||
* explains what 1 point of "effort to fix" represents for the rule. | |||
* of the function parameter (= "gap") must be set. This description | |||
* explains what 1 point of "gap" represents for the rule. | |||
* <p/> | |||
* Example: for the "Insufficient condition coverage", this description for the | |||
* remediation function coefficient/offset would be something like | |||
* remediation function gap multiplier/base effort would be something like | |||
* "Effort to test one uncovered condition". | |||
*/ | |||
public NewRule setEffortToFixDescription(@Nullable String s) { | |||
this.effortToFixDescription = s; | |||
public NewRule setGapDescription(@Nullable String s) { | |||
this.gapDescription = s; | |||
return this; | |||
} | |||
@@ -901,7 +909,7 @@ public interface RulesDefinition { | |||
private final String severity; | |||
private final boolean template; | |||
private final DebtRemediationFunction debtRemediationFunction; | |||
private final String effortToFixDescription; | |||
private final String gapDescription; | |||
private final Set<String> tags; | |||
private final Map<String, Param> params; | |||
private final RuleStatus status; | |||
@@ -918,7 +926,7 @@ public interface RulesDefinition { | |||
this.template = newRule.template; | |||
this.status = newRule.status; | |||
this.debtRemediationFunction = newRule.debtRemediationFunction; | |||
this.effortToFixDescription = newRule.effortToFixDescription; | |||
this.gapDescription = newRule.gapDescription; | |||
this.type = newRule.type == null ? RuleTagsToTypeConverter.convert(newRule.tags) : newRule.type; | |||
this.tags = ImmutableSortedSet.copyOf(Sets.difference(newRule.tags, RuleTagsToTypeConverter.RESERVED_TAGS)); | |||
ImmutableMap.Builder<String, Param> paramsBuilder = ImmutableMap.builder(); | |||
@@ -986,9 +994,18 @@ public interface RulesDefinition { | |||
return debtRemediationFunction; | |||
} | |||
/** | |||
* @deprecated since 5.5, replaced by {@link #gapDescription()} | |||
*/ | |||
@Deprecated | |||
@CheckForNull | |||
public String effortToFixDescription() { | |||
return effortToFixDescription; | |||
return gapDescription(); | |||
} | |||
@CheckForNull | |||
public String gapDescription() { | |||
return gapDescription; | |||
} | |||
@CheckForNull |
@@ -121,32 +121,37 @@ import static org.apache.commons.lang.StringUtils.trim; | |||
* <param> | |||
* <key>another-param</key> | |||
* </param> | |||
** | |||
* <!-- SQALE debt - type of debt remediation function --> | |||
* | |||
* <!-- Quality Model - type of debt remediation function --> | |||
* <!-- See enum {@link org.sonar.api.server.debt.DebtRemediationFunction.Type} for supported values --> | |||
* <!-- Since 5.3 --> | |||
* <debtRemediationFunction>LINEAR_OFFSET</debtRemediationFunction> | |||
* <!-- It was previously named 'debtRemediationFunction' which is still supported but deprecated since 5.5 --> | |||
* <!-- Since 5.5 --> | |||
* <remediationFunction>LINEAR_OFFSET</remediationFunction> | |||
* | |||
* <!-- SQALE debt - raw description of the "effort to fix", used for some types of remediation functions. --> | |||
* <!-- See {@link org.sonar.api.server.rule.RulesDefinition.NewRule#setEffortToFixDescription(String)} --> | |||
* <!-- Since 5.3 --> | |||
* <effortToFixDescription>Effort to test one uncovered condition</effortToFixDescription> | |||
* <!-- Quality Model - raw description of the "gap", used for some types of remediation functions. --> | |||
* <!-- See {@link org.sonar.api.server.rule.RulesDefinition.NewRule#setGapDescription(String)} --> | |||
* <!-- It was previously named 'effortToFixDescription' which is still supported but deprecated since 5.5 --> | |||
* <!-- Since 5.5 --> | |||
* <gapDescription>Effort to test one uncovered condition</gapFixDescription> | |||
* | |||
* <!-- SQALE debt - coefficient of debt remediation function. Must be defined only for some function types. --> | |||
* <!-- Quality Model - gap multiplier of debt remediation function. Must be defined only for some function types. --> | |||
* <!-- See {@link org.sonar.api.server.rule.RulesDefinition.DebtRemediationFunctions} --> | |||
* <!-- Since 5.3 --> | |||
* <debtRemediationFunctionCoefficient>10min</debtRemediationFunctionCoefficient> | |||
* <!-- It was previously named 'debtRemediationFunctionCoefficient' which is still supported but deprecated since 5.5 --> | |||
* <!-- Since 5.5 --> | |||
* <remediationFunctionGapMultiplier>10min</remediationFunctionGapMultiplier> | |||
* | |||
* <!-- SQALE debt - offset of debt remediation function. Must be defined only for some function types. --> | |||
* <!-- Quality Model - base effort of debt remediation function. Must be defined only for some function types. --> | |||
* <!-- See {@link org.sonar.api.server.rule.RulesDefinition.DebtRemediationFunctions} --> | |||
* <!-- Since 5.3 --> | |||
* <debtRemediationFunctionOffset>2min</debtRemediationFunctionOffset> | |||
* <!-- It was previously named 'debtRemediationFunctionOffset' which is still supported but deprecated since 5.5 --> | |||
* <!-- Since 5.5 --> | |||
* <remediationFunctionBaseEffort>2min</remediationFunctionBaseEffort> | |||
* | |||
* <!-- Deprecated field, replaced by "internalKey" --> | |||
* <configKey>Checker/TreeWalker/LocalVariableName</configKey> | |||
* | |||
* <!-- Deprecated field, replaced by "severity" --> | |||
* <priority>BLOCKER</priority> | |||
* | |||
* </rule> | |||
* </rules> | |||
* </pre> | |||
@@ -162,7 +167,7 @@ import static org.apache.commons.lang.StringUtils.trim; | |||
* <tag>security</tag> | |||
* <tag>user-experience</tag> | |||
* <debtRemediationFunction>CONSTANT_ISSUE</debtRemediationFunction> | |||
* <debtRemediationFunctionOffset>10min</debtRemediationFunctionOffset> | |||
* <debtRemediationFunctionBaseOffset>10min</debtRemediationFunctionBaseOffset> | |||
* </rule> | |||
* | |||
* <!-- another rules... --> | |||
@@ -238,10 +243,10 @@ public class RulesDefinitionXmlLoader { | |||
String type = null; | |||
RuleStatus status = RuleStatus.defaultStatus(); | |||
boolean template = false; | |||
String effortToFixDescription = null; | |||
String gapDescription = null; | |||
String debtRemediationFunction = null; | |||
String debtRemediationFunctionOffset = null; | |||
String debtRemediationFunctionCoeff = null; | |||
String debtRemediationFunctionGapMultiplier = null; | |||
String debtRemediationFunctionBaseEffort = null; | |||
List<ParamStruct> params = new ArrayList<>(); | |||
List<String> tags = new ArrayList<>(); | |||
@@ -288,17 +293,17 @@ public class RulesDefinitionXmlLoader { | |||
} else if (equalsIgnoreCase("cardinality", nodeName)) { | |||
template = Cardinality.MULTIPLE == Cardinality.valueOf(nodeValue(cursor)); | |||
} else if (equalsIgnoreCase("effortToFixDescription", nodeName)) { | |||
effortToFixDescription = nodeValue(cursor); | |||
} else if (equalsIgnoreCase("gapDescription", nodeName) || equalsIgnoreCase("effortToFixDescription", nodeName)) { | |||
gapDescription = nodeValue(cursor); | |||
} else if (equalsIgnoreCase("debtRemediationFunction", nodeName)) { | |||
} else if (equalsIgnoreCase("remediationFunction", nodeName) || equalsIgnoreCase("debtRemediationFunction", nodeName)) { | |||
debtRemediationFunction = nodeValue(cursor); | |||
} else if (equalsIgnoreCase("debtRemediationFunctionOffset", nodeName)) { | |||
debtRemediationFunctionOffset = nodeValue(cursor); | |||
} else if (equalsIgnoreCase("remediationFunctionBaseEffort", nodeName) || equalsIgnoreCase("debtRemediationFunctionOffset", nodeName)) { | |||
debtRemediationFunctionGapMultiplier = nodeValue(cursor); | |||
} else if (equalsIgnoreCase("debtRemediationFunctionCoefficient", nodeName)) { | |||
debtRemediationFunctionCoeff = nodeValue(cursor); | |||
} else if (equalsIgnoreCase("remediationFunctionGapMultiplier", nodeName) || equalsIgnoreCase("debtRemediationFunctionCoefficient", nodeName)) { | |||
debtRemediationFunctionBaseEffort = nodeValue(cursor); | |||
} else if (equalsIgnoreCase("status", nodeName)) { | |||
String s = nodeValue(cursor); | |||
@@ -322,12 +327,12 @@ public class RulesDefinitionXmlLoader { | |||
.setTags(tags.toArray(new String[tags.size()])) | |||
.setTemplate(template) | |||
.setStatus(status) | |||
.setEffortToFixDescription(effortToFixDescription); | |||
.setGapDescription(gapDescription); | |||
if (type != null) { | |||
rule.setType(RuleType.valueOf(type)); | |||
} | |||
fillDescription(rule, descriptionFormat, description); | |||
fillRemediationFunction(rule, debtRemediationFunction, debtRemediationFunctionOffset, debtRemediationFunctionCoeff); | |||
fillRemediationFunction(rule, debtRemediationFunction, debtRemediationFunctionGapMultiplier, debtRemediationFunctionBaseEffort); | |||
fillParams(rule, params); | |||
} catch (Exception e) { | |||
throw new IllegalArgumentException(format("Fail to load the rule with key [%s:%s]", repo.key(), key), e); |
@@ -31,32 +31,32 @@ public class DefaultDebtRemediationFunctionTest { | |||
public void create_linear() { | |||
DebtRemediationFunction function = new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR, "10h", null); | |||
assertThat(function.type()).isEqualTo(DefaultDebtRemediationFunction.Type.LINEAR); | |||
assertThat(function.coefficient()).isEqualTo("10h"); | |||
assertThat(function.offset()).isNull(); | |||
assertThat(function.gapMultiplier()).isEqualTo("10h"); | |||
assertThat(function.baseEffort()).isNull(); | |||
} | |||
@Test | |||
public void create_linear_with_offset() { | |||
DebtRemediationFunction function = new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET, "10h", "5min"); | |||
assertThat(function.type()).isEqualTo(DefaultDebtRemediationFunction.Type.LINEAR_OFFSET); | |||
assertThat(function.coefficient()).isEqualTo("10h"); | |||
assertThat(function.offset()).isEqualTo("5min"); | |||
assertThat(function.gapMultiplier()).isEqualTo("10h"); | |||
assertThat(function.baseEffort()).isEqualTo("5min"); | |||
} | |||
@Test | |||
public void create_constant_per_issue() { | |||
DebtRemediationFunction function = new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, null, "10h"); | |||
assertThat(function.type()).isEqualTo(DefaultDebtRemediationFunction.Type.CONSTANT_ISSUE); | |||
assertThat(function.coefficient()).isNull(); | |||
assertThat(function.offset()).isEqualTo("10h"); | |||
assertThat(function.gapMultiplier()).isNull(); | |||
assertThat(function.baseEffort()).isEqualTo("10h"); | |||
} | |||
@Test | |||
public void sanitize_remediation_coefficient_and_offset() { | |||
DebtRemediationFunction function = new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET, " 1 h ", " 10 min"); | |||
assertThat(function.coefficient()).isEqualTo("1h"); | |||
assertThat(function.offset()).isEqualTo("10min"); | |||
assertThat(function.gapMultiplier()).isEqualTo("1h"); | |||
assertThat(function.baseEffort()).isEqualTo("10min"); | |||
} | |||
@Test | |||
@@ -75,7 +75,7 @@ public class DefaultDebtRemediationFunctionTest { | |||
new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR, null, "10h"); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Linear functions must only have a non empty coefficient"); | |||
assertThat(e).hasMessage("Linear functions must only have a non empty gap multiplier"); | |||
} | |||
} | |||
@@ -85,7 +85,7 @@ public class DefaultDebtRemediationFunctionTest { | |||
new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR, "5min", "10h"); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Linear functions must only have a non empty coefficient"); | |||
assertThat(e).hasMessage("Linear functions must only have a non empty gap multiplier"); | |||
} | |||
} | |||
@@ -95,7 +95,7 @@ public class DefaultDebtRemediationFunctionTest { | |||
new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, "10h", null); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Constant/issue functions must only have a non empty offset"); | |||
assertThat(e).hasMessage("Constant/issue functions must only have a non empty base effort"); | |||
} | |||
} | |||
@@ -105,7 +105,7 @@ public class DefaultDebtRemediationFunctionTest { | |||
new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, "5min", "10h"); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Constant/issue functions must only have a non empty offset"); | |||
assertThat(e).hasMessage("Constant/issue functions must only have a non empty base effort"); | |||
} | |||
} | |||
@@ -115,7 +115,7 @@ public class DefaultDebtRemediationFunctionTest { | |||
new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET, "", "10h"); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Linear with offset functions must have both non null coefficient and offset"); | |||
assertThat(e).hasMessage("Linear with offset functions must have both non null gap multiplier and base effort"); | |||
} | |||
} | |||
@@ -125,7 +125,7 @@ public class DefaultDebtRemediationFunctionTest { | |||
new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET, "5min", ""); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Linear with offset functions must have both non null coefficient and offset"); | |||
assertThat(e).hasMessage("Linear with offset functions must have both non null gap multiplier and base effort"); | |||
} | |||
} | |||
@@ -152,7 +152,7 @@ public class DefaultDebtRemediationFunctionTest { | |||
@Test | |||
public void test_to_string() { | |||
assertThat(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET, "10h", "5min").toString()) | |||
.isEqualTo("DebtRemediationFunction{type=LINEAR_OFFSET, coefficient=10h, offset=5min}"); | |||
.isEqualTo("DebtRemediationFunction{type=LINEAR_OFFSET, gap multiplier=10h, base effort=5min}"); | |||
} | |||
@Test | |||
@@ -161,7 +161,7 @@ public class DefaultDebtRemediationFunctionTest { | |||
new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR, "foo", null); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Invalid coefficient: foo (Duration 'foo' is invalid, it should use the following sample format : 2d 10h 15min)"); | |||
assertThat(e).hasMessage("Invalid gap multiplier: foo (Duration 'foo' is invalid, it should use the following sample format : 2d 10h 15min)"); | |||
} | |||
} |
@@ -119,7 +119,7 @@ public class RulesDefinitionTest { | |||
.setName("Insufficient condition coverage") | |||
.setHtmlDescription("Insufficient condition coverage by unit tests") | |||
.setSeverity(Severity.MAJOR) | |||
.setEffortToFixDescription("Effort to test one uncovered branch"); | |||
.setGapDescription("Effort to test one uncovered branch"); | |||
newRule.setDebtRemediationFunction(newRule.debtRemediationFunctions().linearWithOffset("1h", "10min")); | |||
newRepo.done(); | |||
@@ -128,9 +128,9 @@ public class RulesDefinitionTest { | |||
RulesDefinition.Rule rule = repo.rule("InsufficientBranchCoverage"); | |||
assertThat(rule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET); | |||
assertThat(rule.debtRemediationFunction().coefficient()).isEqualTo("1h"); | |||
assertThat(rule.debtRemediationFunction().offset()).isEqualTo("10min"); | |||
assertThat(rule.effortToFixDescription()).isEqualTo("Effort to test one uncovered branch"); | |||
assertThat(rule.debtRemediationFunction().gapMultiplier()).isEqualTo("1h"); | |||
assertThat(rule.debtRemediationFunction().baseEffort()).isEqualTo("10min"); | |||
assertThat(rule.gapDescription()).isEqualTo("Effort to test one uncovered branch"); | |||
} | |||
@Test |
@@ -139,18 +139,18 @@ public class RulesDefinitionXmlLoaderTest { | |||
" <name>One</name>" + | |||
" <description>Desc</description>" + | |||
" <effortToFixDescription>lines</effortToFixDescription>" + | |||
" <debtRemediationFunction>LINEAR</debtRemediationFunction>" + | |||
" <debtRemediationFunctionCoefficient>2d 3h</debtRemediationFunctionCoefficient>" + | |||
" <gapDescription>lines</gapDescription>" + | |||
" <remediationFunction>LINEAR</remediationFunction>" + | |||
" <remediationFunctionGapMultiplier>2d 3h</remediationFunctionGapMultiplier>" + | |||
" </rule>" + | |||
"</rules>"; | |||
RulesDefinition.Rule rule = load(xml).rule("1"); | |||
assertThat(rule.effortToFixDescription()).isEqualTo("lines"); | |||
assertThat(rule.gapDescription()).isEqualTo("lines"); | |||
DebtRemediationFunction function = rule.debtRemediationFunction(); | |||
assertThat(function).isNotNull(); | |||
assertThat(function.type()).isEqualTo(DebtRemediationFunction.Type.LINEAR); | |||
assertThat(function.coefficient()).isEqualTo("2d3h"); | |||
assertThat(function.offset()).isNull(); | |||
assertThat(function.gapMultiplier()).isEqualTo("2d3h"); | |||
assertThat(function.baseEffort()).isNull(); | |||
} | |||
@Test | |||
@@ -163,18 +163,18 @@ public class RulesDefinitionXmlLoaderTest { | |||
" <description>Desc</description>" + | |||
" <effortToFixDescription>lines</effortToFixDescription>" + | |||
" <debtRemediationFunction>LINEAR_OFFSET</debtRemediationFunction>" + | |||
" <debtRemediationFunctionCoefficient>2d 3h</debtRemediationFunctionCoefficient>" + | |||
" <debtRemediationFunctionOffset>5min</debtRemediationFunctionOffset>" + | |||
" <remediationFunction>LINEAR_OFFSET</remediationFunction>" + | |||
" <remediationFunctionGapMultiplier>2d 3h</remediationFunctionGapMultiplier>" + | |||
" <remediationFunctionBaseEffort>5min</remediationFunctionBaseEffort>" + | |||
" </rule>" + | |||
"</rules>"; | |||
RulesDefinition.Rule rule = load(xml).rule("1"); | |||
assertThat(rule.effortToFixDescription()).isEqualTo("lines"); | |||
assertThat(rule.gapDescription()).isEqualTo("lines"); | |||
DebtRemediationFunction function = rule.debtRemediationFunction(); | |||
assertThat(function).isNotNull(); | |||
assertThat(function.type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET); | |||
assertThat(function.coefficient()).isEqualTo("2d3h"); | |||
assertThat(function.offset()).isEqualTo("5min"); | |||
assertThat(function.gapMultiplier()).isEqualTo("2d3h"); | |||
assertThat(function.baseEffort()).isEqualTo("5min"); | |||
} | |||
@Test | |||
@@ -185,16 +185,16 @@ public class RulesDefinitionXmlLoaderTest { | |||
" <key>1</key>" + | |||
" <name>One</name>" + | |||
" <description>Desc</description>" + | |||
" <debtRemediationFunction>CONSTANT_ISSUE</debtRemediationFunction>" + | |||
" <debtRemediationFunctionOffset>5min</debtRemediationFunctionOffset>" + | |||
" <remediationFunction>CONSTANT_ISSUE</remediationFunction>" + | |||
" <remediationFunctionBaseEffort>5min</remediationFunctionBaseEffort>" + | |||
" </rule>" + | |||
"</rules>"; | |||
RulesDefinition.Rule rule = load(xml).rule("1"); | |||
DebtRemediationFunction function = rule.debtRemediationFunction(); | |||
assertThat(function).isNotNull(); | |||
assertThat(function.type()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE); | |||
assertThat(function.coefficient()).isNull(); | |||
assertThat(function.offset()).isEqualTo("5min"); | |||
assertThat(function.gapMultiplier()).isNull(); | |||
assertThat(function.baseEffort()).isEqualTo("5min"); | |||
} | |||
@Test | |||
@@ -206,7 +206,7 @@ public class RulesDefinitionXmlLoaderTest { | |||
" <key>1</key>" + | |||
" <name>One</name>" + | |||
" <description>Desc</description>" + | |||
" <debtRemediationFunction>UNKNOWN</debtRemediationFunction>" + | |||
" <remediationFunction>UNKNOWN</remediationFunction>" + | |||
" </rule>" + | |||
"</rules>"); | |||
fail(); | |||
@@ -226,9 +226,9 @@ public class RulesDefinitionXmlLoaderTest { | |||
" <description>Desc</description>" + | |||
" <effortToFixDescription>lines</effortToFixDescription>" + | |||
" <debtSubCharacteristic>BUG</debtSubCharacteristic>" + | |||
" <debtRemediationFunction>LINEAR_OFFSET</debtRemediationFunction>" + | |||
" <debtRemediationFunctionCoefficient>2d 3h</debtRemediationFunctionCoefficient>" + | |||
" <debtRemediationFunctionOffset>5min</debtRemediationFunctionOffset>" + | |||
" <remediationFunction>LINEAR_OFFSET</remediationFunction>" + | |||
" <remediationFunctionGapMultiplier>2d 3h</remediationFunctionGapMultiplier>" + | |||
" <remediationFunctionBaseEffort>5min</remediationFunctionBaseEffort>" + | |||
" </rule>" + | |||
"</rules>"; | |||
RulesDefinition.Rule rule = load(xml).rule("1"); | |||
@@ -272,6 +272,29 @@ public class RulesDefinitionXmlLoaderTest { | |||
} | |||
} | |||
@Test | |||
public void test_deprecated_remediation_function() { | |||
String xml = "" + | |||
"<rules>" + | |||
" <rule>" + | |||
" <key>1</key>" + | |||
" <name>One</name>" + | |||
" <description>Desc</description>" + | |||
" <effortToFixDescription>lines</effortToFixDescription>" + | |||
" <debtRemediationFunction>LINEAR_OFFSET</debtRemediationFunction>" + | |||
" <debtRemediationFunctionCoefficient>2d 3h</debtRemediationFunctionCoefficient>" + | |||
" <debtRemediationFunctionOffset>5min</debtRemediationFunctionOffset>" + | |||
" </rule>" + | |||
"</rules>"; | |||
RulesDefinition.Rule rule = load(xml).rule("1"); | |||
assertThat(rule.gapDescription()).isEqualTo("lines"); | |||
DebtRemediationFunction function = rule.debtRemediationFunction(); | |||
assertThat(function).isNotNull(); | |||
assertThat(function.type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET); | |||
assertThat(function.gapMultiplier()).isEqualTo("2d3h"); | |||
assertThat(function.baseEffort()).isEqualTo("5min"); | |||
} | |||
private RulesDefinition.Repository load(InputStream input, String encoding) { | |||
RulesDefinition.Context context = new RulesDefinition.Context(); | |||
RulesDefinition.NewRepository newRepository = context.createRepository("squid", "java"); |