public List<RuleDto> selectEnablesAndNonManual() {
SqlSession session = mybatis.openSession();
try {
- return getMapper(session).selectEnablesAndNonManual();
+ return selectEnablesAndNonManual(session);
} finally {
MyBatis.closeQuietly(session);
}
}
+ public List<RuleDto> selectEnablesAndNonManual(SqlSession session) {
+ return getMapper(session).selectEnablesAndNonManual();
+ }
+
public List<RuleDto> selectNonManual(SqlSession session) {
return getMapper(session).selectNonManual();
}
return getMapper(session).selectBySubCharacteristicId(characteristicOrSubCharacteristicId);
}
- public List<RuleDto> selectOverridingDebt(List<String> repositories) {
- SqlSession session = mybatis.openSession();
- try {
- return selectOverridingDebt(repositories, session);
- } finally {
- MyBatis.closeQuietly(session);
- }
- }
-
- public List<RuleDto> selectOverridingDebt(List<String> repositories, SqlSession session) {
- return getMapper(session).selectOverridingDebt(repositories);
- }
-
@CheckForNull
public RuleDto selectById(Integer id, SqlSession session) {
return getMapper(session).selectById(id);
List<RuleDto> selectBySubCharacteristicId(int characteristicId);
- List<RuleDto> selectOverridingDebt(@Param("repositories") List<String> repositories);
-
RuleDto selectById(Integer id);
RuleDto selectByName(String name);
return session.getMapper(CharacteristicMapper.class).selectByName(name);
}
- @CheckForNull
- public CharacteristicDto selectNext(int order, SqlSession session) {
- List<CharacteristicDto> dtos = session.getMapper(CharacteristicMapper.class).selectNext(order);
- return dtos.isEmpty() ? null : dtos.get(0);
- }
-
- @CheckForNull
- public CharacteristicDto selectNext(int order) {
- SqlSession session = mybatis.openSession();
- try {
- return selectNext(order, session);
- } finally {
- MyBatis.closeQuietly(session);
- }
- }
-
- @CheckForNull
- public CharacteristicDto selectPrevious(int order, SqlSession session) {
- List<CharacteristicDto> dtos = session.getMapper(CharacteristicMapper.class).selectPrevious(order);
- return dtos.isEmpty() ? null : dtos.get(0);
- }
-
- @CheckForNull
- public CharacteristicDto selectPrevious(int order) {
- SqlSession session = mybatis.openSession();
- try {
- return selectPrevious(order, session);
- } finally {
- MyBatis.closeQuietly(session);
- }
- }
-
public int selectMaxCharacteristicOrder() {
SqlSession session = mybatis.openSession();
try {
CharacteristicDto selectByName(String name);
- List<CharacteristicDto> selectNext(int order);
-
- List<CharacteristicDto> selectPrevious(int order);
-
Integer selectMaxCharacteristicOrder();
void insert(CharacteristicDto characteristic);
</where>
</select>
- <select id="selectOverridingDebt" resultType="Rule">
- SELECT <include refid="selectColumns"/> FROM rules r
- <where>
- AND (r.characteristic_id is NOT NULL or r.remediation_function IS NOT NULL)
- <if test="repositories.size()>0">
- AND
- <foreach item="repo" index="index" collection="repositories" open="(" separator=" or " close=")">
- r.plugin_name=#{repo}
- </foreach>
- </if>
- </where>
- </select>
-
<update id="update" parameterType="Rule">
UPDATE rules SET
plugin_rule_key=#{ruleKey},
</where>
</select>
- <select id="selectNext" parameterType="Integer" resultType="Characteristic">
- select <include refid="characteristicColumns"/>
- from characteristics c
- <where>
- and c.characteristic_order>#{order}
- and c.parent_id is null
- and c.enabled=${_true}
- </where>
- order by characteristic_order asc
- </select>
-
- <select id="selectPrevious" parameterType="Integer" resultType="Characteristic">
- select <include refid="characteristicColumns"/>
- from characteristics c
- <where>
- and c.characteristic_order<#{order}
- and c.parent_id is null
- and c.enabled=${_true}
- </where>
- order by characteristic_order asc
- </select>
-
<select id="selectMaxCharacteristicOrder" resultType="Integer">
select max(c.characteristic_order)
from characteristics c
import org.sonar.check.Cardinality;
import org.sonar.core.persistence.AbstractDaoTestCase;
-import java.util.Collections;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
assertThat(idsFromRuleDtos(ruleDtos)).containsExactly(3);
}
- @Test
- public void select_overriding_debt_rules() throws Exception {
- setupData("select_overriding_debt_rules");
-
- assertThat(dao.selectOverridingDebt(Collections.<String>emptyList())).hasSize(3);
-
- assertThat(dao.selectOverridingDebt(newArrayList("squid"))).hasSize(2);
- assertThat(dao.selectOverridingDebt(newArrayList("java"))).hasSize(1);
- assertThat(dao.selectOverridingDebt(newArrayList("squid", "java"))).hasSize(3);
- assertThat(dao.selectOverridingDebt(newArrayList("unknown"))).isEmpty();
- }
-
@Test
public void update() {
setupData("update");
assertThat(dao.selectById(10)).isNull();
}
- @Test
- public void select_next_and_previous_characteristic() {
- setupData("select_next_and_previous");
-
- assertThat(dao.selectNext(1)).isNotNull();
- assertThat(dao.selectNext(2)).isNull();
-
- assertThat(dao.selectPrevious(1)).isNull();
- assertThat(dao.selectPrevious(2)).isNotNull();
- }
-
@Test
public void select_max_characteristic_order() {
setupData("shared");
+++ /dev/null
-<dataset>
-
- <!-- Rule overriding debt and with default debt -->
- <rules id="1" plugin_rule_key="UselessImportCheck" plugin_name="squid" name="UselessImportCheck" description="Useless imports should be removed" status="READY"
- characteristic_id="2" default_characteristic_id="50"
- remediation_function="LINEAR_OFFSET" default_remediation_function="LINEAR_OFFSET"
- remediation_factor="5d" default_remediation_factor="5d"
- remediation_offset="10h" default_remediation_offset="10h" updated_at="2014-02-19"/>
-
- <!-- Rule only overriding debt -->
- <rules id="2" plugin_rule_key="LeftCurlyBraceStartLineCheck" plugin_name="squid" name="LeftCurlyBraceStartLineCheck" description="Left curly braces should be located at the beginning of lines of code" status="READY"
- characteristic_id="3" default_characteristic_id="[null]"
- remediation_function="LINEAR_OFFSET" default_remediation_function="[null]"
- remediation_factor="5d" default_remediation_factor="[null]"
- remediation_offset="10h" default_remediation_offset="[null]" updated_at="2014-02-19"/>
-
- <!-- Rule with only default debt : never been returned -->
- <rules id="3" plugin_rule_key="CallToFileDeleteOnExitMethod" plugin_name="squid" name="CallToFileDeleteOnExitMethod" description="CallToFileDeleteOnExitMethod" status="READY"
- characteristic_id="[null]" default_characteristic_id="50"
- remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
- remediation_factor="[null]" default_remediation_factor="5d"
- remediation_offset="[null]" default_remediation_offset="10h" updated_at="2014-02-19"/>
-
- <!-- Removed rule overriding debt -->
- <rules id="4" plugin_rule_key="ObjectFinalizeOverridenCallsSuperFinalizeCheck" plugin_name="java" name="ObjectFinalizeOverridenCallsSuperFinalizeCheck" description="super.finalize() should be called at the end of Object.finalize() implementations" status="REMOVED"
- characteristic_id="3" default_characteristic_id="50"
- remediation_function="LINEAR" default_remediation_function="LINEAR_OFFSET"
- remediation_factor="5d" default_remediation_factor="5min"
- remediation_offset="[null]" default_remediation_offset="10h" updated_at="2014-02-19"/>
-
-</dataset>
+++ /dev/null
-<dataset>
-
- <!-- Root characteristic -->
- <characteristics id="1" kee="PORTABILITY" name="Portability" parent_id="[null]" characteristic_order="1"
- enabled="[true]"
- created_at="2013-11-20" updated_at="2013-11-22"/>
-
- <!-- Disabled root characteristic -->
- <characteristics id="4" kee="MAINTAINABILITY" name="Maintainability" parent_id="[null]" characteristic_order="2"
- enabled="[true]"
- created_at="2013-11-20" updated_at="2013-11-22"/>
-
-</dataset>
import com.google.common.collect.Iterables;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.ibatis.session.SqlSession;
import org.sonar.api.ServerComponent;
import org.sonar.api.server.debt.DebtCharacteristic;
import org.sonar.api.utils.System2;
+import org.sonar.api.utils.ValidationMessages;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.rule.RuleDao;
private final DebtModelOperations debtModelOperations;
private final TechnicalDebtModelRepository debtModelPluginRepository;
private final RuleRepositories ruleRepositories;
- private final DebtCharacteristicsXMLImporter importer;
+ private final DebtCharacteristicsXMLImporter characteristicsXMLImporter;
+ private final DebtRulesXMLImporter rulesXMLImporter;
private final System2 system2;
public DebtModelRestore(MyBatis mybatis, CharacteristicDao dao, RuleDao ruleDao, DebtModelOperations debtModelOperations, TechnicalDebtModelRepository debtModelPluginRepository,
- RuleRepositories ruleRepositories, DebtCharacteristicsXMLImporter importer) {
- this(mybatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, ruleRepositories, importer, System2.INSTANCE);
+ RuleRepositories ruleRepositories, DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter) {
+ this(mybatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, ruleRepositories, characteristicsXMLImporter, rulesXMLImporter, System2.INSTANCE);
}
@VisibleForTesting
DebtModelRestore(MyBatis mybatis, CharacteristicDao dao, RuleDao ruleDao, DebtModelOperations debtModelOperations, TechnicalDebtModelRepository debtModelPluginRepository,
- RuleRepositories ruleRepositories, DebtCharacteristicsXMLImporter importer,
+ RuleRepositories ruleRepositories, DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter,
System2 system2) {
this.mybatis = mybatis;
this.dao = dao;
this.debtModelOperations = debtModelOperations;
this.debtModelPluginRepository = debtModelPluginRepository;
this.ruleRepositories = ruleRepositories;
- this.importer = importer;
+ this.characteristicsXMLImporter = characteristicsXMLImporter;
+ this.rulesXMLImporter = rulesXMLImporter;
this.system2 = system2;
}
/**
* Restore from provided model
*/
- public void restore() {
- restore(Collections.<RuleRepositories.Repository>emptyList());
+ public ValidationMessages restore() {
+ ValidationMessages validationMessages = ValidationMessages.create();
+ restore(loadModelFromPlugin(TechnicalDebtModelRepository.DEFAULT_MODEL), Collections.<DebtRulesXMLImporter.RuleDebt>emptyList(),
+ Collections.<RuleRepositories.Repository>emptyList(), false, validationMessages);
+ return validationMessages;
}
/**
* Restore from plugins providing rules for a given language
*/
- public void restore(String languageKey) {
- restore(ruleRepositories.repositoriesForLang(languageKey));
+ public ValidationMessages restore(String languageKey) {
+ ValidationMessages validationMessages = ValidationMessages.create();
+ restore(loadModelFromPlugin(TechnicalDebtModelRepository.DEFAULT_MODEL), Collections.<DebtRulesXMLImporter.RuleDebt>emptyList(),
+ ruleRepositories.repositoriesForLang(languageKey), false, validationMessages);
+ return validationMessages;
}
- private void restore(Collection<RuleRepositories.Repository> repositories) {
+ /**
+ * Restore model from a given XML model
+ */
+ public ValidationMessages restoreFromXml(String xml) {
+ DebtModel debtModel = characteristicsXMLImporter.importXML(xml);
+ ValidationMessages validationMessages = ValidationMessages.create();
+ List<DebtRulesXMLImporter.RuleDebt> ruleDebts = rulesXMLImporter.importXML(xml, validationMessages);
+ restore(debtModel, ruleDebts, Collections.<RuleRepositories.Repository>emptyList(), true, validationMessages);
+ return validationMessages;
+ }
+
+ /**
+ * Restore model from a given XML model and a given language
+ */
+ public ValidationMessages restoreFromXml(String xml, String languageKey) {
+ DebtModel debtModel = characteristicsXMLImporter.importXML(xml);
+ ValidationMessages validationMessages = ValidationMessages.create();
+ List<DebtRulesXMLImporter.RuleDebt> ruleDebts = rulesXMLImporter.importXML(xml, validationMessages);
+ restore(debtModel, ruleDebts, ruleRepositories.repositoriesForLang(languageKey), true, validationMessages);
+ return validationMessages;
+ }
+
+ private void restore(DebtModel modelToImport, List<DebtRulesXMLImporter.RuleDebt> ruleDebts, Collection<RuleRepositories.Repository> repositories,
+ boolean disableCharacteristicWhenRuleNotFound, ValidationMessages validationMessages) {
checkPermission();
Date updateDate = new Date(system2.now());
SqlSession session = mybatis.openSession();
try {
List<CharacteristicDto> persisted = dao.selectEnabledCharacteristics();
- DebtModel providedModel = loadModelFromXml(TechnicalDebtModelRepository.DEFAULT_MODEL);
- restoreCharacteristics(providedModel, persisted, updateDate, session);
- resetOverridingRuleDebt(repositories, updateDate, session);
+ List<CharacteristicDto> characteristicDtos = restoreCharacteristics(modelToImport, persisted, updateDate, session);
+ restoreRules(characteristicDtos, repositories, ruleDebts, disableCharacteristicWhenRuleNotFound, validationMessages, updateDate, session);
session.commit();
} finally {
}
}
- private void resetOverridingRuleDebt(Collection<RuleRepositories.Repository> repositories, Date updateDate, SqlSession session) {
+ private void restoreRules(List<CharacteristicDto> characteristicDtos, Collection<RuleRepositories.Repository> repositories, List<DebtRulesXMLImporter.RuleDebt> ruleDebts,
+ boolean disableCharacteristicWhenRuleNotFound, ValidationMessages validationMessages, Date updateDate, SqlSession session) {
List<String> repositoryKeys = newArrayList(Iterables.transform(repositories, new Function<RuleRepositories.Repository, String>() {
@Override
public String apply(RuleRepositories.Repository input) {
return input.getKey();
}
}));
- for (RuleDto rule : ruleDao.selectOverridingDebt(repositoryKeys, session)) {
- rule.setCharacteristicId(null);
- rule.setRemediationFunction(null);
- rule.setRemediationFactor(null);
- rule.setRemediationOffset(null);
- rule.setUpdatedAt(updateDate);
- ruleDao.update(rule, session);
- // TODO index rules in E/S
+ for (RuleDto rule : ruleDao.selectEnablesAndNonManual(session)) {
+ if (repositories.isEmpty() || repositoryKeys.contains(rule.getRepositoryKey())) {
+ DebtRulesXMLImporter.RuleDebt ruleDebt = ruleDebtByRule(rule, ruleDebts);
+ if (ruleDebt == null) {
+ rule.setCharacteristicId(disableCharacteristicWhenRuleNotFound ? RuleDto.DISABLED_CHARACTERISTIC_ID : null);
+ rule.setRemediationFunction(null);
+ rule.setRemediationFactor(null);
+ rule.setRemediationOffset(null);
+ } else {
+ CharacteristicDto characteristicDto = characteristicByKey(ruleDebt.characteristicKey(), characteristicDtos, false);
+ // Characteristic cannot be null as it has been created just before
+
+ boolean isSameCharacteristic = characteristicDto.getId().equals(rule.getDefaultCharacteristicId());
+ boolean isSameFunction = isSameRemediationFunction(ruleDebt, rule);
+ rule.setCharacteristicId((!isSameCharacteristic ? characteristicDto.getId() : null));
+ rule.setRemediationFunction((!isSameFunction ? ruleDebt.function().name() : null));
+ rule.setRemediationFactor((!isSameFunction ? ruleDebt.factor() : null));
+ rule.setRemediationOffset((!isSameFunction ? ruleDebt.offset() : null));
+ }
+
+ ruleDebts.remove(ruleDebt);
+ rule.setUpdatedAt(updateDate);
+ ruleDao.update(rule, session);
+ // TODO index rules in E/S
+ }
+ }
+
+ for (DebtRulesXMLImporter.RuleDebt ruleDebt : ruleDebts) {
+ validationMessages.addWarningText(String.format("The rule '%s' does not exist.", ruleDebt.ruleKey()));
}
}
+ static boolean isSameRemediationFunction(DebtRulesXMLImporter.RuleDebt ruleDebt, RuleDto rule) {
+ return new EqualsBuilder()
+ .append(ruleDebt.function().name(), rule.getDefaultRemediationFunction())
+ .append(ruleDebt.factor(), rule.getDefaultRemediationFactor())
+ .append(ruleDebt.offset(), rule.getDefaultRemediationOffset())
+ .isEquals();
+ }
+
@VisibleForTesting
- void restoreCharacteristics(DebtModel targetModel, List<CharacteristicDto> sourceCharacteristics, Date updateDate, SqlSession session) {
+ List<CharacteristicDto> restoreCharacteristics(DebtModel targetModel, List<CharacteristicDto> sourceCharacteristics, Date updateDate, SqlSession session) {
+ List<CharacteristicDto> result = newArrayList();
+
// Restore not existing characteristics
for (DebtCharacteristic characteristic : targetModel.rootCharacteristics()) {
CharacteristicDto rootCharacteristicDto = restoreCharacteristic(characteristic, null, sourceCharacteristics, updateDate, session);
+ result.add(rootCharacteristicDto);
for (DebtCharacteristic subCharacteristic : targetModel.subCharacteristics(characteristic.key())) {
- restoreCharacteristic(subCharacteristic, rootCharacteristicDto.getId(), sourceCharacteristics, updateDate, session);
+ result.add(restoreCharacteristic(subCharacteristic, rootCharacteristicDto.getId(), sourceCharacteristics, updateDate, session));
}
}
// Disable no more existing characteristics
debtModelOperations.disableCharacteristic(sourceCharacteristic, updateDate, session);
}
}
+ return result;
}
private CharacteristicDto restoreCharacteristic(DebtCharacteristic targetCharacteristic, @Nullable Integer parentId, List<CharacteristicDto> sourceCharacteristics,
Date updateDate, SqlSession session) {
- CharacteristicDto sourceCharacteristic = dtoByKey(sourceCharacteristics, targetCharacteristic.key());
+ CharacteristicDto sourceCharacteristic = characteristicByKey(targetCharacteristic.key(), sourceCharacteristics, true);
if (sourceCharacteristic == null) {
CharacteristicDto newCharacteristic = toDto(targetCharacteristic, parentId).setCreatedAt(updateDate);
dao.insert(newCharacteristic, session);
}
}
- private DebtModel loadModelFromXml(String pluginKey) {
+ private DebtModel loadModelFromPlugin(String pluginKey) {
Reader xmlFileReader = null;
try {
xmlFileReader = debtModelPluginRepository.createReaderForXMLFile(pluginKey);
- return importer.importXML(xmlFileReader);
+ return characteristicsXMLImporter.importXML(xmlFileReader);
} finally {
IOUtils.closeQuietly(xmlFileReader);
}
}
- @CheckForNull
- private CharacteristicDto dtoByKey(List<CharacteristicDto> existingModel, final String key) {
- return Iterables.find(existingModel, new Predicate<CharacteristicDto>() {
+ private CharacteristicDto characteristicByKey(final String key, List<CharacteristicDto> existingModel, boolean canByNull) {
+ CharacteristicDto dto = Iterables.find(existingModel, new Predicate<CharacteristicDto>() {
@Override
public boolean apply(CharacteristicDto input) {
return key.equals(input.getKey());
}
}, null);
+ if (dto == null && !canByNull) {
+ throw new IllegalStateException(String.format("Characteristic with key '%s' has not been found ", key));
+ }
+ return dto;
+ }
+
+ @CheckForNull
+ private DebtRulesXMLImporter.RuleDebt ruleDebtByRule(final RuleDto rule, List<DebtRulesXMLImporter.RuleDebt> ruleDebts) {
+ if (ruleDebts.isEmpty()) {
+ return null;
+ }
+ return Iterables.find(ruleDebts, new Predicate<DebtRulesXMLImporter.RuleDebt>() {
+ @Override
+ public boolean apply(DebtRulesXMLImporter.RuleDebt input) {
+ return rule.getRepositoryKey().equals(input.ruleKey().repository()) && rule.getRuleKey().equals(input.ruleKey().rule());
+ }
+ }, null);
}
private static CharacteristicDto toDto(DebtCharacteristic characteristic, @Nullable Integer parentId) {
import org.sonar.api.server.debt.DebtCharacteristic;
import org.sonar.api.server.debt.DebtModel;
+import org.sonar.api.utils.ValidationMessages;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
/**
* Restore from provided model
*/
- public void restore(){
- debtModelRestore.restore();
+ public ValidationMessages restore(){
+ return debtModelRestore.restore();
}
/**
* Restore from plugins providing rules for a given language
*/
- public void restore(String languageKey) {
- debtModelRestore.restore(languageKey);
+ public ValidationMessages restoreFromLanguage(String languageKey) {
+ return debtModelRestore.restore(languageKey);
+ }
+
+ /**
+ * Restore from XML
+ */
+ public ValidationMessages restoreFromXml(String xml){
+ return debtModelRestore.restoreFromXml(xml);
+ }
+
+ /**
+ * Restore from XML and a given language
+ */
+ public ValidationMessages restoreFromXmlAndLanguage(String xml, String languageKey) {
+ return debtModelRestore.restoreFromXml(xml, languageKey);
}
}
import static com.google.common.collect.Lists.newArrayList;
+// TODO replace this by DebtModelRestore
public class DebtModelSynchronizer implements ServerExtension {
private final MyBatis mybatis;
import org.codehaus.staxmate.SMInputFactory;
import org.codehaus.staxmate.in.SMHierarchicCursor;
import org.codehaus.staxmate.in.SMInputCursor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.sonar.api.ServerExtension;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.rule.DebtRemediationFunction;
import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.ValidationMessages;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
public class DebtRulesXMLImporter implements ServerExtension {
- private static final Logger LOG = LoggerFactory.getLogger(DebtRulesXMLImporter.class);
-
public static final String CHARACTERISTIC = "chc";
public static final String CHARACTERISTIC_KEY = "key";
public static final String PROPERTY = "prop";
public static final String PROPERTY_FACTOR = "remediationFactor";
public static final String PROPERTY_OFFSET = "offset";
- public List<RuleDebt> importXML(String xml) {
- return importXML(new StringReader(xml));
+ public List<RuleDebt> importXML(String xml, ValidationMessages validationMessages) {
+ return importXML(new StringReader(xml), validationMessages);
}
- public List<RuleDebt> importXML(Reader xml) {
+ public List<RuleDebt> importXML(Reader xml, ValidationMessages validationMessages) {
List<RuleDebt> ruleDebts = newArrayList();
try {
SMInputFactory inputFactory = initStax();
cursor.advance();
SMInputCursor rootCursor = cursor.childElementCursor(CHARACTERISTIC);
while (rootCursor.getNext() != null) {
- process(ruleDebts, null, null, rootCursor);
+ process(ruleDebts, null, null, validationMessages, rootCursor);
}
cursor.getStreamReader().closeCompletely();
return new SMInputFactory(xmlFactory);
}
- private void process(List<RuleDebt> ruleDebts, @Nullable String rootKey, @Nullable String parentKey, SMInputCursor chcCursor) throws XMLStreamException {
+ private void process(List<RuleDebt> ruleDebts, @Nullable String rootKey, @Nullable String parentKey, ValidationMessages validationMessages, SMInputCursor chcCursor) throws XMLStreamException {
String currentCharacteristicKey = null;
SMInputCursor cursor = chcCursor.childElementCursor();
while (cursor.getNext() != null) {
if (StringUtils.equals(node, CHARACTERISTIC_KEY)) {
currentCharacteristicKey = cursor.collectDescendantText().trim();
} else if (StringUtils.equals(node, CHARACTERISTIC)) {
- process(ruleDebts, parentKey, currentCharacteristicKey, cursor);
+ process(ruleDebts, parentKey, currentCharacteristicKey, validationMessages, cursor);
} else if (StringUtils.equals(node, REPOSITORY_KEY)) {
- RuleDebt ruleDebt = processRule(cursor);
+ RuleDebt ruleDebt = processRule(validationMessages, cursor);
if (ruleDebt != null) {
if (rootKey != null) {
ruleDebt.characteristicKey = parentKey;
ruleDebts.add(ruleDebt);
} else {
- LOG.warn("Rule '" + ruleDebt.ruleKey + "' is ignored because it's defined directly under a root characteristic.");
+ validationMessages.addWarningText("Rule '" + ruleDebt.ruleKey + "' is ignored because it's defined directly under a root characteristic.");
}
}
}
}
@CheckForNull
- private RuleDebt processRule(SMInputCursor cursor) throws XMLStreamException {
+ private RuleDebt processRule(ValidationMessages validationMessages, SMInputCursor cursor) throws XMLStreamException {
String ruleRepositoryKey = cursor.collectDescendantText().trim();
String ruleKey = null;
while (cursor.getNext() != null) {
String node = cursor.getLocalName();
if (StringUtils.equals(node, PROPERTY)) {
- properties.add(processProperty(cursor));
+ properties.add(processProperty(validationMessages, cursor));
} else if (StringUtils.equals(node, RULE_KEY)) {
ruleKey = cursor.collectDescendantText().trim();
}
}
if (StringUtils.isNotBlank(ruleRepositoryKey) && StringUtils.isNotBlank(ruleKey)) {
- return createRule(RuleKey.of(ruleRepositoryKey, ruleKey), properties);
+ return createRule(RuleKey.of(ruleRepositoryKey, ruleKey), properties, validationMessages);
}
return null;
}
- private Property processProperty(SMInputCursor cursor) throws XMLStreamException {
+ private Property processProperty(ValidationMessages validationMessages, SMInputCursor cursor) throws XMLStreamException {
SMInputCursor c = cursor.childElementCursor();
String key = null;
int value = 0;
Double valueDouble = NumberUtils.createDouble(s);
value = valueDouble.intValue();
} catch (NumberFormatException ex) {
- LOG.error(String.format("Cannot import value '%s' for field %s - Expected a numeric value instead", s, key));
+ validationMessages.addErrorText(String.format("Cannot import value '%s' for field %s - Expected a numeric value instead", s, key));
}
} else if (StringUtils.equals(node, PROPERTY_TEXT_VALUE)) {
textValue = c.collectDescendantText().trim();
}
@CheckForNull
- private RuleDebt createRule(RuleKey ruleKey, Properties properties) {
+ private RuleDebt createRule(RuleKey ruleKey, Properties properties, ValidationMessages validationMessages) {
Property function = properties.function();
if (function != null) {
Property offsetProperty = properties.offset();
String offset = offsetProperty != null ? offsetProperty.toDuration() : null;
- return createRuleDebt(ruleKey, function.getTextValue(), factor, offset);
+ return createRuleDebt(ruleKey, function.getTextValue(), factor, offset, validationMessages);
}
return null;
}
@CheckForNull
- private RuleDebt createRuleDebt(RuleKey ruleKey, String function, @Nullable String factor, @Nullable String offset) {
+ private RuleDebt createRuleDebt(RuleKey ruleKey, String function, @Nullable String factor, @Nullable String offset, ValidationMessages validationMessages) {
if ("linear_threshold".equals(function) && 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).setType(DebtRemediationFunction.Type.LINEAR).setFactor(factor);
+ validationMessages.addWarningText(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.Type.LINEAR).setFactor(factor);
} else if ("constant_resource".equals(function)) {
- LOG.warn(String.format("Constant/file function is no longer used, technical debt definitions on '%s' are ignored.", ruleKey));
+ validationMessages.addWarningText(String.format("Constant/file function is no longer used, technical debt definitions on '%s' are ignored.", ruleKey));
} else if (DebtRemediationFunction.Type.CONSTANT_ISSUE.name().equalsIgnoreCase(function) && factor != null && offset == null) {
- return new RuleDebt().setRuleKey(ruleKey).setType(DebtRemediationFunction.Type.CONSTANT_ISSUE).setOffset(factor);
+ return new RuleDebt().setRuleKey(ruleKey).setFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE).setOffset(factor);
} else {
- return new RuleDebt().setRuleKey(ruleKey).setType(DebtRemediationFunction.Type.valueOf(function.toUpperCase())).setFactor(factor).setOffset(offset);
+ return new RuleDebt().setRuleKey(ruleKey).setFunction(DebtRemediationFunction.Type.valueOf(function.toUpperCase())).setFactor(factor).setOffset(offset);
}
return null;
}
return this;
}
- public DebtRemediationFunction.Type type() {
+ public DebtRemediationFunction.Type function() {
return type;
}
- public RuleDebt setType(DebtRemediationFunction.Type type) {
+ public RuleDebt setFunction(DebtRemediationFunction.Type type) {
this.type = type;
return this;
}
import com.google.common.collect.Iterables;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.RuleParam;
import org.sonar.api.rules.RuleRepository;
import org.sonar.api.server.rule.RuleParamType;
import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.api.utils.ValidationMessages;
import org.sonar.check.Cardinality;
import org.sonar.core.i18n.RuleI18nManager;
import org.sonar.core.technicaldebt.TechnicalDebtModelRepository;
*/
public class DeprecatedRulesDefinition implements RulesDefinition {
+ private static final Logger LOG = LoggerFactory.getLogger(DeprecatedRulesDefinition.class);
+
private final RuleI18nManager i18n;
private final RuleRepository[] repositories;
DebtRulesXMLImporter.RuleDebt ruleDebt = findRequirement(ruleDebts, repoKey, ruleKey);
if (ruleDebt != null) {
newRule.setDebtCharacteristic(ruleDebt.characteristicKey());
- switch (ruleDebt.type()) {
+ switch (ruleDebt.function()) {
case LINEAR :
newRule.setDebtRemediationFunction(newRule.debtRemediationFunctions().linear(ruleDebt.factor()));
break;
newRule.setDebtRemediationFunction(newRule.debtRemediationFunctions().constantPerIssue(ruleDebt.offset()));
break;
default :
- throw new IllegalArgumentException(String.format("The type '%s' is unknown", ruleDebt.type()));
+ throw new IllegalArgumentException(String.format("The type '%s' is unknown", ruleDebt.function()));
}
}
}
Reader xmlFileReader = null;
try {
xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey);
- return importer.importXML(xmlFileReader);
+ ValidationMessages validationMessages = ValidationMessages.create();
+ List<DebtRulesXMLImporter.RuleDebt> rules = importer.importXML(xmlFileReader, validationMessages);
+ validationMessages.log(LOG);
+ return rules;
} finally {
IOUtils.closeQuietly(xmlFileReader);
}
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
+import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.api.server.rule.DebtRemediationFunction;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
+import org.sonar.api.utils.ValidationMessages;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.rule.RuleDao;
import java.io.Reader;
import java.util.Collections;
import java.util.Date;
+import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
@Mock
DebtCharacteristicsXMLImporter characteristicsXMLImporter;
+ @Mock
+ DebtRulesXMLImporter rulesXMLImporter;
+
@Mock
RuleRepositories ruleRepositories;
int currentId;
- DebtModel defaultModel = new DebtModel();
+ DebtModel characteristics = new DebtModel();
+ List<DebtRulesXMLImporter.RuleDebt> rules = newArrayList();
DebtModelRestore debtModelRestore;
Reader defaultModelReader = mock(Reader.class);
when(debtModelPluginRepository.createReaderForXMLFile("technical-debt")).thenReturn(defaultModelReader);
- when(characteristicsXMLImporter.importXML(eq(defaultModelReader))).thenReturn(defaultModel);
+ when(characteristicsXMLImporter.importXML(eq(defaultModelReader))).thenReturn(characteristics);
+ when(characteristicsXMLImporter.importXML(anyString())).thenReturn(characteristics);
+ when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(rules);
- debtModelRestore = new DebtModelRestore(myBatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, ruleRepositories, characteristicsXMLImporter, system2);
+ debtModelRestore = new DebtModelRestore(myBatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, ruleRepositories, characteristicsXMLImporter, rulesXMLImporter,
+ system2);
}
@Test
debtModelRestore.restoreCharacteristics(
new DebtModel()
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER_RELATED_PORTABILITY").setName("Compiler"), "PORTABILITY"),
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"),
Collections.<CharacteristicDto>emptyList(),
now,
session
CharacteristicDto dto2 = characteristicArgument.getAllValues().get(1);
assertThat(dto2.getId()).isEqualTo(11);
- assertThat(dto2.getKey()).isEqualTo("COMPILER_RELATED_PORTABILITY");
+ assertThat(dto2.getKey()).isEqualTo("COMPILER");
assertThat(dto2.getName()).isEqualTo("Compiler");
assertThat(dto2.getParentId()).isEqualTo(10);
assertThat(dto2.getOrder()).isNull();
debtModelRestore.restoreCharacteristics(
new DebtModel()
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER_RELATED_PORTABILITY").setName("Compiler"), "PORTABILITY"),
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"),
newArrayList(
new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate).setUpdatedAt(oldDate),
- new CharacteristicDto().setId(2).setKey("COMPILER_RELATED_PORTABILITY").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate).setUpdatedAt(oldDate)
),
now,
session
CharacteristicDto dto2 = characteristicArgument.getAllValues().get(1);
assertThat(dto2.getId()).isEqualTo(2);
- assertThat(dto2.getKey()).isEqualTo("COMPILER_RELATED_PORTABILITY");
+ assertThat(dto2.getKey()).isEqualTo("COMPILER");
assertThat(dto2.getName()).isEqualTo("Compiler");
assertThat(dto2.getParentId()).isEqualTo(1);
assertThat(dto2.getOrder()).isNull();
@Test
public void disable_no_more_existing_characteristics_when_restoring_characteristics() throws Exception {
CharacteristicDto dto1 = new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1);
- CharacteristicDto dto2 = new CharacteristicDto().setId(2).setKey("COMPILER_RELATED_PORTABILITY").setName("Compiler").setParentId(1);
+ CharacteristicDto dto2 = new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1);
debtModelRestore.restoreCharacteristics(new DebtModel(), newArrayList(dto1, dto2), now, session);
public void restore_from_provided_model() throws Exception {
Date oldDate = DateUtils.parseDate("2014-01-01");
- defaultModel
+ characteristics
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER_RELATED_PORTABILITY").setName("Compiler"), "PORTABILITY");
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate),
- new CharacteristicDto().setId(2).setKey("COMPILER_RELATED_PORTABILITY").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate)
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate)
));
- when(ruleDao.selectOverridingDebt(Collections.<String>emptyList(), session)).thenReturn(newArrayList(
- new RuleDto().setCharacteristicId(10).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+ new RuleDto().setRepositoryKey("squid").setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
.setCreatedAt(oldDate).setUpdatedAt(oldDate)
));
verify(dao, times(2)).update(any(CharacteristicDto.class), eq(session));
verifyNoMoreInteractions(dao);
- verify(ruleDao).selectOverridingDebt(Collections.<String>emptyList(), session);
+ verify(ruleDao).selectEnablesAndNonManual(session);
ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
verify(ruleDao).update(ruleArgument.capture(), eq(session));
verifyNoMoreInteractions(ruleDao);
public void restore_from_language() throws Exception {
Date oldDate = DateUtils.parseDate("2014-01-01");
- defaultModel
+ characteristics
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER_RELATED_PORTABILITY").setName("Compiler"), "PORTABILITY");
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate),
- new CharacteristicDto().setId(2).setKey("COMPILER_RELATED_PORTABILITY").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate)
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate)
));
- when(ruleDao.selectOverridingDebt(newArrayList("squid"), session)).thenReturn(newArrayList(
- new RuleDto().setRepositoryKey("squid")
- .setCharacteristicId(10).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+ new RuleDto().setId(1).setRepositoryKey("squid")
+ .setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate),
+ // Should be ignored
+ new RuleDto().setId(2).setRepositoryKey("checkstyle")
+ .setCharacteristicId(3).setRemediationFunction("LINEAR").setRemediationFactor("2h")
.setCreatedAt(oldDate).setUpdatedAt(oldDate)
));
verify(dao, times(2)).update(any(CharacteristicDto.class), eq(session));
verifyNoMoreInteractions(dao);
- verify(ruleDao).selectOverridingDebt(newArrayList("squid"), session);
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+
+ verify(session).commit();
+ }
+
+ @Test
+ public void restore_from_xml_with_different_characteristic_and_same_function() throws Exception {
+ Date oldDate = DateUtils.parseDate("2014-01-01");
+
+ characteristics
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+
+ when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
+
+ rules.add(new DebtRulesXMLImporter.RuleDebt()
+ .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h"));
+
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+ new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
+ .setDefaultCharacteristicId(10).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelRestore.restoreFromXml("<xml/>");
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+ assertThat(rule.getCharacteristicId()).isEqualTo(2);
+ assertThat(rule.getRemediationFunction()).isNull();
+ assertThat(rule.getRemediationFactor()).isNull();
+ assertThat(rule.getRemediationOffset()).isNull();
+ assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
+ verify(session).commit();
+ }
+
+ @Test
+ public void restore_from_xml_with_same_characteristic_and_different_function() throws Exception {
+ Date oldDate = DateUtils.parseDate("2014-01-01");
+
+ characteristics
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+
+ when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
+
+ rules.add(new DebtRulesXMLImporter.RuleDebt()
+ .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET).setFactor("12h").setOffset("11min"));
+
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+ new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
+ .setDefaultCharacteristicId(2).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelRestore.restoreFromXml("<xml/>");
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+ assertThat(rule.getCharacteristicId()).isNull();
+ assertThat(rule.getRemediationFunction()).isEqualTo("LINEAR_OFFSET");
+ assertThat(rule.getRemediationFactor()).isEqualTo("12h");
+ assertThat(rule.getRemediationOffset()).isEqualTo("11min");
+ assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
+ verify(session).commit();
+ }
+
+ @Test
+ public void restore_from_xml_with_same_characteristic_and_same_function() throws Exception {
+ Date oldDate = DateUtils.parseDate("2014-01-01");
+
+ characteristics
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+
+ when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
+
+ rules.add(new DebtRulesXMLImporter.RuleDebt()
+ .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET).setFactor("2h").setOffset("15min"));
+
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+ new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
+ .setDefaultCharacteristicId(2).setDefaultRemediationFunction("LINEAR_OFFSET").setDefaultRemediationFactor("2h").setDefaultRemediationOffset("15min")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelRestore.restoreFromXml("<xml/>");
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
verify(ruleDao).update(ruleArgument.capture(), eq(session));
verifyNoMoreInteractions(ruleDao);
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+ assertThat(rule.getCharacteristicId()).isNull();
+ assertThat(rule.getRemediationFunction()).isNull();
+ assertThat(rule.getRemediationFactor()).isNull();
+ assertThat(rule.getRemediationOffset()).isNull();
+ assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
+ verify(session).commit();
+ }
+
+ @Test
+ public void restore_from_xml_disable_rule_debt_when_not_in_xml() throws Exception {
+ Date oldDate = DateUtils.parseDate("2014-01-01");
+
+ characteristics
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+
+ when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
+
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+ new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
+ .setDefaultCharacteristicId(2).setDefaultRemediationFunction("LINEAR_OFFSET").setDefaultRemediationFactor("2h").setDefaultRemediationOffset("15min")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelRestore.restoreFromXml("<xml/>");
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+ assertThat(rule.getCharacteristicId()).isEqualTo(-1);
+ assertThat(rule.getRemediationFunction()).isNull();
+ assertThat(rule.getRemediationFactor()).isNull();
+ assertThat(rule.getRemediationOffset()).isNull();
+ assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
verify(session).commit();
}
+
+ @Test
+ public void restore_from_xml_and_language() throws Exception {
+ Date oldDate = DateUtils.parseDate("2014-01-01");
+
+ characteristics
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+
+ when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
+
+ rules.add(new DebtRulesXMLImporter.RuleDebt()
+ .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h"));
+
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+ new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
+ .setDefaultCharacteristicId(10).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate),
+ // Should be ignored
+ new RuleDto().setId(2).setRepositoryKey("checkstyle")
+ .setCharacteristicId(3).setRemediationFunction("LINEAR").setRemediationFactor("2h")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ RuleRepositories.Repository squid = mock(RuleRepositories.Repository.class);
+ when(squid.getKey()).thenReturn("squid");
+ when(ruleRepositories.repositoriesForLang("java")).thenReturn(newArrayList(squid));
+
+ debtModelRestore.restoreFromXml("<xml/>", "java");
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+
+ verify(session).commit();
+ }
+
+ @Test
+ public void add_warning_message_when_rule_from_xml_is_not_found() throws Exception {
+ Date oldDate = DateUtils.parseDate("2014-01-01");
+
+ characteristics
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+
+ when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
+
+ rules.add(new DebtRulesXMLImporter.RuleDebt()
+ .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h"));
+
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(Collections.<RuleDto>emptyList());
+
+ ValidationMessages validationMessages = debtModelRestore.restoreFromXml("<xml/>");
+
+ assertThat(validationMessages.getWarnings()).hasSize(1);
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ verifyNoMoreInteractions(ruleDao);
+
+ verify(session).commit();
+ }
+
}
@Test
public void restore_from_language() {
- service.restore("xoo");
+ service.restoreFromLanguage("xoo");
verify(debtModelRestore).restore("xoo");
}
+ @Test
+ public void restore_xml() {
+ service.restoreFromXml("<xml/>");
+ verify(debtModelRestore).restoreFromXml("<xml/>");
+ }
+
+ @Test
+ public void restore_from_xml_and_language() {
+ service.restoreFromXmlAndLanguage("<xml/>", "xoo");
+ verify(debtModelRestore).restoreFromXml("<xml/>", "xoo");
+ }
+
}
import org.junit.Test;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.rule.DebtRemediationFunction;
+import org.sonar.api.utils.ValidationMessages;
import java.io.IOException;
import java.util.List;
public class DebtRulesXMLImporterTest {
+ ValidationMessages validationMessages = ValidationMessages.create();
DebtRulesXMLImporter importer = new DebtRulesXMLImporter();
@Test
public void import_rules() {
String xml = getFileContent("import_rules.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
+
assertThat(results).hasSize(2);
+ assertThat(validationMessages.getErrors()).isEmpty();
+ assertThat(validationMessages.getWarnings()).isEmpty();
}
@Test
public void import_linear() {
String xml = getFileContent("import_linear.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
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.type()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
+ assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
assertThat(ruleDebt.factor()).isEqualTo("3h");
assertThat(ruleDebt.offset()).isNull();
}
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);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
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.type()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
+ assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
assertThat(ruleDebt.factor()).isEqualTo("3h");
assertThat(ruleDebt.offset()).isNull();
}
public void import_linear_with_offset() {
String xml = getFileContent("import_linear_with_offset.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
- assertThat(ruleDebt.type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
+ assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
assertThat(ruleDebt.factor()).isEqualTo("3h");
assertThat(ruleDebt.offset()).isEqualTo("1min");
}
public void import_constant_issue() {
String xml = getFileContent("import_constant_issue.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
- assertThat(ruleDebt.type()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE);
+ assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE);
assertThat(ruleDebt.factor()).isNull();
assertThat(ruleDebt.offset()).isEqualTo("3d");
}
public void use_default_unit_when_no_unit() {
String xml = getFileContent("use_default_unit_when_no_unit.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
- assertThat(ruleDebt.type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
+ assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
assertThat(ruleDebt.factor()).isEqualTo("3d");
assertThat(ruleDebt.offset()).isEqualTo("1d");
}
public void replace_mn_by_min() {
String xml = getFileContent("replace_mn_by_min.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
- assertThat(ruleDebt.type()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
+ assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
assertThat(ruleDebt.factor()).isEqualTo("3min");
assertThat(ruleDebt.offset()).isNull();
}
public void convert_deprecated_linear_with_threshold_function_by_linear_function() {
String xml = getFileContent("convert_deprecated_linear_with_threshold_function_by_linear_function.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
- assertThat(ruleDebt.type()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
+ assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
assertThat(ruleDebt.factor()).isEqualTo("3h");
assertThat(ruleDebt.offset()).isNull();
+
+ assertThat(validationMessages.getWarnings()).isNotEmpty();
}
@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);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
- assertThat(ruleDebt.type()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE);
+ assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE);
assertThat(ruleDebt.factor()).isNull();
assertThat(ruleDebt.offset()).isEqualTo("3h");
}
public void ignore_deprecated_constant_per_file_function() {
String xml = getFileContent("ignore_deprecated_constant_per_file_function.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).isEmpty();
+
+ assertThat(validationMessages.getWarnings()).isNotEmpty();
}
@Test
public void ignore_rule_on_root_characteristics() {
String xml = getFileContent("ignore_rule_on_root_characteristics.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).isEmpty();
+
+ assertThat(validationMessages.getWarnings()).isNotEmpty();
}
@Test
public void import_badly_formatted_xml() {
String xml = getFileContent("import_badly_formatted_xml.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
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.type()).isEqualTo(org.sonar.api.server.rule.DebtRemediationFunction.Type.LINEAR);
+ assertThat(ruleDebt.function()).isEqualTo(org.sonar.api.server.rule.DebtRemediationFunction.Type.LINEAR);
assertThat(ruleDebt.factor()).isEqualTo("3h");
assertThat(ruleDebt.offset()).isNull();
}
@Test
public void ignore_invalid_value() throws Exception {
String xml = getFileContent("ignore_invalid_value.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml);
+ List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).isEmpty();
+
+ assertThat(validationMessages.getErrors()).isNotEmpty();
}
@Test
import org.sonar.api.rules.RuleRepository;
import org.sonar.api.server.rule.DebtRemediationFunction;
import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.api.utils.ValidationMessages;
import org.sonar.core.i18n.RuleI18nManager;
import org.sonar.core.technicaldebt.TechnicalDebtModelRepository;
import org.sonar.server.debt.DebtRulesXMLImporter;
import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
new DebtRulesXMLImporter.RuleDebt()
.setCharacteristicKey("MEMORY_EFFICIENCY")
.setRuleKey(RuleKey.of("checkstyle", "ConstantName"))
- .setType(DebtRemediationFunction.Type.LINEAR_OFFSET)
+ .setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET)
.setFactor("1d")
.setOffset("10min")
);
Reader javaModelReader = mock(Reader.class);
when(debtModelRepository.createReaderForXMLFile("java")).thenReturn(javaModelReader);
when(debtModelRepository.getContributingPluginList()).thenReturn(newArrayList("java"));
- when(importer.importXML(eq(javaModelReader))).thenReturn(ruleDebts);
+ when(importer.importXML(eq(javaModelReader), any(ValidationMessages.class))).thenReturn(ruleDebts);
new DeprecatedRulesDefinition(i18n, new RuleRepository[]{new CheckstyleRules()}, debtModelRepository, importer).define(context);