diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-03-24 13:28:04 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-03-24 13:28:13 +0100 |
commit | 15f82899fb01062f6fe511ca4f18e1aa55690d47 (patch) | |
tree | e631ef51f39cf1da6eb0893434acf0ea2943e345 /sonar-server | |
parent | 2a3aba9720dd5c2afa717ec30b479402c1058c59 (diff) | |
download | sonarqube-15f82899fb01062f6fe511ca4f18e1aa55690d47.tar.gz sonarqube-15f82899fb01062f6fe511ca4f18e1aa55690d47.zip |
SONAR-5121 Add restore from XMl actions
Diffstat (limited to 'sonar-server')
9 files changed, 482 insertions, 111 deletions
diff --git a/sonar-server/src/main/java/org/sonar/server/debt/DebtModelRestore.java b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelRestore.java index 380d4d5a379..205850d4f67 100644 --- a/sonar-server/src/main/java/org/sonar/server/debt/DebtModelRestore.java +++ b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelRestore.java @@ -25,10 +25,12 @@ import com.google.common.base.Predicate; 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; @@ -58,17 +60,18 @@ public class DebtModelRestore implements ServerComponent { 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; @@ -76,34 +79,63 @@ public class DebtModelRestore implements ServerComponent { 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 { @@ -111,31 +143,64 @@ public class DebtModelRestore implements ServerComponent { } } - 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 @@ -144,11 +209,12 @@ public class DebtModelRestore implements ServerComponent { 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); @@ -166,24 +232,40 @@ public class DebtModelRestore implements ServerComponent { } } - 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) { diff --git a/sonar-server/src/main/java/org/sonar/server/debt/DebtModelService.java b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelService.java index 483f4fa5c28..a0777d8f528 100644 --- a/sonar-server/src/main/java/org/sonar/server/debt/DebtModelService.java +++ b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelService.java @@ -22,6 +22,7 @@ package org.sonar.server.debt; 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; @@ -84,15 +85,29 @@ public class DebtModelService implements DebtModel { /** * 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); } } diff --git a/sonar-server/src/main/java/org/sonar/server/debt/DebtModelSynchronizer.java b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelSynchronizer.java index e8ea229be18..f6e3f8b331c 100644 --- a/sonar-server/src/main/java/org/sonar/server/debt/DebtModelSynchronizer.java +++ b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelSynchronizer.java @@ -36,6 +36,7 @@ import java.util.List; import static com.google.common.collect.Lists.newArrayList; +// TODO replace this by DebtModelRestore public class DebtModelSynchronizer implements ServerExtension { private final MyBatis mybatis; diff --git a/sonar-server/src/main/java/org/sonar/server/debt/DebtRulesXMLImporter.java b/sonar-server/src/main/java/org/sonar/server/debt/DebtRulesXMLImporter.java index fb08a965599..762b11deacb 100644 --- a/sonar-server/src/main/java/org/sonar/server/debt/DebtRulesXMLImporter.java +++ b/sonar-server/src/main/java/org/sonar/server/debt/DebtRulesXMLImporter.java @@ -29,12 +29,11 @@ import org.codehaus.stax2.XMLInputFactory2; 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; @@ -49,8 +48,6 @@ import static com.google.common.collect.Lists.newArrayList; 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"; @@ -66,11 +63,11 @@ public class DebtRulesXMLImporter implements ServerExtension { 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(); @@ -80,7 +77,7 @@ public class DebtRulesXMLImporter implements ServerExtension { 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(); @@ -99,7 +96,7 @@ public class DebtRulesXMLImporter implements ServerExtension { 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) { @@ -107,15 +104,15 @@ public class DebtRulesXMLImporter implements ServerExtension { 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."); } } } @@ -123,7 +120,7 @@ public class DebtRulesXMLImporter implements ServerExtension { } @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; @@ -131,18 +128,18 @@ public class DebtRulesXMLImporter implements ServerExtension { 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; @@ -158,7 +155,7 @@ public class DebtRulesXMLImporter implements ServerExtension { 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(); @@ -169,7 +166,7 @@ public class DebtRulesXMLImporter implements ServerExtension { } @CheckForNull - private RuleDebt createRule(RuleKey ruleKey, Properties properties) { + private RuleDebt createRule(RuleKey ruleKey, Properties properties, ValidationMessages validationMessages) { Property function = properties.function(); if (function != null) { @@ -178,22 +175,22 @@ public class DebtRulesXMLImporter implements ServerExtension { 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; } @@ -291,11 +288,11 @@ public class DebtRulesXMLImporter implements ServerExtension { 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; } diff --git a/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinition.java b/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinition.java index 84c0d5b7c60..59b8b8f0a58 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinition.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinition.java @@ -23,12 +23,15 @@ import com.google.common.base.Predicate; 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; @@ -49,6 +52,8 @@ import static com.google.common.collect.Lists.newArrayList; */ public class DeprecatedRulesDefinition implements RulesDefinition { + private static final Logger LOG = LoggerFactory.getLogger(DeprecatedRulesDefinition.class); + private final RuleI18nManager i18n; private final RuleRepository[] repositories; @@ -105,7 +110,7 @@ public class DeprecatedRulesDefinition implements RulesDefinition { 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; @@ -116,7 +121,7 @@ public class DeprecatedRulesDefinition implements RulesDefinition { 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())); } } } @@ -160,7 +165,10 @@ public class DeprecatedRulesDefinition implements RulesDefinition { 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); } diff --git a/sonar-server/src/test/java/org/sonar/server/debt/DebtModelRestoreTest.java b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelRestoreTest.java index 4c96c20172e..05b8ac50a92 100644 --- a/sonar-server/src/test/java/org/sonar/server/debt/DebtModelRestoreTest.java +++ b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelRestoreTest.java @@ -29,9 +29,12 @@ import org.mockito.Mock; 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; @@ -45,6 +48,7 @@ import org.sonar.server.user.MockUserSession; 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; @@ -77,6 +81,9 @@ public class DebtModelRestoreTest { DebtCharacteristicsXMLImporter characteristicsXMLImporter; @Mock + DebtRulesXMLImporter rulesXMLImporter; + + @Mock RuleRepositories ruleRepositories; @Mock @@ -86,7 +93,8 @@ public class DebtModelRestoreTest { int currentId; - DebtModel defaultModel = new DebtModel(); + DebtModel characteristics = new DebtModel(); + List<DebtRulesXMLImporter.RuleDebt> rules = newArrayList(); DebtModelRestore debtModelRestore; @@ -111,9 +119,12 @@ public class DebtModelRestoreTest { 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 @@ -121,7 +132,7 @@ public class DebtModelRestoreTest { 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 @@ -141,7 +152,7 @@ public class DebtModelRestoreTest { 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(); @@ -156,10 +167,10 @@ public class DebtModelRestoreTest { 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 @@ -179,7 +190,7 @@ public class DebtModelRestoreTest { 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(); @@ -190,7 +201,7 @@ public class DebtModelRestoreTest { @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); @@ -202,17 +213,17 @@ public class DebtModelRestoreTest { 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) )); @@ -222,7 +233,7 @@ public class DebtModelRestoreTest { 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); @@ -241,18 +252,22 @@ public class DebtModelRestoreTest { 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) )); @@ -266,11 +281,237 @@ public class DebtModelRestoreTest { 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(); + } + } diff --git a/sonar-server/src/test/java/org/sonar/server/debt/DebtModelServiceTest.java b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelServiceTest.java index 9c044e6508a..59051abf5f6 100644 --- a/sonar-server/src/test/java/org/sonar/server/debt/DebtModelServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelServiceTest.java @@ -102,8 +102,20 @@ public class DebtModelServiceTest { @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"); + } + } diff --git a/sonar-server/src/test/java/org/sonar/server/debt/DebtRulesXMLImporterTest.java b/sonar-server/src/test/java/org/sonar/server/debt/DebtRulesXMLImporterTest.java index d2ddfde267b..e1e8f82b2bc 100644 --- a/sonar-server/src/test/java/org/sonar/server/debt/DebtRulesXMLImporterTest.java +++ b/sonar-server/src/test/java/org/sonar/server/debt/DebtRulesXMLImporterTest.java @@ -25,6 +25,7 @@ import com.google.common.io.Resources; 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; @@ -34,27 +35,31 @@ import static org.fest.assertions.Fail.fail; 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(); } @@ -63,13 +68,13 @@ public class DebtRulesXMLImporterTest { 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(); } @@ -78,12 +83,12 @@ public class DebtRulesXMLImporterTest { 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"); } @@ -92,12 +97,12 @@ public class DebtRulesXMLImporterTest { 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"); } @@ -106,12 +111,12 @@ public class DebtRulesXMLImporterTest { 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"); } @@ -120,12 +125,12 @@ public class DebtRulesXMLImporterTest { 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(); } @@ -134,26 +139,28 @@ public class DebtRulesXMLImporterTest { 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"); } @@ -162,29 +169,33 @@ public class DebtRulesXMLImporterTest { 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(); } @@ -192,8 +203,10 @@ public class DebtRulesXMLImporterTest { @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 diff --git a/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionTest.java b/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionTest.java index 636b5f757de..cfad5d4927b 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionTest.java @@ -31,6 +31,7 @@ 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.api.utils.ValidationMessages; import org.sonar.core.i18n.RuleI18nManager; import org.sonar.core.technicaldebt.TechnicalDebtModelRepository; import org.sonar.server.debt.DebtRulesXMLImporter; @@ -41,6 +42,7 @@ import java.util.List; 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; @@ -158,7 +160,7 @@ public class DeprecatedRulesDefinitionTest { 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") ); @@ -166,7 +168,7 @@ public class DeprecatedRulesDefinitionTest { 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); |