@@ -0,0 +1,140 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.api.server.rule; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.apache.commons.lang.builder.ReflectionToStringBuilder; | |||
import org.apache.commons.lang.builder.ToStringStyle; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
/** | |||
* @since 4.3 | |||
*/ | |||
public class DebtRemediationFunction { | |||
public static enum Type { | |||
LINEAR, LINEAR_OFFSET, CONSTANT_ISSUE | |||
} | |||
public static class ValidationException extends RuntimeException { | |||
public ValidationException(String message) { | |||
super(message); | |||
} | |||
} | |||
private Type type; | |||
private String factor; | |||
private String offset; | |||
private DebtRemediationFunction(Type type, @Nullable String factor, @Nullable String offset) { | |||
this.type = type; | |||
// TODO validate factor and offset format | |||
this.factor = StringUtils.deleteWhitespace(factor); | |||
this.offset = StringUtils.deleteWhitespace(offset); | |||
switch (type) { | |||
case LINEAR: | |||
if (this.factor == null || this.offset != null) { | |||
throw new ValidationException(String.format("%s is invalid, Linear remediation function should only define a factor", this)); | |||
} | |||
break; | |||
case LINEAR_OFFSET: | |||
if (this.factor == null || this.offset == null) { | |||
throw new ValidationException(String.format("%s is invalid, Linear with offset remediation function should define both factor and offset", this)); | |||
} | |||
break; | |||
case CONSTANT_ISSUE: | |||
if (this.factor != null || this.offset == null) { | |||
throw new ValidationException(String.format("%s is invalid, Constant/issue remediation function should only define an offset", this)); | |||
} | |||
break; | |||
} | |||
} | |||
public static DebtRemediationFunction create(Type type, @Nullable String factor, @Nullable String offset) { | |||
return new DebtRemediationFunction(type, factor, offset); | |||
} | |||
public static DebtRemediationFunction createLinear(String factor) { | |||
return new DebtRemediationFunction(Type.LINEAR, factor, null); | |||
} | |||
public static DebtRemediationFunction createLinearWithOffset(String factor, String offset) { | |||
return new DebtRemediationFunction(Type.LINEAR_OFFSET, factor, offset); | |||
} | |||
public static DebtRemediationFunction createConstantPerIssue(String offset) { | |||
return new DebtRemediationFunction(Type.CONSTANT_ISSUE, null, offset); | |||
} | |||
public Type type() { | |||
return type; | |||
} | |||
@CheckForNull | |||
public String factor() { | |||
return factor; | |||
} | |||
@CheckForNull | |||
public String offset() { | |||
return offset; | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) { | |||
return true; | |||
} | |||
if (o == null || getClass() != o.getClass()) { | |||
return false; | |||
} | |||
DebtRemediationFunction that = (DebtRemediationFunction) o; | |||
if (type != that.type) { | |||
return false; | |||
} | |||
if (factor != null ? !factor.equals(that.factor) : that.factor != null) { | |||
return false; | |||
} | |||
if (offset != null ? !offset.equals(that.offset) : that.offset != null) { | |||
return false; | |||
} | |||
return true; | |||
} | |||
@Override | |||
public int hashCode() { | |||
int result = type.hashCode(); | |||
result = 31 * result + (factor != null ? factor.hashCode() : 0); | |||
result = 31 * result + (offset != null ? offset.hashCode() : 0); | |||
return result; | |||
} | |||
@Override | |||
public String toString() { | |||
return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString(); | |||
} | |||
} |
@@ -24,7 +24,6 @@ import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.ServerExtension; | |||
import org.sonar.api.rule.RemediationFunction; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
@@ -312,10 +311,8 @@ public interface RulesDefinition extends ServerExtension { | |||
private String name, htmlDescription, internalKey, severity = Severity.MAJOR; | |||
private boolean template; | |||
private RuleStatus status = RuleStatus.defaultStatus(); | |||
private String characteristicKey; | |||
private RemediationFunction remediationFunction; | |||
private String remediationFactor; | |||
private String remediationOffset; | |||
private String debtCharacteristic; | |||
private DebtRemediationFunction debtRemediationFunction; | |||
private String effortToFixL10nKey; | |||
private final Set<String> tags = Sets.newTreeSet(); | |||
private final Map<String, NewParam> paramsByKey = Maps.newHashMap(); | |||
@@ -376,25 +373,13 @@ public interface RulesDefinition extends ServerExtension { | |||
return this; | |||
} | |||
public NewRule setCharacteristicKey(@Nullable String characteristicKey) { | |||
this.characteristicKey = characteristicKey; | |||
public NewRule setDebtCharacteristic(@Nullable String debtCharacteristic) { | |||
this.debtCharacteristic = debtCharacteristic; | |||
return this; | |||
} | |||
public NewRule setRemediationFunction(@Nullable RemediationFunction remediationFunction) { | |||
this.remediationFunction = remediationFunction; | |||
return this; | |||
} | |||
public NewRule setRemediationFactor(@Nullable String remediationFactor) { | |||
// TODO validate format | |||
this.remediationFactor = StringUtils.deleteWhitespace(remediationFactor); | |||
return this; | |||
} | |||
public NewRule setRemediationOffset(@Nullable String remediationOffset) { | |||
// TODO validate format | |||
this.remediationOffset = StringUtils.deleteWhitespace(remediationOffset); | |||
public NewRule setDebtRemediationFunction(@Nullable DebtRemediationFunction debtRemediationFunction) { | |||
this.debtRemediationFunction = debtRemediationFunction; | |||
return this; | |||
} | |||
@@ -467,10 +452,8 @@ public interface RulesDefinition extends ServerExtension { | |||
private final Repository repository; | |||
private final String repoKey, key, name, htmlDescription, internalKey, severity; | |||
private final boolean template; | |||
private final String characteristicKey; | |||
private final RemediationFunction remediationFunction; | |||
private final String remediationFactor; | |||
private final String remediationOffset; | |||
private final String debtCharacteristic; | |||
private final DebtRemediationFunction debtRemediationFunction; | |||
private final String effortToFixL10nKey; | |||
private final Set<String> tags; | |||
private final Map<String, Param> params; | |||
@@ -486,10 +469,8 @@ public interface RulesDefinition extends ServerExtension { | |||
this.severity = newRule.severity; | |||
this.template = newRule.template; | |||
this.status = newRule.status; | |||
this.characteristicKey = newRule.characteristicKey; | |||
this.remediationFunction = newRule.remediationFunction; | |||
this.remediationFactor = newRule.remediationFactor; | |||
this.remediationOffset = newRule.remediationOffset; | |||
this.debtCharacteristic = newRule.debtCharacteristic; | |||
this.debtRemediationFunction = newRule.debtRemediationFunction; | |||
this.effortToFixL10nKey = newRule.effortToFixL10nKey; | |||
this.tags = ImmutableSortedSet.copyOf(newRule.tags); | |||
ImmutableMap.Builder<String, Param> paramsBuilder = ImmutableMap.builder(); | |||
@@ -529,23 +510,13 @@ public interface RulesDefinition extends ServerExtension { | |||
} | |||
@CheckForNull | |||
public String characteristicKey() { | |||
return characteristicKey; | |||
} | |||
@CheckForNull | |||
public RemediationFunction remediationFunction() { | |||
return remediationFunction; | |||
} | |||
@CheckForNull | |||
public String remediationFactor() { | |||
return remediationFactor; | |||
public String debtCharacteristic() { | |||
return debtCharacteristic; | |||
} | |||
@CheckForNull | |||
public String remediationOffset() { | |||
return remediationOffset; | |||
public DebtRemediationFunction debtRemediationFunction() { | |||
return debtRemediationFunction; | |||
} | |||
@CheckForNull |
@@ -0,0 +1,146 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.api.server.rule; | |||
import org.junit.Test; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.fest.assertions.Fail.fail; | |||
public class DebtRemediationFunctionTest { | |||
@Test | |||
public void create_linear() throws Exception { | |||
DebtRemediationFunction function = DebtRemediationFunction.createLinear("10h"); | |||
assertThat(function.type()).isEqualTo(DebtRemediationFunction.Type.LINEAR); | |||
assertThat(function.factor()).isEqualTo("10h"); | |||
assertThat(function.offset()).isNull(); | |||
} | |||
@Test | |||
public void create_linear_with_offset() throws Exception { | |||
DebtRemediationFunction function = DebtRemediationFunction.createLinearWithOffset("10h", "5min"); | |||
assertThat(function.type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET); | |||
assertThat(function.factor()).isEqualTo("10h"); | |||
assertThat(function.offset()).isEqualTo("5min"); | |||
} | |||
@Test | |||
public void create_constant_per_issue() throws Exception { | |||
DebtRemediationFunction function = DebtRemediationFunction.createConstantPerIssue("10h"); | |||
assertThat(function.type()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE); | |||
assertThat(function.factor()).isNull(); | |||
assertThat(function.offset()).isEqualTo("10h"); | |||
} | |||
@Test | |||
public void sanitize_remediation_factor_and_offset() { | |||
DebtRemediationFunction function = DebtRemediationFunction.create(DebtRemediationFunction.Type.LINEAR_OFFSET, " 1 h ", " 10 mi n"); | |||
assertThat(function.factor()).isEqualTo("1h"); | |||
assertThat(function.offset()).isEqualTo("10min"); | |||
} | |||
@Test | |||
public void fail_to_create_linear_when_no_factor() throws Exception { | |||
try { | |||
DebtRemediationFunction.create(DebtRemediationFunction.Type.LINEAR, null, "10h"); | |||
fail(); | |||
} catch(Exception e) { | |||
assertThat(e).isInstanceOf(DebtRemediationFunction.ValidationException.class); | |||
} | |||
} | |||
@Test | |||
public void fail_to_create_linear_when_offset() throws Exception { | |||
try { | |||
DebtRemediationFunction.create(DebtRemediationFunction.Type.LINEAR, "5min", "10h"); | |||
fail(); | |||
} catch(Exception e) { | |||
assertThat(e).isInstanceOf(DebtRemediationFunction.ValidationException.class); | |||
} | |||
} | |||
@Test | |||
public void fail_to_create_constant_per_issue_when_no_offset() throws Exception { | |||
try { | |||
DebtRemediationFunction.create(DebtRemediationFunction.Type.CONSTANT_ISSUE, "10h", null); | |||
fail(); | |||
} catch(Exception e) { | |||
assertThat(e).isInstanceOf(DebtRemediationFunction.ValidationException.class); | |||
} | |||
} | |||
@Test | |||
public void fail_to_create_constant_per_issue_when_factor() throws Exception { | |||
try { | |||
DebtRemediationFunction.create(DebtRemediationFunction.Type.CONSTANT_ISSUE, "5min", "10h"); | |||
fail(); | |||
} catch(Exception e) { | |||
assertThat(e).isInstanceOf(DebtRemediationFunction.ValidationException.class); | |||
} | |||
} | |||
@Test | |||
public void fail_to_create_linear_with_offset_when_no_factor() throws Exception { | |||
try { | |||
DebtRemediationFunction.create(DebtRemediationFunction.Type.LINEAR_OFFSET, null, "10h"); | |||
fail(); | |||
} catch(Exception e) { | |||
assertThat(e).isInstanceOf(DebtRemediationFunction.ValidationException.class); | |||
} | |||
} | |||
@Test | |||
public void fail_to_create_linear_with_offset_when_no_offset() throws Exception { | |||
try { | |||
DebtRemediationFunction.create(DebtRemediationFunction.Type.LINEAR_OFFSET, "5min", null); | |||
fail(); | |||
} catch(Exception e) { | |||
assertThat(e).isInstanceOf(DebtRemediationFunction.ValidationException.class); | |||
} | |||
} | |||
@Test | |||
public void test_equals_and_hashcode() throws Exception { | |||
DebtRemediationFunction function = DebtRemediationFunction.createLinearWithOffset("10h", "5min"); | |||
DebtRemediationFunction functionWithSameValue = DebtRemediationFunction.createLinearWithOffset("10h", "5min"); | |||
DebtRemediationFunction functionWithDifferentType = DebtRemediationFunction.createConstantPerIssue("5min"); | |||
assertThat(function).isEqualTo(function); | |||
assertThat(function).isEqualTo(functionWithSameValue); | |||
assertThat(function).isNotEqualTo(functionWithDifferentType); | |||
assertThat(function).isNotEqualTo(DebtRemediationFunction.createLinearWithOffset("11h", "5min")); | |||
assertThat(function).isNotEqualTo(DebtRemediationFunction.createLinearWithOffset("10h", "6min")); | |||
assertThat(function).isNotEqualTo(DebtRemediationFunction.createLinear("10h")); | |||
assertThat(function).isNotEqualTo(DebtRemediationFunction.createConstantPerIssue("6min")); | |||
assertThat(function.hashCode()).isEqualTo(function.hashCode()); | |||
assertThat(function.hashCode()).isEqualTo(functionWithSameValue.hashCode()); | |||
assertThat(function.hashCode()).isNotEqualTo(functionWithDifferentType.hashCode()); | |||
} | |||
@Test | |||
public void test_to_string() throws Exception { | |||
assertThat(DebtRemediationFunction.createLinearWithOffset("10h", "5min").toString()).isNotNull(); | |||
} | |||
} |
@@ -19,9 +19,7 @@ | |||
*/ | |||
package org.sonar.api.server.rule; | |||
import org.junit.Ignore; | |||
import org.junit.Test; | |||
import org.sonar.api.rule.RemediationFunction; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
@@ -72,10 +70,8 @@ public class RulesDefinitionTest { | |||
.setSeverity(Severity.BLOCKER) | |||
.setInternalKey("/something") | |||
.setStatus(RuleStatus.BETA) | |||
.setCharacteristicKey("COMPILER") | |||
.setRemediationFunction(RemediationFunction.LINEAR_OFFSET) | |||
.setRemediationFactor("1h") | |||
.setRemediationOffset("10min") | |||
.setDebtCharacteristic("COMPILER") | |||
.setDebtRemediationFunction(DebtRemediationFunction.create(DebtRemediationFunction.Type.LINEAR_OFFSET, "1h", "10min")) | |||
.setEffortToFixL10nKey("squid.S115.effortToFix") | |||
.setTags("one", "two") | |||
.addTags("two", "three", "four"); | |||
@@ -95,10 +91,8 @@ public class RulesDefinitionTest { | |||
assertThat(npeRule.internalKey()).isEqualTo("/something"); | |||
assertThat(npeRule.template()).isFalse(); | |||
assertThat(npeRule.status()).isEqualTo(RuleStatus.BETA); | |||
assertThat(npeRule.characteristicKey()).isEqualTo("COMPILER"); | |||
assertThat(npeRule.remediationFunction()).isEqualTo(RemediationFunction.LINEAR_OFFSET); | |||
assertThat(npeRule.remediationFactor()).isEqualTo("1h"); | |||
assertThat(npeRule.remediationOffset()).isEqualTo("10min"); | |||
assertThat(npeRule.debtCharacteristic()).isEqualTo("COMPILER"); | |||
assertThat(npeRule.debtRemediationFunction()).isEqualTo(DebtRemediationFunction.create(DebtRemediationFunction.Type.LINEAR_OFFSET, "1h", "10min")); | |||
assertThat(npeRule.effortToFixL10nKey()).isEqualTo("squid.S115.effortToFix"); | |||
assertThat(npeRule.toString()).isEqualTo("[repository=findbugs, key=NPE]"); | |||
assertThat(npeRule.repository()).isSameAs(findbugs); | |||
@@ -122,10 +116,8 @@ public class RulesDefinitionTest { | |||
assertThat(rule.internalKey()).isNull(); | |||
assertThat(rule.status()).isEqualTo(RuleStatus.defaultStatus()); | |||
assertThat(rule.tags()).isEmpty(); | |||
assertThat(rule.characteristicKey()).isNull(); | |||
assertThat(rule.remediationFunction()).isNull(); | |||
assertThat(rule.remediationFactor()).isNull(); | |||
assertThat(rule.remediationOffset()).isNull(); | |||
assertThat(rule.debtCharacteristic()).isNull(); | |||
assertThat(rule.debtRemediationFunction()).isNull(); | |||
} | |||
@Test | |||
@@ -167,21 +159,6 @@ public class RulesDefinitionTest { | |||
assertThat(rule.name()).isEqualTo("NullPointer"); | |||
} | |||
@Test | |||
public void sanitize_remediation_factor_and_offset() { | |||
RulesDefinition.NewRepository newFindbugs = context.createRepository("findbugs", "java"); | |||
newFindbugs.createRule("NPE") | |||
.setName("Detect NPE") | |||
.setHtmlDescription("NPE") | |||
.setRemediationFactor(" 1 h ") | |||
.setRemediationOffset(" 10 mi n "); | |||
newFindbugs.done(); | |||
RulesDefinition.Rule npeRule = context.repository("findbugs").rule("NPE"); | |||
assertThat(npeRule.remediationFactor()).isEqualTo("1h"); | |||
assertThat(npeRule.remediationOffset()).isEqualTo("10min"); | |||
} | |||
@Test | |||
public void extend_repository() { | |||
assertThat(context.extendedRepositories()).isEmpty(); | |||
@@ -276,7 +253,7 @@ public class RulesDefinitionTest { | |||
@Test | |||
public void fail_to_load_rule_description_from_file() { | |||
RulesDefinition.NewRepository newRepository = context.createRepository("findbugs", "java"); | |||
newRepository.createRule("NPE").setName("NPE").setHtmlDescription((URL)null); | |||
newRepository.createRule("NPE").setName("NPE").setHtmlDescription((URL) null); | |||
try { | |||
newRepository.done(); | |||
fail(); | |||
@@ -288,7 +265,7 @@ public class RulesDefinitionTest { | |||
@Test | |||
public void fail_if_blank_rule_html_description() { | |||
RulesDefinition.NewRepository newRepository = context.createRepository("findbugs", "java"); | |||
newRepository.createRule("NPE").setName("NPE").setHtmlDescription((String)null); | |||
newRepository.createRule("NPE").setName("NPE").setHtmlDescription((String) null); | |||
try { | |||
newRepository.done(); | |||
fail(); | |||
@@ -317,22 +294,4 @@ public class RulesDefinitionTest { | |||
} | |||
} | |||
@Test | |||
@Ignore("TODO") | |||
public void fail_if_bad_remediation_factor_or_offset() { | |||
try { | |||
context.createRepository("findbugs", "java").createRule("NPE").setRemediationFactor("ten hours"); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Duration 'ten hours' is invalid, it should use the following sample format : 2d 10h 15min"); | |||
} | |||
try { | |||
context.createRepository("findbugs", "java").createRule("NPE").setRemediationOffset("ten hours"); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Duration 'ten hours' is invalid, it should use the following sample format : 2d 10h 15min"); | |||
} | |||
} | |||
} |
@@ -18,7 +18,7 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.core.technicaldebt; | |||
package org.sonar.server.debt; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.codehaus.stax2.XMLInputFactory2; | |||
@@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory; | |||
import org.sonar.api.ServerExtension; | |||
import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; | |||
import org.sonar.api.utils.ValidationMessages; | |||
import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel; | |||
import javax.xml.stream.XMLInputFactory; | |||
import javax.xml.stream.XMLStreamException; | |||
@@ -98,7 +99,6 @@ public class DebtCharacteristicsXMLImporter implements ServerExtension { | |||
// <chc> can contain characteristics or requirements | |||
} else if (StringUtils.equals(node, CHARACTERISTIC)) { | |||
processCharacteristic(model, characteristic, cursor, messages); | |||
} | |||
} | |||
@@ -18,7 +18,7 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.core.technicaldebt; | |||
package org.sonar.server.debt; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.ibatis.session.SqlSession; | |||
@@ -28,6 +28,8 @@ import org.sonar.api.ServerExtension; | |||
import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; | |||
import org.sonar.api.utils.ValidationMessages; | |||
import org.sonar.core.persistence.MyBatis; | |||
import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel; | |||
import org.sonar.core.technicaldebt.TechnicalDebtModelRepository; | |||
import org.sonar.core.technicaldebt.db.CharacteristicDao; | |||
import org.sonar.core.technicaldebt.db.CharacteristicDto; | |||
@@ -18,7 +18,7 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.core.technicaldebt; | |||
package org.sonar.server.debt; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.base.Strings; | |||
@@ -32,9 +32,10 @@ import org.codehaus.staxmate.in.SMInputCursor; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.ServerExtension; | |||
import org.sonar.api.rule.RemediationFunction; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.server.rule.DebtRemediationFunction; | |||
import org.sonar.api.utils.Duration; | |||
import org.sonar.api.utils.MessageException; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
@@ -123,10 +124,9 @@ public class DebtRulesXMLImporter implements ServerExtension { | |||
} | |||
} | |||
private RuleDebt processRule(SMInputCursor cursor) | |||
throws XMLStreamException { | |||
@CheckForNull | |||
private RuleDebt processRule(SMInputCursor cursor) throws XMLStreamException { | |||
RuleDebt ruleDebt = new RuleDebt(); | |||
String ruleRepositoryKey = cursor.collectDescendantText().trim(); | |||
String ruleKey = null; | |||
Properties properties = new Properties(); | |||
@@ -139,11 +139,9 @@ public class DebtRulesXMLImporter implements ServerExtension { | |||
} | |||
} | |||
if (StringUtils.isNotBlank(ruleRepositoryKey) && StringUtils.isNotBlank(ruleKey)) { | |||
ruleDebt.ruleKey = RuleKey.of(ruleRepositoryKey, ruleKey); | |||
} else { | |||
return null; | |||
return processRule(RuleKey.of(ruleRepositoryKey, ruleKey), properties); | |||
} | |||
return processFunctionsOnRequirement(ruleDebt, properties); | |||
return null; | |||
} | |||
private Property processProperty(SMInputCursor cursor) throws XMLStreamException { | |||
@@ -166,43 +164,42 @@ public class DebtRulesXMLImporter implements ServerExtension { | |||
} | |||
} else if (StringUtils.equals(node, PROPERTY_TEXT_VALUE)) { | |||
textValue = c.collectDescendantText().trim(); | |||
textValue = "mn".equals(textValue) ? Duration.MINUTE : textValue; | |||
} | |||
} | |||
return new Property(key, value, textValue); | |||
} | |||
@CheckForNull | |||
private RuleDebt processFunctionsOnRequirement(RuleDebt requirement, Properties properties) { | |||
Property function = properties.function(); | |||
Property factor = properties.factor(); | |||
Property offset = properties.offset(); | |||
private RuleDebt processRule(RuleKey ruleKey, Properties properties){ | |||
try { | |||
return createRule(ruleKey, properties); | |||
} catch (DebtRemediationFunction.ValidationException e) { | |||
throw MessageException.of(String.format("Rule '%s' is invalid : %s", ruleKey, e.getMessage())); | |||
} | |||
} | |||
@CheckForNull | |||
private RuleDebt createRule(RuleKey ruleKey, Properties properties) { | |||
Property function = properties.function(); | |||
if (function != null) { | |||
// Init with default values | |||
requirement.factor = "0" + Duration.DAY; | |||
requirement.offset = "0" + Duration.DAY; | |||
Property factorProperty = properties.factor(); | |||
String factor = factorProperty != null ? factorProperty.toDuration() : null; | |||
Property offsetProperty = properties.offset(); | |||
String offset = offsetProperty != null ? offsetProperty.toDuration() : null; | |||
String functionKey = function.getTextValue(); | |||
if ("linear_threshold".equals(functionKey)) { | |||
function.setTextValue(RemediationFunction.LINEAR.name().toLowerCase()); | |||
offset.setValue(0); | |||
offset.setTextValue(Duration.DAY); | |||
LOG.warn(String.format("Linear with threshold function is no longer used, remediation function of '%s' is replaced by linear.", requirement.ruleKey)); | |||
if ("linear_threshold".equals(functionKey) && factor != null) { | |||
LOG.warn(String.format("Linear with threshold function is no longer used, remediation function of '%s' is replaced by linear.", ruleKey)); | |||
return new RuleDebt().setRuleKey(ruleKey).setFunction(DebtRemediationFunction.createLinear(factor)); | |||
} else if ("constant_resource".equals(functionKey)) { | |||
LOG.warn(String.format("Constant/file function is no longer used, technical debt definitions on '%s' are ignored.", requirement.ruleKey)); | |||
return null; | |||
} | |||
requirement.function = RemediationFunction.valueOf(function.getTextValue().toUpperCase()); | |||
if (factor != null) { | |||
requirement.factor = Integer.toString(factor.getValue()); | |||
requirement.factor += !Strings.isNullOrEmpty(factor.getTextValue()) ? factor.getTextValue() : Duration.DAY; | |||
LOG.warn(String.format("Constant/file function is no longer used, technical debt definitions on '%s' are ignored.", ruleKey)); | |||
} else if (DebtRemediationFunction.Type.CONSTANT_ISSUE.name().toLowerCase().equals(functionKey) && factor != null && offset == null) { | |||
return new RuleDebt().setRuleKey(ruleKey).setFunction(DebtRemediationFunction.createConstantPerIssue(factor)); | |||
} else { | |||
return new RuleDebt().setRuleKey(ruleKey).setFunction(DebtRemediationFunction.create(DebtRemediationFunction.Type.valueOf(functionKey.toUpperCase()), factor, offset)); | |||
} | |||
if (offset != null) { | |||
requirement.offset = Integer.toString(offset.getValue()); | |||
requirement.offset += !Strings.isNullOrEmpty(offset.getTextValue()) ? offset.getTextValue() : Duration.DAY; | |||
} | |||
return requirement; | |||
} | |||
return null; | |||
} | |||
@@ -252,16 +249,6 @@ public class DebtRulesXMLImporter implements ServerExtension { | |||
this.textValue = textValue; | |||
} | |||
private Property setValue(int value) { | |||
this.value = value; | |||
return this; | |||
} | |||
private Property setTextValue(String textValue) { | |||
this.textValue = textValue; | |||
return this; | |||
} | |||
private String getKey() { | |||
return key; | |||
} | |||
@@ -271,16 +258,24 @@ public class DebtRulesXMLImporter implements ServerExtension { | |||
} | |||
private String getTextValue() { | |||
return "mn".equals(textValue) ? Duration.MINUTE : textValue; | |||
return textValue; | |||
} | |||
@CheckForNull | |||
public String toDuration() { | |||
if (key != null && getValue() > 0) { | |||
String duration = Integer.toString(getValue()); | |||
duration += !Strings.isNullOrEmpty(getTextValue()) ? getTextValue() : Duration.DAY; | |||
return duration; | |||
} | |||
return null; | |||
} | |||
} | |||
public static class RuleDebt { | |||
private RuleKey ruleKey; | |||
private String characteristicKey; | |||
private RemediationFunction function; | |||
private String factor; | |||
private String offset; | |||
private DebtRemediationFunction function; | |||
public RuleKey ruleKey() { | |||
return ruleKey; | |||
@@ -300,32 +295,14 @@ public class DebtRulesXMLImporter implements ServerExtension { | |||
return this; | |||
} | |||
public RemediationFunction function() { | |||
public DebtRemediationFunction function() { | |||
return function; | |||
} | |||
public RuleDebt setFunction(RemediationFunction function) { | |||
public RuleDebt setFunction(DebtRemediationFunction function) { | |||
this.function = function; | |||
return this; | |||
} | |||
public String factor() { | |||
return factor; | |||
} | |||
public RuleDebt setFactor(String factor) { | |||
this.factor = factor; | |||
return this; | |||
} | |||
public String offset() { | |||
return offset; | |||
} | |||
public RuleDebt setOffset(String offset) { | |||
this.offset = offset; | |||
return this; | |||
} | |||
} | |||
} |
@@ -18,7 +18,7 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.technicaldebt; | |||
package org.sonar.server.debt; | |||
import org.sonar.api.ServerComponent; | |||
import org.sonar.api.technicaldebt.server.Characteristic; |
@@ -18,6 +18,6 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.server.technicaldebt; | |||
package org.sonar.server.debt; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -63,7 +63,10 @@ import org.sonar.core.qualitygate.db.QualityGateConditionDao; | |||
import org.sonar.core.qualitygate.db.QualityGateDao; | |||
import org.sonar.core.resource.DefaultResourcePermissions; | |||
import org.sonar.core.rule.DefaultRuleFinder; | |||
import org.sonar.core.technicaldebt.*; | |||
import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager; | |||
import org.sonar.core.technicaldebt.TechnicalDebtModelRepository; | |||
import org.sonar.core.technicaldebt.TechnicalDebtModelSynchronizer; | |||
import org.sonar.core.technicaldebt.TechnicalDebtXMLImporter; | |||
import org.sonar.core.test.TestPlanPerspectiveLoader; | |||
import org.sonar.core.test.TestablePerspectiveLoader; | |||
import org.sonar.core.timemachine.Periods; | |||
@@ -83,6 +86,10 @@ import org.sonar.server.db.EmbeddedDatabaseFactory; | |||
import org.sonar.server.db.migrations.DatabaseMigration; | |||
import org.sonar.server.db.migrations.DatabaseMigrations; | |||
import org.sonar.server.db.migrations.DatabaseMigrator; | |||
import org.sonar.server.debt.DebtCharacteristicsXMLImporter; | |||
import org.sonar.server.debt.DebtModelSynchronizer; | |||
import org.sonar.server.debt.DebtRulesXMLImporter; | |||
import org.sonar.server.debt.DebtService; | |||
import org.sonar.server.es.ESIndex; | |||
import org.sonar.server.es.ESNode; | |||
import org.sonar.server.issue.*; | |||
@@ -110,7 +117,6 @@ import org.sonar.server.source.SourceService; | |||
import org.sonar.server.source.ws.SourcesShowWsHandler; | |||
import org.sonar.server.source.ws.SourcesWs; | |||
import org.sonar.server.startup.*; | |||
import org.sonar.server.technicaldebt.DebtService; | |||
import org.sonar.server.text.MacroInterpreter; | |||
import org.sonar.server.text.RubyTextService; | |||
import org.sonar.server.ui.JRubyI18n; |
@@ -31,8 +31,8 @@ import org.sonar.api.server.rule.RuleParamType; | |||
import org.sonar.api.server.rule.RulesDefinition; | |||
import org.sonar.check.Cardinality; | |||
import org.sonar.core.i18n.RuleI18nManager; | |||
import org.sonar.core.technicaldebt.DebtRulesXMLImporter; | |||
import org.sonar.core.technicaldebt.TechnicalDebtModelRepository; | |||
import org.sonar.server.debt.DebtRulesXMLImporter; | |||
import javax.annotation.CheckForNull; | |||
@@ -104,10 +104,8 @@ public class DeprecatedRulesDefinition implements RulesDefinition { | |||
private void updateRuleDebtDefinitions(NewRule newRule, String repoKey, String ruleKey, List<DebtRulesXMLImporter.RuleDebt> ruleDebts){ | |||
DebtRulesXMLImporter.RuleDebt ruleDebt = findRequirement(ruleDebts, repoKey, ruleKey); | |||
if (ruleDebt != null) { | |||
newRule.setCharacteristicKey(ruleDebt.characteristicKey()); | |||
newRule.setRemediationFunction(ruleDebt.function()); | |||
newRule.setRemediationFactor(ruleDebt.factor()); | |||
newRule.setRemediationOffset(ruleDebt.offset()); | |||
newRule.setDebtCharacteristic(ruleDebt.characteristicKey()); | |||
newRule.setDebtRemediationFunction(ruleDebt.function()); | |||
} | |||
} | |||
@@ -28,9 +28,9 @@ import org.apache.ibatis.session.SqlSession; | |||
import org.picocontainer.Startable; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.rule.RemediationFunction; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rules.Rule; | |||
import org.sonar.api.server.rule.DebtRemediationFunction; | |||
import org.sonar.api.server.rule.RulesDefinition; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.api.utils.System2; | |||
@@ -165,8 +165,6 @@ public class RuleRegistration implements Startable { | |||
} | |||
private RuleDto enableAndInsert(Buffer buffer, SqlSession sqlSession, RulesDefinition.Rule ruleDef, List<CharacteristicDto> characteristicDtos) { | |||
RemediationFunction remediationFunction = ruleDef.remediationFunction(); | |||
RuleDto ruleDto = new RuleDto() | |||
.setCardinality(ruleDef.template() ? Cardinality.MULTIPLE : Cardinality.SINGLE) | |||
.setConfigKey(ruleDef.internalKey()) | |||
@@ -181,12 +179,14 @@ public class RuleRegistration implements Startable { | |||
.setStatus(ruleDef.status().name()); | |||
CharacteristicDto characteristic = findCharacteristic(characteristicDtos, ruleDef); | |||
if (characteristic != null) { | |||
ruleDto.setDefaultCharacteristicId(characteristic.getId()) | |||
.setDefaultRemediationFunction(remediationFunction != null ? remediationFunction.name() : null) | |||
.setDefaultRemediationFactor(ruleDef.remediationFactor()) | |||
.setDefaultRemediationOffset(ruleDef.remediationOffset()) | |||
.setEffortToFixL10nKey(ruleDef.effortToFixL10nKey()); | |||
ruleDto.setDefaultCharacteristicId(characteristic != null ? characteristic.getId() : null) | |||
.setEffortToFixL10nKey(ruleDef.effortToFixL10nKey()); | |||
DebtRemediationFunction remediationFunction = ruleDef.debtRemediationFunction(); | |||
if (remediationFunction != null) { | |||
ruleDto.setDefaultRemediationFunction(remediationFunction.type().name()); | |||
ruleDto.setDefaultRemediationFactor(remediationFunction.factor()); | |||
ruleDto.setDefaultRemediationOffset(remediationFunction.offset()); | |||
} | |||
ruleDao.insert(ruleDto, sqlSession); | |||
@@ -261,16 +261,16 @@ public class RuleRegistration implements Startable { | |||
CharacteristicDto characteristic = findCharacteristic(characteristicDtos, def); | |||
// Debt definitions are set to null if the characteristic is null or unknown | |||
Integer characteristicId = characteristic != null ? characteristic.getId() : null; | |||
RemediationFunction remediationFunction = characteristic != null ? def.remediationFunction() : null; | |||
String remediationFactor = characteristic != null ? def.remediationFactor() : null; | |||
String remediationOffset = characteristic != null ? def.remediationOffset() : null; | |||
String effortToFixL10nKey = characteristic != null ? def.effortToFixL10nKey() : null; | |||
DebtRemediationFunction debtRemediationFunction = def.debtRemediationFunction(); | |||
String remediationFactor = debtRemediationFunction != null ? debtRemediationFunction.factor() : null; | |||
String remediationOffset = debtRemediationFunction != null ? debtRemediationFunction.offset() : null; | |||
String effortToFixL10nKey = def.effortToFixL10nKey(); | |||
if (!ObjectUtils.equals(dto.getDefaultCharacteristicId(), characteristicId)) { | |||
dto.setDefaultCharacteristicId(characteristicId); | |||
changed = true; | |||
} | |||
String remediationFunctionString = remediationFunction != null ? remediationFunction.name() : null; | |||
String remediationFunctionString = debtRemediationFunction != null ? debtRemediationFunction.type().name() : null; | |||
if (!StringUtils.equals(dto.getDefaultRemediationFunction(), remediationFunctionString)) { | |||
dto.setDefaultRemediationFunction(remediationFunctionString); | |||
changed = true; | |||
@@ -539,7 +539,7 @@ public class RuleRegistration implements Startable { | |||
@CheckForNull | |||
private CharacteristicDto findCharacteristic(List<CharacteristicDto> characteristicDtos, RulesDefinition.Rule ruleDef) { | |||
final String key = ruleDef.characteristicKey(); | |||
final String key = ruleDef.debtCharacteristic(); | |||
if (key == null) { | |||
// Rule is not linked to a characteristic, nothing to do | |||
return null; | |||
@@ -553,7 +553,7 @@ public class RuleRegistration implements Startable { | |||
}, null); | |||
if (characteristicDto == null) { | |||
LOG.warn(String.format("Characteristic '%s' has not been found, technical debt definitions on rule '%s:%s' will be ignored", | |||
LOG.warn(String.format("Characteristic '%s' has not been found on rule '%s:%s'", | |||
key, ruleDef.repository().name(), ruleDef.key())); | |||
} else if (characteristicDto.getParentId() == null) { | |||
throw MessageException.of(String.format("Rule '%s:%s' cannot be linked on the root characteristic '%s'", |
@@ -49,6 +49,8 @@ import java.util.Collection; | |||
import java.util.List; | |||
/** | |||
* This script copy every requirements from characteristics table (every row where rule_id is not null) to the rules table. | |||
* | |||
* This script need to be executed after rules registration because default debt columns (characteristics, function, factor and offset) has to be populated | |||
* in order to be able to compare default values with overridden values. | |||
* |
@@ -24,7 +24,7 @@ import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.utils.TimeProfiler; | |||
import org.sonar.api.utils.ValidationMessages; | |||
import org.sonar.core.technicaldebt.DebtModelSynchronizer; | |||
import org.sonar.server.debt.DebtModelSynchronizer; | |||
public class RegisterDebtModel { | |||
@@ -55,7 +55,7 @@ class Internal | |||
end | |||
def self.debt | |||
component(Java::OrgSonarServerTechnicaldebt::DebtService.java_class) | |||
component(Java::OrgSonarServerDebt::DebtService.java_class) | |||
end | |||
def self.profiling |
@@ -18,13 +18,14 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.core.technicaldebt; | |||
package org.sonar.server.debt; | |||
import com.google.common.base.Charsets; | |||
import com.google.common.io.Resources; | |||
import org.junit.Test; | |||
import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; | |||
import org.sonar.api.utils.ValidationMessages; | |||
import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel; | |||
import java.io.IOException; | |||
@@ -18,7 +18,7 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.core.technicaldebt; | |||
package org.sonar.server.debt; | |||
import com.google.common.collect.Lists; | |||
import org.apache.ibatis.session.SqlSession; | |||
@@ -33,6 +33,8 @@ import org.mockito.stubbing.Answer; | |||
import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; | |||
import org.sonar.api.utils.ValidationMessages; | |||
import org.sonar.core.persistence.MyBatis; | |||
import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel; | |||
import org.sonar.core.technicaldebt.TechnicalDebtModelRepository; | |||
import org.sonar.core.technicaldebt.db.CharacteristicDao; | |||
import org.sonar.core.technicaldebt.db.CharacteristicDto; | |||
@@ -18,18 +18,20 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.core.technicaldebt; | |||
package org.sonar.server.debt; | |||
import com.google.common.base.Charsets; | |||
import com.google.common.io.Resources; | |||
import org.junit.Test; | |||
import org.sonar.api.rule.RemediationFunction; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.server.rule.DebtRemediationFunction; | |||
import org.sonar.api.utils.MessageException; | |||
import java.io.IOException; | |||
import java.util.List; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.fest.assertions.Fail.fail; | |||
public class DebtRulesXMLImporterTest { | |||
@@ -53,9 +55,20 @@ public class DebtRulesXMLImporterTest { | |||
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0); | |||
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); | |||
assertThat(ruleDebt.ruleKey()).isEqualTo(RuleKey.of("checkstyle", "Regexp")); | |||
assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR); | |||
assertThat(ruleDebt.factor()).isEqualTo("3h"); | |||
assertThat(ruleDebt.offset()).isEqualTo("0d"); | |||
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.createLinear("3h")); | |||
} | |||
@Test | |||
public void import_linear_having_offset_to_zero() { | |||
String xml = getFileContent("import_linear_having_offset_to_zero.xml"); | |||
List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml); | |||
assertThat(results).hasSize(1); | |||
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0); | |||
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); | |||
assertThat(ruleDebt.ruleKey()).isEqualTo(RuleKey.of("checkstyle", "Regexp")); | |||
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.createLinear("3h")); | |||
} | |||
@Test | |||
@@ -67,9 +80,7 @@ public class DebtRulesXMLImporterTest { | |||
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0); | |||
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); | |||
assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR_OFFSET); | |||
assertThat(ruleDebt.factor()).isEqualTo("3h"); | |||
assertThat(ruleDebt.offset()).isEqualTo("1min"); | |||
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.createLinearWithOffset("3h", "1min")); | |||
} | |||
@Test | |||
@@ -81,9 +92,7 @@ public class DebtRulesXMLImporterTest { | |||
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0); | |||
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); | |||
assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.CONSTANT_ISSUE); | |||
assertThat(ruleDebt.factor()).isEqualTo("0d"); | |||
assertThat(ruleDebt.offset()).isEqualTo("3d"); | |||
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.createConstantPerIssue("3d")); | |||
} | |||
@Test | |||
@@ -95,9 +104,7 @@ public class DebtRulesXMLImporterTest { | |||
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0); | |||
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); | |||
assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR); | |||
assertThat(ruleDebt.factor()).isEqualTo("3d"); | |||
assertThat(ruleDebt.offset()).isEqualTo("1d"); | |||
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.createLinearWithOffset("3d", "1d")); | |||
} | |||
@Test | |||
@@ -109,9 +116,7 @@ public class DebtRulesXMLImporterTest { | |||
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0); | |||
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); | |||
assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR); | |||
assertThat(ruleDebt.factor()).isEqualTo("3min"); | |||
assertThat(ruleDebt.offset()).isEqualTo("0d"); | |||
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.createLinear("3min")); | |||
} | |||
@Test | |||
@@ -123,9 +128,19 @@ public class DebtRulesXMLImporterTest { | |||
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0); | |||
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); | |||
assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR); | |||
assertThat(ruleDebt.factor()).isEqualTo("3h"); | |||
assertThat(ruleDebt.offset()).isEqualTo("0d"); | |||
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.createLinear("3h")); | |||
} | |||
@Test | |||
public void convert_constant_per_issue_with_factor_by_constant_by_issue_with_offset() { | |||
String xml = getFileContent("convert_constant_per_issue_with_factor_by_constant_by_issue_with_offset.xml"); | |||
List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml); | |||
assertThat(results).hasSize(1); | |||
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0); | |||
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); | |||
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.createConstantPerIssue("3h")); | |||
} | |||
@Test | |||
@@ -154,9 +169,7 @@ public class DebtRulesXMLImporterTest { | |||
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0); | |||
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); | |||
assertThat(ruleDebt.ruleKey()).isEqualTo(RuleKey.of("checkstyle", "Regexp")); | |||
assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR); | |||
assertThat(ruleDebt.factor()).isEqualTo("3h"); | |||
assertThat(ruleDebt.offset()).isEqualTo("0d"); | |||
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.createLinear("3h")); | |||
} | |||
@Test | |||
@@ -166,6 +179,17 @@ public class DebtRulesXMLImporterTest { | |||
assertThat(results).isEmpty(); | |||
} | |||
@Test | |||
public void fail_to_import_linear_having_offset() throws Exception { | |||
String xml = getFileContent("fail_to_import_linear_having_offset.xml"); | |||
try { | |||
importer.importXML(xml); | |||
fail(); | |||
} catch (Exception e) { | |||
assertThat(e).isInstanceOf(MessageException.class); | |||
} | |||
} | |||
private String getFileContent(String file) { | |||
try { | |||
return Resources.toString(Resources.getResource(DebtRulesXMLImporterTest.class, "DebtRulesXMLImporterTest/" + file), Charsets.UTF_8); |
@@ -17,7 +17,7 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.technicaldebt; | |||
package org.sonar.server.debt; | |||
import org.junit.Before; | |||
import org.junit.Test; |
@@ -23,17 +23,17 @@ import org.junit.Test; | |||
import org.junit.runner.RunWith; | |||
import org.mockito.Mock; | |||
import org.mockito.runners.MockitoJUnitRunner; | |||
import org.sonar.api.rule.RemediationFunction; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
import org.sonar.api.rules.Rule; | |||
import org.sonar.api.rules.RulePriority; | |||
import org.sonar.api.rules.RuleRepository; | |||
import org.sonar.api.server.rule.DebtRemediationFunction; | |||
import org.sonar.api.server.rule.RulesDefinition; | |||
import org.sonar.core.i18n.RuleI18nManager; | |||
import org.sonar.core.technicaldebt.DebtRulesXMLImporter; | |||
import org.sonar.core.technicaldebt.TechnicalDebtModelRepository; | |||
import org.sonar.server.debt.DebtRulesXMLImporter; | |||
import java.io.Reader; | |||
import java.util.Arrays; | |||
@@ -158,10 +158,8 @@ public class DeprecatedRulesDefinitionTest { | |||
new DebtRulesXMLImporter.RuleDebt() | |||
.setCharacteristicKey("MEMORY_EFFICIENCY") | |||
.setRuleKey(RuleKey.of("checkstyle", "ConstantName")) | |||
.setFunction(RemediationFunction.LINEAR_OFFSET) | |||
.setFactor("1d") | |||
.setOffset("10min") | |||
); | |||
.setFunction(DebtRemediationFunction.createLinearWithOffset("1d", "10min") | |||
)); | |||
Reader javaModelReader = mock(Reader.class); | |||
when(debtModelRepository.createReaderForXMLFile("java")).thenReturn(javaModelReader); | |||
@@ -177,10 +175,8 @@ public class DeprecatedRulesDefinitionTest { | |||
RulesDefinition.Rule rule = checkstyle.rule("ConstantName"); | |||
assertThat(rule).isNotNull(); | |||
assertThat(rule.key()).isEqualTo("ConstantName"); | |||
assertThat(rule.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); | |||
assertThat(rule.remediationFunction()).isEqualTo(RemediationFunction.LINEAR_OFFSET); | |||
assertThat(rule.remediationFactor()).isEqualTo("1d"); | |||
assertThat(rule.remediationOffset()).isEqualTo("10min"); | |||
assertThat(rule.debtCharacteristic()).isEqualTo("MEMORY_EFFICIENCY"); | |||
assertThat(rule.debtRemediationFunction()).isEqualTo(DebtRemediationFunction.createLinearWithOffset("1d", "10min")); | |||
} | |||
} |
@@ -22,9 +22,9 @@ package org.sonar.server.rule; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.sonar.api.rule.RemediationFunction; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
import org.sonar.api.server.rule.DebtRemediationFunction; | |||
import org.sonar.api.server.rule.RulesDefinition; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.core.persistence.AbstractDaoTestCase; | |||
@@ -168,11 +168,11 @@ public class RuleRegistrationTest extends AbstractDaoTestCase { | |||
} | |||
@Test | |||
public void remove_rule_debt_definitions_if_characteristic_not_found() { | |||
setupData("remove_rule_debt_definitions_if_characteristic_not_found"); | |||
public void set_no_characteristic_when_characteristic_not_found() { | |||
setupData("set_no_characteristic_when_characteristic_not_found"); | |||
task.start(); | |||
checkTables("remove_rule_debt_definitions_if_characteristic_not_found", EXCLUDED_COLUMN_NAMES, "rules"); | |||
checkTables("set_no_characteristic_when_characteristic_not_found", EXCLUDED_COLUMN_NAMES, "rules"); | |||
} | |||
@Test | |||
@@ -247,10 +247,8 @@ public class RuleRegistrationTest extends AbstractDaoTestCase { | |||
.setName("One") | |||
.setHtmlDescription("Description of One") | |||
.setSeverity(Severity.BLOCKER) | |||
.setCharacteristicKey("MEMORY_EFFICIENCY") | |||
.setRemediationFunction(RemediationFunction.LINEAR_OFFSET) | |||
.setRemediationFactor("5d") | |||
.setRemediationOffset("10h") | |||
.setDebtCharacteristic("MEMORY_EFFICIENCY") | |||
.setDebtRemediationFunction(DebtRemediationFunction.createLinearWithOffset("5d", "10h")) | |||
.setEffortToFixL10nKey("squid.S115.effortToFix") | |||
.setInternalKey("config1") | |||
.setTags("tag1", "tag3", "tag5"); |
@@ -22,7 +22,7 @@ package org.sonar.server.startup; | |||
import org.junit.Test; | |||
import org.sonar.api.utils.ValidationMessages; | |||
import org.sonar.core.technicaldebt.DebtModelSynchronizer; | |||
import org.sonar.server.debt.DebtModelSynchronizer; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Mockito.*; |
@@ -0,0 +1,50 @@ | |||
<!-- | |||
~ SonarQube, open source software quality management tool. | |||
~ Copyright (C) 2008-2014 SonarSource | |||
~ mailto:contact AT sonarsource DOT com | |||
~ | |||
~ SonarQube is free software; you can redistribute it and/or | |||
~ modify it under the terms of the GNU Lesser General Public | |||
~ License as published by the Free Software Foundation; either | |||
~ version 3 of the License, or (at your option) any later version. | |||
~ | |||
~ SonarQube is distributed in the hope that it will be useful, | |||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
~ Lesser General Public License for more details. | |||
~ | |||
~ You should have received a copy of the GNU Lesser General Public License | |||
~ along with this program; if not, write to the Free Software Foundation, | |||
~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
--> | |||
<sqale> | |||
<chc> | |||
<key>USABILITY</key> | |||
<name>Usability</name> | |||
<desc>Estimate usability</desc> | |||
</chc> | |||
<chc> | |||
<key>EFFICIENCY</key> | |||
<name>Efficiency</name> | |||
<chc> | |||
<key>MEMORY_EFFICIENCY</key> | |||
<name>Memory use</name> | |||
<chc> | |||
<rule-repo>checkstyle</rule-repo> | |||
<rule-key>Regexp</rule-key> | |||
<prop> | |||
<key>remediationFunction</key> | |||
<txt>constant_issue</txt> | |||
</prop> | |||
<prop> | |||
<!-- Should be replaced by offset --> | |||
<key>remediationFactor</key> | |||
<val>3.0</val> | |||
<txt>h</txt> | |||
</prop> | |||
</chc> | |||
</chc> | |||
</chc> | |||
</sqale> |
@@ -0,0 +1,49 @@ | |||
<!-- | |||
~ SonarQube, open source software quality management tool. | |||
~ Copyright (C) 2008-2014 SonarSource | |||
~ mailto:contact AT sonarsource DOT com | |||
~ | |||
~ SonarQube is free software; you can redistribute it and/or | |||
~ modify it under the terms of the GNU Lesser General Public | |||
~ License as published by the Free Software Foundation; either | |||
~ version 3 of the License, or (at your option) any later version. | |||
~ | |||
~ SonarQube is distributed in the hope that it will be useful, | |||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
~ Lesser General Public License for more details. | |||
~ | |||
~ You should have received a copy of the GNU Lesser General Public License | |||
~ along with this program; if not, write to the Free Software Foundation, | |||
~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
--> | |||
<sqale> | |||
<chc> | |||
<key>USABILITY</key> | |||
<name>Usability</name> | |||
<desc>Estimate usability</desc> | |||
</chc> | |||
<chc> | |||
<key>EFFICIENCY</key> | |||
<name>Efficiency</name> | |||
<chc> | |||
<key>MEMORY_EFFICIENCY</key> | |||
<name>Memory use</name> | |||
<chc> | |||
<rule-repo>checkstyle</rule-repo> | |||
<rule-key>Regexp</rule-key> | |||
<prop> | |||
<key>offset</key> | |||
<val>3.0</val> | |||
<txt>h</txt> | |||
</prop> | |||
<prop> | |||
<key>remediationFunction</key> | |||
<txt>linear</txt> | |||
</prop> | |||
</chc> | |||
</chc> | |||
</chc> | |||
</sqale> |
@@ -0,0 +1,34 @@ | |||
<sqale> | |||
<chc> | |||
<key>USABILITY</key> | |||
<name>Usability</name> | |||
<desc>Estimate usability</desc> | |||
</chc> | |||
<chc> | |||
<key>EFFICIENCY</key> | |||
<name>Efficiency</name> | |||
<chc> | |||
<key>MEMORY_EFFICIENCY</key> | |||
<name>Memory use</name> | |||
<chc> | |||
<rule-repo>checkstyle</rule-repo> | |||
<rule-key>Regexp</rule-key> | |||
<prop> | |||
<key>remediationFactor</key> | |||
<val>3.0</val> | |||
<txt>h</txt> | |||
</prop> | |||
<prop> | |||
<key>remediationFunction</key> | |||
<txt>linear</txt> | |||
</prop> | |||
<prop> | |||
<key>offset</key> | |||
<val>0.0</val> | |||
<txt>min</txt> | |||
</prop> | |||
</chc> | |||
</chc> | |||
</chc> | |||
</sqale> |
@@ -45,7 +45,7 @@ | |||
</prop> | |||
<prop> | |||
<key>remediationFunction</key> | |||
<txt>linear</txt> | |||
<txt>linear_offset</txt> | |||
</prop> | |||
<prop> | |||
<key>offset</key> |
@@ -19,7 +19,7 @@ | |||
</prop> | |||
<prop> | |||
<key>remediationFunction</key> | |||
<txt>linear</txt> | |||
<txt>linear_offset</txt> | |||
</prop> | |||
<prop> | |||
<key>offset</key> |
@@ -1,13 +1,12 @@ | |||
<dataset> | |||
<!-- All debt parameters are reset as the characteristic MEMORY_EFFICIENCY do not exists --> | |||
<rules id="1" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="config1" name="One" description="Description of One" | |||
status="READY" priority="4" cardinality="SINGLE" parent_id="[null]" language="java" | |||
characteristic_id="[null]" default_characteristic_id="[null]" | |||
remediation_function="[null]" default_remediation_function="[null]" | |||
remediation_factor="[null]" default_remediation_factor="[null]" | |||
remediation_offset="[null]" default_remediation_offset="[null]" | |||
effort_to_fix_l10n_key="[null]"/> | |||
remediation_function="[null]" default_remediation_function="LINEAR_OFFSET" | |||
remediation_factor="[null]" default_remediation_factor="5d" | |||
remediation_offset="[null]" default_remediation_offset="10h" | |||
effort_to_fix_l10n_key="squid.S115.effortToFix"/> | |||
<rules id="2" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" | |||
status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java" |