Browse Source

SONAR-7453 Rename rules remediation function fields in API

tags/5.5-M10
Julien Lancelot 8 years ago
parent
commit
b669aac1e3
21 changed files with 298 additions and 206 deletions
  1. 3
    3
      plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java
  2. 3
    3
      plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java
  3. 6
    4
      server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java
  4. 2
    2
      server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelBackup.java
  5. 6
    6
      server/sonar-server/src/main/java/org/sonar/server/rule/CommonRuleDefinitionsImpl.java
  6. 4
    4
      server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java
  7. 4
    3
      server/sonar-server/src/main/java/org/sonar/server/rule/RuleOperations.java
  8. 4
    4
      server/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdater.java
  9. 17
    36
      server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapper.java
  10. 5
    6
      server/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoaderTest.java
  11. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesMediumTest.java
  12. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
  13. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/rule/RuleOperationsTest.java
  14. 62
    18
      sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtRemediationFunction.java
  15. 37
    16
      sonar-plugin-api/src/main/java/org/sonar/api/server/debt/internal/DefaultDebtRemediationFunction.java
  16. 9
    10
      sonar-plugin-api/src/main/java/org/sonar/api/server/rule/DefaultDebtRemediationFunctions.java
  17. 37
    20
      sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java
  18. 32
    27
      sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinitionXmlLoader.java
  19. 16
    16
      sonar-plugin-api/src/test/java/org/sonar/api/server/debt/DefaultDebtRemediationFunctionTest.java
  20. 4
    4
      sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java
  21. 43
    20
      sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionXmlLoaderTest.java

+ 3
- 3
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java View File

@@ -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")

+ 3
- 3
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java View 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

+ 6
- 4
server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java View File

@@ -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;
}

+ 2
- 2
server/sonar-server/src/main/java/org/sonar/server/debt/DebtModelBackup.java View File

@@ -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

+ 6
- 6
server/sonar-server/src/main/java/org/sonar/server/rule/CommonRuleDefinitionsImpl.java View File

@@ -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);
}
}

+ 4
- 4
server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java View File

@@ -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);
}

+ 4
- 3
server/sonar-server/src/main/java/org/sonar/server/rule/RuleOperations.java View File

@@ -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,

+ 4
- 4
server/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdater.java View File

@@ -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();
}


+ 17
- 36
server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapper.java View File

@@ -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());
}
}


+ 5
- 6
server/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionLoaderTest.java View File

@@ -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

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesMediumTest.java View File

@@ -287,7 +287,7 @@ public class RegisterRulesMediumTest {
.setInternalKey("new_internal");
rule
.setDebtRemediationFunction(rule.debtRemediationFunctions().linearWithOffset("1h", "30min"))
.setEffortToFixDescription("Effort");
.setGapDescription("Effort");
}
});


+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java View File

@@ -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");

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/rule/RuleOperationsTest.java View File

@@ -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));

+ 62
- 18
sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtRemediationFunction.java View File

@@ -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();

}

+ 37
- 16
sonar-plugin-api/src/main/java/org/sonar/api/server/debt/internal/DefaultDebtRemediationFunction.java View File

@@ -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();
}
}

+ 9
- 10
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/DefaultDebtRemediationFunctions.java View File

@@ -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()));
}

+ 37
- 20
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java View File

@@ -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

+ 32
- 27
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinitionXmlLoader.java View File

@@ -121,32 +121,37 @@ import static org.apache.commons.lang.StringUtils.trim;
* &lt;param&gt;
* &lt;key&gt;another-param&lt;/key&gt;
* &lt;/param&gt;
**
* &lt;!-- SQALE debt - type of debt remediation function --&gt;
*
* &lt;!-- Quality Model - type of debt remediation function --&gt;
* &lt;!-- See enum {@link org.sonar.api.server.debt.DebtRemediationFunction.Type} for supported values --&gt;
* &lt;!-- Since 5.3 --&gt;
* &lt;debtRemediationFunction&gt;LINEAR_OFFSET&lt;/debtRemediationFunction&gt;
* &lt;!-- It was previously named 'debtRemediationFunction' which is still supported but deprecated since 5.5 --&gt;
* &lt;!-- Since 5.5 --&gt;
* &lt;remediationFunction&gt;LINEAR_OFFSET&lt;/remediationFunction&gt;
*
* &lt;!-- SQALE debt - raw description of the "effort to fix", used for some types of remediation functions. --&gt;
* &lt;!-- See {@link org.sonar.api.server.rule.RulesDefinition.NewRule#setEffortToFixDescription(String)} --&gt;
* &lt;!-- Since 5.3 --&gt;
* &lt;effortToFixDescription&gt;Effort to test one uncovered condition&lt;/effortToFixDescription&gt;
* &lt;!-- Quality Model - raw description of the "gap", used for some types of remediation functions. --&gt;
* &lt;!-- See {@link org.sonar.api.server.rule.RulesDefinition.NewRule#setGapDescription(String)} --&gt;
* &lt;!-- It was previously named 'effortToFixDescription' which is still supported but deprecated since 5.5 --&gt;
* &lt;!-- Since 5.5 --&gt;
* &lt;gapDescription&gt;Effort to test one uncovered condition&lt;/gapFixDescription&gt;
*
* &lt;!-- SQALE debt - coefficient of debt remediation function. Must be defined only for some function types. --&gt;
* &lt;!-- Quality Model - gap multiplier of debt remediation function. Must be defined only for some function types. --&gt;
* &lt;!-- See {@link org.sonar.api.server.rule.RulesDefinition.DebtRemediationFunctions} --&gt;
* &lt;!-- Since 5.3 --&gt;
* &lt;debtRemediationFunctionCoefficient&gt;10min&lt;/debtRemediationFunctionCoefficient&gt;
* &lt;!-- It was previously named 'debtRemediationFunctionCoefficient' which is still supported but deprecated since 5.5 --&gt;
* &lt;!-- Since 5.5 --&gt;
* &lt;remediationFunctionGapMultiplier&gt;10min&lt;/remediationFunctionGapMultiplier&gt;
*
* &lt;!-- SQALE debt - offset of debt remediation function. Must be defined only for some function types. --&gt;
* &lt;!-- Quality Model - base effort of debt remediation function. Must be defined only for some function types. --&gt;
* &lt;!-- See {@link org.sonar.api.server.rule.RulesDefinition.DebtRemediationFunctions} --&gt;
* &lt;!-- Since 5.3 --&gt;
* &lt;debtRemediationFunctionOffset&gt;2min&lt;/debtRemediationFunctionOffset&gt;
* &lt;!-- It was previously named 'debtRemediationFunctionOffset' which is still supported but deprecated since 5.5 --&gt;
* &lt;!-- Since 5.5 --&gt;
* &lt;remediationFunctionBaseEffort&gt;2min&lt;/remediationFunctionBaseEffort&gt;
*
* &lt;!-- Deprecated field, replaced by "internalKey" --&gt;
* &lt;configKey&gt;Checker/TreeWalker/LocalVariableName&lt;/configKey&gt;
*
* &lt;!-- Deprecated field, replaced by "severity" --&gt;
* &lt;priority&gt;BLOCKER&lt;/priority&gt;
*
* &lt;/rule&gt;
* &lt;/rules&gt;
* </pre>
@@ -162,7 +167,7 @@ import static org.apache.commons.lang.StringUtils.trim;
* &lt;tag&gt;security&lt;/tag&gt;
* &lt;tag&gt;user-experience&lt;/tag&gt;
* &lt;debtRemediationFunction&gt;CONSTANT_ISSUE&lt;/debtRemediationFunction&gt;
* &lt;debtRemediationFunctionOffset&gt;10min&lt;/debtRemediationFunctionOffset&gt;
* &lt;debtRemediationFunctionBaseOffset&gt;10min&lt;/debtRemediationFunctionBaseOffset&gt;
* &lt;/rule&gt;
*
* &lt;!-- another rules... --&gt;
@@ -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);

+ 16
- 16
sonar-plugin-api/src/test/java/org/sonar/api/server/debt/DefaultDebtRemediationFunctionTest.java View File

@@ -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)");
}

}

+ 4
- 4
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionTest.java View File

@@ -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

+ 43
- 20
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RulesDefinitionXmlLoaderTest.java View File

@@ -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");

Loading…
Cancel
Save