aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizer.java117
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtRuleCache.java101
-rw-r--r--sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporter.java326
-rw-r--r--sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizerTest.java118
-rw-r--r--sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtRuleCacheTest.java94
-rw-r--r--sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporterTest.java247
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/TechnicalDebtModel.java1
-rw-r--r--sonar-server/src/main/java/org/sonar/server/debt/DebtCharacteristicsXMLImporter.java12
-rw-r--r--sonar-server/src/main/java/org/sonar/server/debt/DebtModel.java72
-rw-r--r--sonar-server/src/main/java/org/sonar/server/debt/DebtModelBackup.java (renamed from sonar-server/src/main/java/org/sonar/server/debt/DebtModelRestore.java)215
-rw-r--r--sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java (renamed from sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModelRepository.java)11
-rw-r--r--sonar-server/src/main/java/org/sonar/server/debt/DebtModelService.java28
-rw-r--r--sonar-server/src/main/java/org/sonar/server/debt/DebtModelXMLExporter.java321
-rw-r--r--sonar-server/src/main/java/org/sonar/server/debt/DebtRulesXMLImporter.java84
-rw-r--r--sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java12
-rw-r--r--sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRulesDefinition.java32
-rw-r--r--sonar-server/src/main/java/org/sonar/server/startup/RegisterDebtModel.java10
-rw-r--r--sonar-server/src/test/java/org/sonar/server/debt/DebtCharacteristicsXMLImporterTest.java3
-rw-r--r--sonar-server/src/test/java/org/sonar/server/debt/DebtModelBackupTest.java (renamed from sonar-server/src/test/java/org/sonar/server/debt/DebtModelRestoreTest.java)291
-rw-r--r--sonar-server/src/test/java/org/sonar/server/debt/DebtModelPluginRepositoryTest.java (renamed from sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelRepositoryTest.java)14
-rw-r--r--sonar-server/src/test/java/org/sonar/server/debt/DebtModelServiceTest.java23
-rw-r--r--sonar-server/src/test/java/org/sonar/server/debt/DebtModelXMLExporterTest.java142
-rw-r--r--sonar-server/src/test/java/org/sonar/server/debt/DebtRulesXMLImporterTest.java60
-rw-r--r--sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRulesDefinitionTest.java9
-rw-r--r--sonar-server/src/test/java/org/sonar/server/startup/RegisterDebtModelTest.java10
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/debt/DebtModelPluginRepositoryTest/csharp-model.xml (renamed from sonar-core/src/test/resources/org/sonar/core/technicaldebt/TechnicalDebtModelRepositoryTest/csharp-model.xml)0
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/debt/DebtModelPluginRepositoryTest/java-model.xml (renamed from sonar-core/src/test/resources/org/sonar/core/technicaldebt/TechnicalDebtModelRepositoryTest/java-model.xml)0
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/debt/DebtModelXMLExporterTest/export_xml.xml32
-rw-r--r--sonar-server/src/test/resources/org/sonar/server/debt/DebtRulesXMLImporterTest/read_integer.xml29
29 files changed, 1014 insertions, 1400 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizer.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizer.java
deleted file mode 100644
index f4a76c5eb13..00000000000
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizer.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.core.technicaldebt;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.ibatis.session.SqlSession;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.ServerExtension;
-import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
-import org.sonar.api.utils.ValidationMessages;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.technicaldebt.db.CharacteristicDao;
-import org.sonar.core.technicaldebt.db.CharacteristicDto;
-
-import java.io.Reader;
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-/**
- * @deprecated since 4.3
- */
-@Deprecated
-public class TechnicalDebtModelSynchronizer implements ServerExtension {
-
- private static final Logger LOG = LoggerFactory.getLogger(TechnicalDebtModelSynchronizer.class);
-
- private final MyBatis mybatis;
- private final CharacteristicDao dao;
- private final TechnicalDebtModelRepository languageModelFinder;
- private final TechnicalDebtXMLImporter importer;
-
- public TechnicalDebtModelSynchronizer(MyBatis mybatis, CharacteristicDao dao, TechnicalDebtModelRepository modelRepository, TechnicalDebtXMLImporter importer) {
- this.mybatis = mybatis;
- this.dao = dao;
- this.languageModelFinder = modelRepository;
- this.importer = importer;
- }
-
- public List<CharacteristicDto> synchronize(ValidationMessages messages, TechnicalDebtRuleCache rulesCache) {
- SqlSession session = mybatis.openSession();
-
- List<CharacteristicDto> model = newArrayList();
- try {
- model = synchronize(messages, rulesCache, session);
- session.commit();
- } finally {
- MyBatis.closeQuietly(session);
- }
- return model;
- }
-
- public List<CharacteristicDto> synchronize(ValidationMessages messages, TechnicalDebtRuleCache rulesCache, SqlSession session) {
- DefaultTechnicalDebtModel defaultModel = loadModelFromXml(TechnicalDebtModelRepository.DEFAULT_MODEL, messages, rulesCache);
- List<CharacteristicDto> model = loadOrCreateModelFromDb(defaultModel, session);
- messages.log(LOG);
-
- return model;
- }
-
- private List<CharacteristicDto> loadOrCreateModelFromDb(DefaultTechnicalDebtModel defaultModel, SqlSession session) {
- List<CharacteristicDto> characteristicDtos = loadModel();
- if (characteristicDtos.isEmpty()) {
- return createTechnicalDebtModel(defaultModel, session);
- }
- return characteristicDtos;
- }
-
- private List<CharacteristicDto> loadModel() {
- return dao.selectEnabledCharacteristics();
- }
-
- private List<CharacteristicDto> createTechnicalDebtModel(DefaultTechnicalDebtModel defaultModel, SqlSession session) {
- List<CharacteristicDto> characteristics = newArrayList();
- for (DefaultCharacteristic rootCharacteristic : defaultModel.rootCharacteristics()) {
- CharacteristicDto rootCharacteristicDto = CharacteristicDto.toDto(rootCharacteristic, null);
- dao.insert(rootCharacteristicDto, session);
- characteristics.add(rootCharacteristicDto);
- for (DefaultCharacteristic characteristic : rootCharacteristic.children()) {
- CharacteristicDto characteristicDto = CharacteristicDto.toDto(characteristic, rootCharacteristicDto.getId());
- dao.insert(characteristicDto, session);
- characteristics.add(characteristicDto);
- }
- }
- return characteristics;
- }
-
- public DefaultTechnicalDebtModel loadModelFromXml(String pluginKey, ValidationMessages messages, TechnicalDebtRuleCache rulesCache) {
- Reader xmlFileReader = null;
- try {
- xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey);
- return importer.importXML(xmlFileReader, messages, rulesCache);
- } finally {
- IOUtils.closeQuietly(xmlFileReader);
- }
- }
-
-}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtRuleCache.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtRuleCache.java
deleted file mode 100644
index 6e06fcba40b..00000000000
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtRuleCache.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.core.technicaldebt;
-
-import com.google.common.collect.Maps;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.rules.RuleQuery;
-
-import javax.annotation.CheckForNull;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @deprecated since 4.3
- */
-@Deprecated
-public class TechnicalDebtRuleCache {
-
- private final RuleFinder ruleFinder;
-
- private Map<String, Map<String, Rule>> cachedRules;
- private Map<Integer, Rule> cachedRulesId;
-
- public TechnicalDebtRuleCache(RuleFinder ruleFinder) {
- this.ruleFinder = ruleFinder;
- }
-
- @CheckForNull
- public Rule getByRuleKey(RuleKey ruleKey) {
- initRules();
- return lookUpRuleInCache(ruleKey.repository(), ruleKey.rule());
- }
-
- @CheckForNull
- public Rule getByRuleId(Integer ruleId) {
- initRules();
- return cachedRulesId.get(ruleId);
- }
-
- public boolean exists(Integer ruleId) {
- initRules();
- return getByRuleId(ruleId) != null;
- }
-
- public boolean exists(RuleKey ruleKey) {
- return getByRuleKey(ruleKey) != null;
- }
-
- private void initRules(){
- if(cachedRules == null) {
- loadRules();
- }
- }
-
- private void loadRules() {
- cachedRules = Maps.newHashMap();
- cachedRulesId = Maps.newHashMap();
- Collection<Rule> rules = ruleFinder.findAll(RuleQuery.create());
- for (Rule rule : rules) {
- if(!cachedRules.containsKey(rule.getRepositoryKey())) {
- cachedRules.put(rule.getRepositoryKey(), new HashMap<String, Rule>());
- }
- Map<String, Rule> cachedRepository = cachedRules.get(rule.getRepositoryKey());
- if(!cachedRepository.containsKey(rule.getKey())) {
- cachedRepository.put(rule.getKey(), rule);
- cachedRulesId.put(rule.getId(), rule);
- }
- }
- }
-
- @CheckForNull
- private Rule lookUpRuleInCache(String repository, String ruleKey) {
- Map<String, Rule> cachedRepository = cachedRules.get(repository);
- if(cachedRepository != null) {
- return cachedRepository.get(ruleKey);
- }
- return null;
- }
-
-}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporter.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporter.java
deleted file mode 100644
index b0a66036121..00000000000
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporter.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.core.technicaldebt;
-
-import com.google.common.base.Predicate;
-import com.google.common.base.Strings;
-import com.google.common.collect.Iterables;
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang.math.NumberUtils;
-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.rules.Rule;
-import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
-import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement;
-import org.sonar.api.utils.ValidationMessages;
-import org.sonar.api.utils.internal.WorkDuration;
-
-import javax.annotation.CheckForNull;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamException;
-
-import java.io.Reader;
-import java.io.StringReader;
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-/**
- * @deprecated since 4.3
- */
-@Deprecated
-public class TechnicalDebtXMLImporter implements ServerExtension {
-
- private static final Logger LOG = LoggerFactory.getLogger(TechnicalDebtXMLImporter.class);
-
- public static final String CHARACTERISTIC = "chc";
- public static final String CHARACTERISTIC_KEY = "key";
- public static final String CHARACTERISTIC_NAME = "name";
- public static final String PROPERTY = "prop";
-
- public static final String PROPERTY_KEY = "key";
- public static final String PROPERTY_VALUE = "val";
- public static final String PROPERTY_TEXT_VALUE = "txt";
-
- public static final String REPOSITORY_KEY = "rule-repo";
- public static final String RULE_KEY = "rule-key";
-
- public static final String PROPERTY_FUNCTION = "remediationFunction";
- public static final String PROPERTY_FACTOR = "remediationFactor";
- public static final String PROPERTY_OFFSET = "offset";
-
- public DefaultTechnicalDebtModel importXML(String xml, ValidationMessages messages, TechnicalDebtRuleCache technicalDebtRuleCache) {
- return importXML(new StringReader(xml), messages, technicalDebtRuleCache);
- }
-
- public DefaultTechnicalDebtModel importXML(Reader xml, ValidationMessages messages, TechnicalDebtRuleCache repositoryCache) {
- DefaultTechnicalDebtModel model = new DefaultTechnicalDebtModel();
- try {
- SMInputFactory inputFactory = initStax();
- SMHierarchicCursor cursor = inputFactory.rootElementCursor(xml);
-
- // advance to <sqale>
- cursor.advance();
- SMInputCursor chcCursor = cursor.childElementCursor(CHARACTERISTIC);
-
- while (chcCursor.getNext() != null) {
- processCharacteristic(model, null, chcCursor, messages, repositoryCache);
- }
-
- cursor.getStreamReader().closeCompletely();
-
- } catch (XMLStreamException e) {
- LOG.error("XML is not valid", e);
- messages.addErrorText("XML is not valid: " + e.getMessage());
- }
- return model;
- }
-
- private SMInputFactory initStax() {
- XMLInputFactory xmlFactory = XMLInputFactory2.newInstance();
- xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
- xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
- xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
- xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
- return new SMInputFactory(xmlFactory);
- }
-
- private DefaultCharacteristic processCharacteristic(DefaultTechnicalDebtModel model, DefaultCharacteristic parent, SMInputCursor chcCursor, ValidationMessages messages,
- TechnicalDebtRuleCache technicalDebtRuleCache) throws XMLStreamException {
- DefaultCharacteristic characteristic = new DefaultCharacteristic();
- SMInputCursor cursor = chcCursor.childElementCursor();
- while (cursor.getNext() != null) {
- String node = cursor.getLocalName();
- if (StringUtils.equals(node, CHARACTERISTIC_KEY)) {
- characteristic.setKey(cursor.collectDescendantText().trim());
- // Attached to parent only if a key is existing, otherwise characteristic with empty key can be added.
- characteristic.setParent(parent);
-
- } else if (StringUtils.equals(node, CHARACTERISTIC_NAME)) {
- characteristic.setName(cursor.collectDescendantText().trim(), false);
-
- // <chc> can contain characteristics or requirements
- } else if (StringUtils.equals(node, CHARACTERISTIC)) {
- processCharacteristic(model, characteristic, cursor, messages, technicalDebtRuleCache);
-
- } else if (StringUtils.equals(node, REPOSITORY_KEY)) {
- DefaultRequirement requirement = processRequirement(cursor, messages, technicalDebtRuleCache);
- if (requirement != null) {
- addRequirement(requirement, parent, messages);
- }
- }
- }
-
- if (StringUtils.isNotBlank(characteristic.key()) && characteristic.isRoot()) {
- characteristic.setOrder(model.rootCharacteristics().size() + 1);
- model.addRootCharacteristic(characteristic);
- return characteristic;
- }
- return null;
- }
-
- private void addRequirement(DefaultRequirement requirement, DefaultCharacteristic parent, ValidationMessages messages) {
- DefaultCharacteristic root = parent.parent();
- if (root == null) {
- messages.addWarningText("Requirement '" + requirement.ruleKey() + "' is ignored because it's defined directly under a root characteristic.");
- } else {
- requirement.setCharacteristic(parent);
- requirement.setRootCharacteristic(root);
- }
- }
-
- private DefaultRequirement processRequirement(SMInputCursor cursor, ValidationMessages messages, TechnicalDebtRuleCache technicalDebtRuleCache)
- throws XMLStreamException {
-
- DefaultRequirement requirement = new DefaultRequirement();
- String ruleRepositoryKey = cursor.collectDescendantText().trim();
- String ruleKey = null;
- Properties properties = new Properties();
- while (cursor.getNext() != null) {
- String node = cursor.getLocalName();
- if (StringUtils.equals(node, PROPERTY)) {
- properties.add(processProperty(cursor, messages));
- } else if (StringUtils.equals(node, RULE_KEY)) {
- ruleKey = cursor.collectDescendantText().trim();
- }
- }
- fillRule(requirement, ruleRepositoryKey, ruleKey, messages, technicalDebtRuleCache);
- if (requirement.ruleKey() == null) {
- return null;
- }
- return processFunctionsOnRequirement(requirement, properties, messages);
- }
-
- private void fillRule(DefaultRequirement requirement, String ruleRepositoryKey, String ruleKey, ValidationMessages messages,
- TechnicalDebtRuleCache technicalDebtRuleCache) {
- if (StringUtils.isNotBlank(ruleRepositoryKey) && StringUtils.isNotBlank(ruleKey)) {
- Rule rule = technicalDebtRuleCache.getByRuleKey(RuleKey.of(ruleRepositoryKey, ruleKey));
- if (rule != null) {
- requirement.setRuleKey(RuleKey.of(ruleRepositoryKey, ruleKey));
- } else {
- messages.addWarningText("Rule not found: [repository=" + ruleRepositoryKey + ", key=" + ruleKey + "]");
- }
- }
- }
-
- private Property processProperty(SMInputCursor cursor, ValidationMessages messages) throws XMLStreamException {
- SMInputCursor c = cursor.childElementCursor();
- String key = null;
- int value = 0;
- String textValue = null;
- while (c.getNext() != null) {
- String node = c.getLocalName();
- if (StringUtils.equals(node, PROPERTY_KEY)) {
- key = c.collectDescendantText().trim();
-
- } else if (StringUtils.equals(node, PROPERTY_VALUE)) {
- String s = c.collectDescendantText().trim();
- try {
- // The value is still a double for the moment
- Double valueDouble = NumberUtils.createDouble(s);
- value = valueDouble.intValue();
- } catch (NumberFormatException ex) {
- messages.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();
- }
- }
- return new Property(key, value, textValue);
- }
-
- @CheckForNull
- private DefaultRequirement processFunctionsOnRequirement(DefaultRequirement requirement, Properties properties, ValidationMessages messages) {
- Property function = properties.function();
- Property factor = properties.factor();
- Property offset = properties.offset();
-
- if (function != null) {
- // Requirements should always have values, so we init it with default values
- requirement.setFactorValue(0);
- requirement.setFactorUnit(WorkDuration.UNIT.DAYS);
- requirement.setOffsetValue(0);
- requirement.setOffsetUnit(WorkDuration.UNIT.DAYS);
-
- String functionKey = function.getTextValue();
- if ("linear_threshold".equals(functionKey)) {
- function.setTextValue(DefaultRequirement.FUNCTION_LINEAR);
- offset.setValue(0);
- offset.setTextValue("d");
- messages.addWarningText(String.format("Linear with threshold function is no longer used, function of the requirement '%s' is replaced by linear.", requirement.ruleKey()));
- } else if ("constant_resource".equals(functionKey)) {
- messages.addWarningText(String.format("Constant/file function is no longer used, requirements '%s' are ignored.", requirement.ruleKey()));
- return null;
- }
-
- requirement.setFunction(function.getTextValue());
- if (factor != null) {
- requirement.setFactorValue(factor.getValue());
- if (!Strings.isNullOrEmpty(factor.getTextValue())) {
- requirement.setFactorUnit(DefaultRequirement.toUnit(factor.getTextValue()));
- }
- }
- if (offset != null) {
- requirement.setOffsetValue(offset.getValue());
- if (!Strings.isNullOrEmpty(offset.getTextValue())) {
- requirement.setOffsetUnit(DefaultRequirement.toUnit(offset.getTextValue()));
- }
- }
- return requirement;
- }
- return null;
- }
-
- private static class Properties {
- List<Property> properties;
-
- public Properties() {
- this.properties = newArrayList();
- }
-
- public Properties add(Property property) {
- this.properties.add(property);
- return this;
- }
-
- public Property function() {
- return find(PROPERTY_FUNCTION);
- }
-
- public Property factor() {
- return find(PROPERTY_FACTOR);
- }
-
- public Property offset() {
- return find(PROPERTY_OFFSET);
- }
-
- private Property find(final String key) {
- return Iterables.find(properties, new Predicate<Property>() {
- @Override
- public boolean apply(Property input) {
- return input.getKey().equals(key);
- }
- }, null);
- }
-
- }
-
- private static class Property {
- String key;
- int value;
- String textValue;
-
- private Property(String key, int value, String textValue) {
- this.key = key;
- this.value = value;
- this.textValue = textValue;
- }
-
- private Property setValue(int value) {
- this.value = value;
- return this;
- }
-
- private Property setTextValue(String textValue) {
- this.textValue = textValue;
- return this;
- }
-
- private String getKey() {
- return key;
- }
-
- private int getValue() {
- return value;
- }
-
- private String getTextValue() {
- return textValue;
- }
- }
-}
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizerTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizerTest.java
deleted file mode 100644
index e2c12374ea5..00000000000
--- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelSynchronizerTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.core.technicaldebt;
-
-import com.google.common.collect.Lists;
-import org.apache.ibatis.session.SqlSession;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.mockito.stubbing.Answer;
-import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
-import org.sonar.api.utils.ValidationMessages;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.technicaldebt.db.CharacteristicDao;
-import org.sonar.core.technicaldebt.db.CharacteristicDto;
-
-import java.io.Reader;
-import java.util.Collections;
-import java.util.List;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
-
-@RunWith(MockitoJUnitRunner.class)
-public class TechnicalDebtModelSynchronizerTest {
-
- @Mock
- MyBatis myBatis;
-
- @Mock
- SqlSession session;
-
- @Mock
- TechnicalDebtModelRepository technicalDebtModelRepository;
-
- @Mock
- TechnicalDebtRuleCache ruleCache;
-
- @Mock
- CharacteristicDao dao;
-
- @Mock
- TechnicalDebtXMLImporter xmlImporter;
-
- Integer currentId = 1;
-
- private DefaultTechnicalDebtModel defaultModel;
-
- private TechnicalDebtModelSynchronizer manager;
-
- @Before
- public void initAndMerge() throws Exception {
- when(myBatis.openSession()).thenReturn(session);
-
- defaultModel = new DefaultTechnicalDebtModel();
- Reader defaultModelReader = mock(Reader.class);
- when(technicalDebtModelRepository.createReaderForXMLFile("technical-debt")).thenReturn(defaultModelReader);
- when(xmlImporter.importXML(eq(defaultModelReader), any(ValidationMessages.class), eq(ruleCache))).thenReturn(defaultModel);
-
- doAnswer(new Answer() {
- public Object answer(InvocationOnMock invocation) {
- Object[] args = invocation.getArguments();
- CharacteristicDto dto = (CharacteristicDto) args[0];
- dto.setId(currentId++);
- return null;
- }
- }).when(dao).insert(any(CharacteristicDto.class), any(SqlSession.class));
-
-
- manager = new TechnicalDebtModelSynchronizer(myBatis, dao, technicalDebtModelRepository, xmlImporter);
- }
-
- @Test
- public void create_default_model_on_first_execution_when_no_plugin() throws Exception {
- DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("PORTABILITY");
- new DefaultCharacteristic().setKey("COMPILER_RELATED_PORTABILITY").setParent(rootCharacteristic);
- defaultModel.addRootCharacteristic(rootCharacteristic);
-
- when(technicalDebtModelRepository.getContributingPluginList()).thenReturn(Collections.<String>emptyList());
- when(dao.selectEnabledCharacteristics()).thenReturn(Lists.<CharacteristicDto>newArrayList());
-
- manager.synchronize(ValidationMessages.create(), ruleCache);
-
- verify(dao).selectEnabledCharacteristics();
- ArgumentCaptor<CharacteristicDto> characteristicCaptor = ArgumentCaptor.forClass(CharacteristicDto.class);
- verify(dao, times(2)).insert(characteristicCaptor.capture(), eq(session));
-
- List<CharacteristicDto> result = characteristicCaptor.getAllValues();
- assertThat(result.get(0).getKey()).isEqualTo("PORTABILITY");
- assertThat(result.get(1).getKey()).isEqualTo("COMPILER_RELATED_PORTABILITY");
- verifyNoMoreInteractions(dao);
- }
-
-}
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtRuleCacheTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtRuleCacheTest.java
deleted file mode 100644
index 8e0afd525c3..00000000000
--- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtRuleCacheTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.core.technicaldebt;
-
-import com.google.common.collect.Lists;
-import org.fest.assertions.Assertions;
-import org.junit.Test;
-import org.mockito.Matchers;
-import org.mockito.Mockito;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.rules.RuleQuery;
-
-import java.util.Collections;
-
-public class TechnicalDebtRuleCacheTest {
-
- @Test
- public void lazy_load_rules_on_first_call() throws Exception {
-
- RuleFinder ruleFinder = Mockito.mock(RuleFinder.class);
- Mockito.when(ruleFinder.findAll(Matchers.any(RuleQuery.class))).thenReturn(Collections.EMPTY_LIST);
-
- TechnicalDebtRuleCache technicalDebtRuleCache = new TechnicalDebtRuleCache(ruleFinder);
- technicalDebtRuleCache.getByRuleKey(RuleKey.of("repo1", "rule1"));
- technicalDebtRuleCache.getByRuleKey(RuleKey.of("repo1", "rule1"));
-
- Mockito.verify(ruleFinder, Mockito.times(1)).findAll(Matchers.any(RuleQuery.class));
- }
-
- @Test
- public void return_matching_rule() throws Exception {
-
- Rule rule1 = Rule.create("repo1", "rule1");
- Rule rule2 = Rule.create("repo2", "rule2");
-
- RuleFinder ruleFinder = Mockito.mock(RuleFinder.class);
- Mockito.when(ruleFinder.findAll(Matchers.any(RuleQuery.class))).thenReturn(Lists.newArrayList(rule1, rule2));
-
- TechnicalDebtRuleCache technicalDebtRuleCache = new TechnicalDebtRuleCache(ruleFinder);
- Rule actualRule1 = technicalDebtRuleCache.getByRuleKey(RuleKey.of("repo1", "rule1"));
- Rule actualRule2 = technicalDebtRuleCache.getByRuleKey(RuleKey.of("repo2", "rule2"));
-
- Assertions.assertThat(actualRule1).isEqualTo(rule1);
- Assertions.assertThat(actualRule2).isEqualTo(rule2);
- }
-
- @Test
- public void return_if_rule_exists() throws Exception {
-
- Rule rule1 = Rule.create("repo1", "rule1");
-
- RuleFinder ruleFinder = Mockito.mock(RuleFinder.class);
- Mockito.when(ruleFinder.findAll(Matchers.any(RuleQuery.class))).thenReturn(Lists.newArrayList(rule1));
-
- TechnicalDebtRuleCache technicalDebtRuleCache = new TechnicalDebtRuleCache(ruleFinder);
-
- Assertions.assertThat(technicalDebtRuleCache.exists(RuleKey.of("repo1", "rule1"))).isTrue();
- Assertions.assertThat(technicalDebtRuleCache.exists(RuleKey.of("repo2", "rule2"))).isFalse();
- }
-
- @Test
- public void return_if_rule_id_exists() throws Exception {
-
- Rule rule1 = Rule.create("repo1", "rule1");
- rule1.setId(1);
-
- RuleFinder ruleFinder = Mockito.mock(RuleFinder.class);
- Mockito.when(ruleFinder.findAll(Matchers.any(RuleQuery.class))).thenReturn(Lists.newArrayList(rule1));
-
- TechnicalDebtRuleCache technicalDebtRuleCache = new TechnicalDebtRuleCache(ruleFinder);
-
- Assertions.assertThat(technicalDebtRuleCache.exists(1)).isTrue();
- Assertions.assertThat(technicalDebtRuleCache.exists(2)).isFalse();
- }
-}
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporterTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporterTest.java
deleted file mode 100644
index 4ff4e1f17ee..00000000000
--- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtXMLImporterTest.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.core.technicaldebt;
-
-import com.google.common.base.Charsets;
-import com.google.common.collect.Lists;
-import com.google.common.io.Resources;
-import org.junit.Test;
-import org.mockito.Matchers;
-import org.mockito.Mockito;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.rules.RuleQuery;
-import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
-import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement;
-import org.sonar.api.utils.ValidationMessages;
-import org.sonar.api.utils.internal.WorkDuration;
-
-import java.io.IOException;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class TechnicalDebtXMLImporterTest {
-
- @Test
- public void import_characteristics() {
- TechnicalDebtRuleCache technicalDebtRuleCache = mockRuleCache();
-
- String xml = getFileContent("import_characteristics.xml");
-
- ValidationMessages messages = ValidationMessages.create();
- DefaultTechnicalDebtModel sqale = new TechnicalDebtXMLImporter().importXML(xml, messages, technicalDebtRuleCache);
-
- assertThat(sqale.rootCharacteristics()).hasSize(2);
- assertThat(sqale.rootCharacteristics().get(0).key()).isEqualTo("PORTABILITY");
- assertThat(sqale.rootCharacteristics().get(1).key()).isEqualTo("MAINTAINABILITY");
-
- DefaultCharacteristic portability = sqale.characteristicByKey("PORTABILITY");
- assertThat(portability.order()).isEqualTo(1);
- assertThat(portability.children()).hasSize(2);
- assertThat(portability.children().get(0).key()).isEqualTo("COMPILER_RELATED_PORTABILITY");
- assertThat(sqale.characteristicByKey("COMPILER_RELATED_PORTABILITY").parent().key()).isEqualTo("PORTABILITY");
- assertThat(portability.children().get(1).key()).isEqualTo("HARDWARE_RELATED_PORTABILITY");
- assertThat(sqale.characteristicByKey("HARDWARE_RELATED_PORTABILITY").parent().key()).isEqualTo("PORTABILITY");
-
- DefaultCharacteristic maintainability = sqale.characteristicByKey("MAINTAINABILITY");
- assertThat(maintainability.order()).isEqualTo(2);
- assertThat(maintainability.children()).hasSize(1);
- assertThat(maintainability.children().get(0).key()).isEqualTo("READABILITY");
- assertThat(sqale.characteristicByKey("READABILITY").parent().key()).isEqualTo("MAINTAINABILITY");
- }
-
- @Test
- public void use_default_unit_when_no_unit() {
- TechnicalDebtRuleCache technicalDebtRuleCache = mockRuleCache();
-
- String xml = getFileContent("use_default_unit_when_no_unit.xml");
-
- ValidationMessages messages = ValidationMessages.create();
- DefaultTechnicalDebtModel sqale = new TechnicalDebtXMLImporter().importXML(xml, messages, technicalDebtRuleCache);
-
- DefaultCharacteristic memoryEfficiency = sqale.characteristicByKey("MEMORY_EFFICIENCY");
- DefaultRequirement requirement = memoryEfficiency.requirements().get(0);
- assertThat(requirement.factorValue()).isEqualTo(3);
- assertThat(requirement.factorUnit()).isEqualTo(WorkDuration.UNIT.DAYS);
- assertThat(requirement.offsetValue()).isEqualTo(1);
- assertThat(requirement.offsetUnit()).isEqualTo(WorkDuration.UNIT.DAYS);
- }
-
- @Test
- public void import_xml_with_linear_function() {
- TechnicalDebtRuleCache technicalDebtRuleCache = mockRuleCache();
-
- String xml = getFileContent("shouldImportXML_with_linear.xml");
-
- ValidationMessages messages = ValidationMessages.create();
- DefaultTechnicalDebtModel sqale = new TechnicalDebtXMLImporter().importXML(xml, messages, technicalDebtRuleCache);
-
- checkXmlCorrectlyImported(sqale, messages);
- }
-
- @Test
- public void import_xml_with_linear_with_offset() {
- TechnicalDebtRuleCache technicalDebtRuleCache = mockRuleCache();
-
- String xml = getFileContent("shouldImportXML_with_linear_with_offset.xml");
-
- ValidationMessages messages = ValidationMessages.create();
- DefaultTechnicalDebtModel sqale = new TechnicalDebtXMLImporter().importXML(xml, messages, technicalDebtRuleCache);
-
- checkXmlCorrectlyImported(sqale, 1, WorkDuration.UNIT.HOURS, messages);
- }
-
- @Test
- public void convert_deprecated_linear_with_threshold_function_by_linear_function() {
- TechnicalDebtRuleCache technicalDebtRuleCache = mockRuleCache();
-
- String xml = getFileContent("shouldImportXML_with_deprecated_linear_with_threshold.xml");
-
- ValidationMessages messages = ValidationMessages.create();
- DefaultTechnicalDebtModel sqale = new TechnicalDebtXMLImporter().importXML(xml, messages, technicalDebtRuleCache);
-
- checkXmlCorrectlyImported(sqale, 0, WorkDuration.UNIT.DAYS, messages);
- assertThat(messages.getWarnings()).hasSize(1);
- }
-
- @Test
- public void ignore_deprecated_constant_per_file_function() {
- TechnicalDebtRuleCache technicalDebtRuleCache = mockRuleCache();
-
- String xml = getFileContent("shouldImportXML_with_deprecated_constant_per_file.xml");
-
- ValidationMessages messages = ValidationMessages.create();
- DefaultTechnicalDebtModel sqale = new TechnicalDebtXMLImporter().importXML(xml, messages, technicalDebtRuleCache);
-
- assertThat(messages.getWarnings()).hasSize(1);
-
- // characteristics
- assertThat(sqale.rootCharacteristics()).hasSize(1);
- DefaultCharacteristic efficiency = sqale.characteristicByKey("EFFICIENCY");
- assertThat(efficiency.requirements()).isEmpty();
- }
-
- @Test
- public void ignore_requirement_on_root_characteristics() {
- TechnicalDebtRuleCache technicalDebtRuleCache = mockRuleCache();
-
- String xml = getFileContent("ignore_requirement_on_root_characteristics.xml");
-
- ValidationMessages messages = ValidationMessages.create();
- DefaultTechnicalDebtModel sqale = new TechnicalDebtXMLImporter().importXML(xml, messages, technicalDebtRuleCache);
-
- assertThat(messages.getWarnings()).hasSize(1);
-
- assertThat(sqale.characteristics()).hasSize(1);
- DefaultCharacteristic efficiency = sqale.characteristicByKey("EFFICIENCY");
- assertThat(efficiency.requirements()).isEmpty();
- assertThat(messages.getWarnings().get(0)).contains("checkstyle");
- }
-
- @Test
- public void shouldBadlyFormattedImportXML() {
- TechnicalDebtRuleCache technicalDebtRuleCache = mockRuleCache();
- String xml = getFileContent("shouldImportXML_badly-formatted.xml");
-
- ValidationMessages messages = ValidationMessages.create();
- DefaultTechnicalDebtModel sqale = new TechnicalDebtXMLImporter().importXML(xml, messages, technicalDebtRuleCache);
-
- checkXmlCorrectlyImported(sqale, messages);
- }
-
- @Test
- public void ignore_requirement_with_not_found_rule() {
- TechnicalDebtRuleCache technicalDebtRuleCache = mockRuleCache();
- String xml = getFileContent("shouldLogWarningIfRuleNotFound.xml");
- ValidationMessages messages = ValidationMessages.create();
-
- DefaultTechnicalDebtModel sqale = new TechnicalDebtXMLImporter().importXML(xml, messages, technicalDebtRuleCache);
-
- assertThat(messages.getWarnings()).hasSize(1);
- assertThat(messages.getWarnings().get(0)).isEqualTo("Rule not found: [repository=findbugs, key=Foo]");
-
- // characteristics
- assertThat(sqale.rootCharacteristics()).hasSize(1);
- DefaultCharacteristic efficiency = sqale.characteristicByKey("EFFICIENCY");
- assertThat(efficiency.requirements()).isEmpty();
- assertThat(messages.getWarnings().get(0)).contains("findbugs");
- }
-
- @Test
- public void shouldNotifyOnUnexpectedValueTypeInXml() throws Exception {
-
- TechnicalDebtRuleCache technicalDebtRuleCache = mockRuleCache();
-
- String xml = getFileContent("shouldRejectXML_with_invalid_value.xml");
- ValidationMessages messages = ValidationMessages.create();
-
- new TechnicalDebtXMLImporter().importXML(xml, messages, technicalDebtRuleCache);
-
- assertThat(messages.getErrors()).hasSize(1);
- assertThat(messages.getErrors().get(0)).isEqualTo("Cannot import value 'abc' for field factor - Expected a numeric value instead");
- }
-
- private TechnicalDebtRuleCache mockRuleCache() {
- RuleFinder finder = Mockito.mock(RuleFinder.class);
- Mockito.when(finder.findAll(Matchers.any(RuleQuery.class))).thenReturn(Lists.newArrayList(Rule.create("checkstyle", "Regexp", "Regular expression")));
- return new TechnicalDebtRuleCache(finder);
- }
-
- private void checkXmlCorrectlyImported(DefaultTechnicalDebtModel sqale, ValidationMessages messages) {
- checkXmlCorrectlyImported(sqale, 0, WorkDuration.UNIT.DAYS, messages);
- }
-
- private void checkXmlCorrectlyImported(DefaultTechnicalDebtModel sqale, Integer offsetValue, WorkDuration.UNIT offsetUnit, ValidationMessages messages) {
- assertThat(messages.getErrors()).isEmpty();
-
- // characteristics
- assertThat(sqale.rootCharacteristics()).hasSize(2);
- DefaultCharacteristic efficiency = sqale.characteristicByKey("EFFICIENCY");
- assertThat(efficiency.name()).isEqualTo("Efficiency");
-
- // sub-characteristics
- assertThat(efficiency.children()).hasSize(1);
- DefaultCharacteristic memoryEfficiency = sqale.characteristicByKey("MEMORY_EFFICIENCY");
- assertThat(memoryEfficiency.name()).isEqualTo("Memory use");
-
- // requirement
- assertThat(memoryEfficiency.requirements()).hasSize(1);
- DefaultRequirement requirement = memoryEfficiency.requirements().get(0);
- assertThat(requirement.ruleKey().repository()).isEqualTo("checkstyle");
- assertThat(requirement.ruleKey().rule()).isEqualTo("Regexp");
- assertThat(requirement.function()).isEqualTo("linear");
- assertThat(requirement.factorValue()).isEqualTo(3);
- assertThat(requirement.factorUnit()).isEqualTo(WorkDuration.UNIT.HOURS);
- assertThat(requirement.offsetValue()).isEqualTo(offsetValue);
- assertThat(requirement.offsetUnit()).isEqualTo(offsetUnit);
- assertThat(requirement.characteristic().key()).isEqualTo("MEMORY_EFFICIENCY");
- assertThat(requirement.rootCharacteristic().key()).isEqualTo("EFFICIENCY");
- }
-
- private String getFileContent(String file) {
- try {
- return Resources.toString(Resources.getResource(TechnicalDebtXMLImporterTest.class, "TechnicalDebtXMLImporterTest/" + file), Charsets.UTF_8);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/TechnicalDebtModel.java b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/TechnicalDebtModel.java
index 4cca21cd19f..a18212ec83e 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/TechnicalDebtModel.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/TechnicalDebtModel.java
@@ -29,6 +29,7 @@ import java.util.List;
/**
* @since 4.1
+ * Used by Views plugin
*/
public interface TechnicalDebtModel {
diff --git a/sonar-server/src/main/java/org/sonar/server/debt/DebtCharacteristicsXMLImporter.java b/sonar-server/src/main/java/org/sonar/server/debt/DebtCharacteristicsXMLImporter.java
index d19fc9a163c..d0855fe34d8 100644
--- a/sonar-server/src/main/java/org/sonar/server/debt/DebtCharacteristicsXMLImporter.java
+++ b/sonar-server/src/main/java/org/sonar/server/debt/DebtCharacteristicsXMLImporter.java
@@ -25,7 +25,7 @@ import org.codehaus.stax2.XMLInputFactory2;
import org.codehaus.staxmate.SMInputFactory;
import org.codehaus.staxmate.in.SMHierarchicCursor;
import org.codehaus.staxmate.in.SMInputCursor;
-import org.sonar.api.ServerExtension;
+import org.sonar.api.ServerComponent;
import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
import javax.annotation.CheckForNull;
@@ -36,11 +36,12 @@ import javax.xml.stream.XMLStreamException;
import java.io.Reader;
import java.io.StringReader;
-public class DebtCharacteristicsXMLImporter implements ServerExtension {
+import static org.sonar.server.debt.DebtModelXMLExporter.*;
- public static final String CHARACTERISTIC = "chc";
- public static final String CHARACTERISTIC_KEY = "key";
- public static final String CHARACTERISTIC_NAME = "name";
+/**
+ * Import characteristics from an xml
+ */
+public class DebtCharacteristicsXMLImporter implements ServerComponent {
public DebtModel importXML(String xml) {
return importXML(new StringReader(xml));
@@ -102,4 +103,5 @@ public class DebtCharacteristicsXMLImporter implements ServerExtension {
}
}
}
+
}
diff --git a/sonar-server/src/main/java/org/sonar/server/debt/DebtModel.java b/sonar-server/src/main/java/org/sonar/server/debt/DebtModel.java
deleted file mode 100644
index be3fa0109d1..00000000000
--- a/sonar-server/src/main/java/org/sonar/server/debt/DebtModel.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.debt;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
-import org.sonar.api.server.debt.DebtCharacteristic;
-
-import javax.annotation.CheckForNull;
-
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-// TODO Maybe should be an inner class of DebtCharacteristicsXMLImporter? Will see following how will be implemented the Backup feature
-public class DebtModel {
-
- private Multimap<String, DebtCharacteristic> characteristicsByRootKey;
-
- public DebtModel() {
- characteristicsByRootKey = ArrayListMultimap.create();
- }
-
- public DebtModel addRootCharacteristic(DebtCharacteristic characteristic) {
- characteristicsByRootKey.put(null, characteristic);
- return this;
- }
-
- public DebtModel addSubCharacteristic(DebtCharacteristic subCharacteristic, String characteristicKey) {
- characteristicsByRootKey.put(characteristicKey, subCharacteristic);
- return this;
- }
-
- public List<DebtCharacteristic> rootCharacteristics() {
- return newArrayList(characteristicsByRootKey.get(null));
- }
-
- public List<DebtCharacteristic> subCharacteristics(String characteristicKey) {
- return newArrayList(characteristicsByRootKey.get(characteristicKey));
- }
-
- @CheckForNull
- public DebtCharacteristic characteristicByKey(final String key) {
- return Iterables.find(characteristicsByRootKey.values(), new Predicate<DebtCharacteristic>() {
- @Override
- public boolean apply(DebtCharacteristic input) {
- return key.equals(input.key());
- }
- }, null);
- }
-
-}
diff --git a/sonar-server/src/main/java/org/sonar/server/debt/DebtModelRestore.java b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelBackup.java
index 205850d4f67..ec9ac28c22f 100644
--- a/sonar-server/src/main/java/org/sonar/server/debt/DebtModelRestore.java
+++ b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelBackup.java
@@ -20,7 +20,6 @@
package org.sonar.server.debt;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.apache.commons.io.IOUtils;
@@ -28,80 +27,138 @@ 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.rule.RuleKey;
import org.sonar.api.server.debt.DebtCharacteristic;
+import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.api.server.rule.DebtRemediationFunction;
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 org.sonar.core.rule.RuleDto;
-import org.sonar.core.technicaldebt.TechnicalDebtModelRepository;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
-import org.sonar.server.rule.RuleRepositories;
import org.sonar.server.user.UserSession;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import java.io.Reader;
-import java.util.Collection;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
+import static org.sonar.server.debt.DebtModelXMLExporter.DebtModel;
+import static org.sonar.server.debt.DebtModelXMLExporter.RuleDebt;
-public class DebtModelRestore implements ServerComponent {
+public class DebtModelBackup implements ServerComponent {
private final MyBatis mybatis;
private final CharacteristicDao dao;
private final RuleDao ruleDao;
private final DebtModelOperations debtModelOperations;
- private final TechnicalDebtModelRepository debtModelPluginRepository;
- private final RuleRepositories ruleRepositories;
+ private final DebtModelPluginRepository debtModelPluginRepository;
private final DebtCharacteristicsXMLImporter characteristicsXMLImporter;
private final DebtRulesXMLImporter rulesXMLImporter;
+ private final DebtModelXMLExporter debtModelXMLExporter;
private final System2 system2;
- public DebtModelRestore(MyBatis mybatis, CharacteristicDao dao, RuleDao ruleDao, DebtModelOperations debtModelOperations, TechnicalDebtModelRepository debtModelPluginRepository,
- RuleRepositories ruleRepositories, DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter) {
- this(mybatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, ruleRepositories, characteristicsXMLImporter, rulesXMLImporter, System2.INSTANCE);
+ public DebtModelBackup(MyBatis mybatis, CharacteristicDao dao, RuleDao ruleDao, DebtModelOperations debtModelOperations, DebtModelPluginRepository debtModelPluginRepository,
+ DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter,
+ DebtModelXMLExporter debtModelXMLExporter) {
+ this(mybatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, characteristicsXMLImporter, rulesXMLImporter, debtModelXMLExporter,
+ System2.INSTANCE);
}
@VisibleForTesting
- DebtModelRestore(MyBatis mybatis, CharacteristicDao dao, RuleDao ruleDao, DebtModelOperations debtModelOperations, TechnicalDebtModelRepository debtModelPluginRepository,
- RuleRepositories ruleRepositories, DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter,
- System2 system2) {
+ DebtModelBackup(MyBatis mybatis, CharacteristicDao dao, RuleDao ruleDao, DebtModelOperations debtModelOperations, DebtModelPluginRepository debtModelPluginRepository,
+ DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter,
+ DebtModelXMLExporter debtModelXMLExporter, System2 system2) {
this.mybatis = mybatis;
this.dao = dao;
this.ruleDao = ruleDao;
this.debtModelOperations = debtModelOperations;
this.debtModelPluginRepository = debtModelPluginRepository;
- this.ruleRepositories = ruleRepositories;
this.characteristicsXMLImporter = characteristicsXMLImporter;
this.rulesXMLImporter = rulesXMLImporter;
+ this.debtModelXMLExporter = debtModelXMLExporter;
this.system2 = system2;
}
+ public String backup() {
+ return backupFromLanguage(null);
+ }
+
+ public String backup(String languageKey) {
+ return backupFromLanguage(languageKey);
+ }
+
+ private String backupFromLanguage(@Nullable String languageKey) {
+ checkPermission();
+
+ SqlSession session = mybatis.openSession();
+ try {
+ DebtModel debtModel = new DebtModel();
+ List<CharacteristicDto> characteristicDtos = dao.selectEnabledCharacteristics(session);
+ for (CharacteristicDto characteristicDto : characteristicDtos) {
+ if (characteristicDto.getParentId() == null) {
+ debtModel.addRootCharacteristic(toDebtCharacteristic(characteristicDto));
+ for (CharacteristicDto sub : subCharacteristics(characteristicDto.getId(), characteristicDtos)) {
+ debtModel.addSubCharacteristic(toDebtCharacteristic(sub), characteristicDto.getKey());
+ }
+ }
+ }
+
+ List<RuleDebt> rules = newArrayList();
+ for (RuleDto rule : ruleDao.selectEnablesAndNonManual(session)) {
+ if ((languageKey == null || languageKey.equals(rule.getLanguage())) && rule.hasCharacteristic()) {
+ Integer characteristicId = rule.getCharacteristicId() != null ? rule.getCharacteristicId() : rule.getDefaultCharacteristicId();
+ rules.add(toRuleDebt(rule, debtModel.characteristicById(characteristicId).key()));
+ }
+ }
+ return debtModelXMLExporter.export(debtModel, rules);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
/**
* Restore from provided model
*/
- public ValidationMessages restore() {
- ValidationMessages validationMessages = ValidationMessages.create();
- restore(loadModelFromPlugin(TechnicalDebtModelRepository.DEFAULT_MODEL), Collections.<DebtRulesXMLImporter.RuleDebt>emptyList(),
- Collections.<RuleRepositories.Repository>emptyList(), false, validationMessages);
- return validationMessages;
+ public void restore() {
+ restoreProvided(loadModelFromPlugin(DebtModelPluginRepository.DEFAULT_MODEL), null);
}
/**
* Restore from plugins providing rules for a given language
*/
- 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;
+ public void restore(String languageKey) {
+ restoreProvided(loadModelFromPlugin(DebtModelPluginRepository.DEFAULT_MODEL), languageKey);
+ }
+
+ private void restoreProvided(DebtModel modelToImport, @Nullable String languageKey) {
+ checkPermission();
+
+ Date updateDate = new Date(system2.now());
+ SqlSession session = mybatis.openSession();
+ try {
+ restoreCharacteristics(modelToImport, updateDate, session);
+ for (RuleDto rule : ruleDao.selectEnablesAndNonManual(session)) {
+ if (languageKey == null || languageKey.equals(rule.getLanguage())) {
+ 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
+ }
+ }
+ session.commit();
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
}
/**
@@ -110,8 +167,8 @@ public class DebtModelRestore implements ServerComponent {
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);
+ List<RuleDebt> ruleDebts = rulesXMLImporter.importXML(xml, validationMessages);
+ restore(debtModel, ruleDebts, null, validationMessages);
return validationMessages;
}
@@ -121,21 +178,19 @@ public class DebtModelRestore implements ServerComponent {
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);
+ List<RuleDebt> ruleDebts = rulesXMLImporter.importXML(xml, validationMessages);
+ restore(debtModel, ruleDebts, languageKey, validationMessages);
return validationMessages;
}
- private void restore(DebtModel modelToImport, List<DebtRulesXMLImporter.RuleDebt> ruleDebts, Collection<RuleRepositories.Repository> repositories,
- boolean disableCharacteristicWhenRuleNotFound, ValidationMessages validationMessages) {
+ private void restore(DebtModel modelToImport, List<RuleDebt> ruleDebts, @Nullable String languageKey, ValidationMessages validationMessages) {
checkPermission();
Date updateDate = new Date(system2.now());
SqlSession session = mybatis.openSession();
try {
- List<CharacteristicDto> persisted = dao.selectEnabledCharacteristics();
- List<CharacteristicDto> characteristicDtos = restoreCharacteristics(modelToImport, persisted, updateDate, session);
- restoreRules(characteristicDtos, repositories, ruleDebts, disableCharacteristicWhenRuleNotFound, validationMessages, updateDate, session);
+ List<CharacteristicDto> characteristicDtos = restoreCharacteristics(modelToImport, updateDate, session);
+ restoreRules(characteristicDtos, languageKey, ruleDebts, validationMessages, updateDate, session);
session.commit();
} finally {
@@ -143,19 +198,13 @@ public class DebtModelRestore implements ServerComponent {
}
}
- 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();
- }
- }));
+ private void restoreRules(List<CharacteristicDto> characteristicDtos, @Nullable String languageKey, List<RuleDebt> ruleDebts,
+ ValidationMessages validationMessages, Date updateDate, SqlSession session) {
for (RuleDto rule : ruleDao.selectEnablesAndNonManual(session)) {
- if (repositories.isEmpty() || repositoryKeys.contains(rule.getRepositoryKey())) {
- DebtRulesXMLImporter.RuleDebt ruleDebt = ruleDebtByRule(rule, ruleDebts);
+ if (languageKey == null || languageKey.equals(rule.getLanguage())) {
+ RuleDebt ruleDebt = ruleDebtByRule(rule, ruleDebts);
if (ruleDebt == null) {
- rule.setCharacteristicId(disableCharacteristicWhenRuleNotFound ? RuleDto.DISABLED_CHARACTERISTIC_ID : null);
+ rule.setCharacteristicId(rule.getDefaultCharacteristicId() != null ? RuleDto.DISABLED_CHARACTERISTIC_ID : null);
rule.setRemediationFunction(null);
rule.setRemediationFactor(null);
rule.setRemediationOffset(null);
@@ -178,21 +227,15 @@ public class DebtModelRestore implements ServerComponent {
}
}
- for (DebtRulesXMLImporter.RuleDebt ruleDebt : ruleDebts) {
+ for (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
- List<CharacteristicDto> restoreCharacteristics(DebtModel targetModel, List<CharacteristicDto> sourceCharacteristics, Date updateDate, SqlSession session) {
+ List<CharacteristicDto> restoreCharacteristics(DebtModel targetModel, Date updateDate, SqlSession session) {
+ List<CharacteristicDto> sourceCharacteristics = dao.selectEnabledCharacteristics(session);
+
List<CharacteristicDto> result = newArrayList();
// Restore not existing characteristics
@@ -232,6 +275,14 @@ public class DebtModelRestore implements ServerComponent {
}
}
+ private static boolean isSameRemediationFunction(RuleDebt ruleDebt, RuleDto rule) {
+ return new EqualsBuilder()
+ .append(ruleDebt.function().name(), rule.getDefaultRemediationFunction())
+ .append(ruleDebt.factor(), rule.getDefaultRemediationFactor())
+ .append(ruleDebt.offset(), rule.getDefaultRemediationOffset())
+ .isEquals();
+ }
+
private DebtModel loadModelFromPlugin(String pluginKey) {
Reader xmlFileReader = null;
try {
@@ -242,8 +293,21 @@ public class DebtModelRestore implements ServerComponent {
}
}
- private CharacteristicDto characteristicByKey(final String key, List<CharacteristicDto> existingModel, boolean canByNull) {
- CharacteristicDto dto = Iterables.find(existingModel, new Predicate<CharacteristicDto>() {
+ @CheckForNull
+ private static RuleDebt ruleDebtByRule(final RuleDto rule, List<RuleDebt> ruleDebts) {
+ if (ruleDebts.isEmpty()) {
+ return null;
+ }
+ return Iterables.find(ruleDebts, new Predicate<RuleDebt>() {
+ @Override
+ public boolean apply(RuleDebt input) {
+ return rule.getRepositoryKey().equals(input.ruleKey().repository()) && rule.getRuleKey().equals(input.ruleKey().rule());
+ }
+ }, null);
+ }
+
+ private static CharacteristicDto characteristicByKey(final String key, List<CharacteristicDto> characteristicDtos, boolean canByNull) {
+ CharacteristicDto dto = Iterables.find(characteristicDtos, new Predicate<CharacteristicDto>() {
@Override
public boolean apply(CharacteristicDto input) {
return key.equals(input.getKey());
@@ -255,17 +319,25 @@ public class DebtModelRestore implements ServerComponent {
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>() {
+ private static List<CharacteristicDto> subCharacteristics(final Integer parentId, List<CharacteristicDto> allCharacteristics) {
+ return newArrayList(Iterables.filter(allCharacteristics, new Predicate<CharacteristicDto>() {
@Override
- public boolean apply(DebtRulesXMLImporter.RuleDebt input) {
- return rule.getRepositoryKey().equals(input.ruleKey().repository()) && rule.getRuleKey().equals(input.ruleKey().rule());
+ public boolean apply(CharacteristicDto input) {
+ return parentId.equals(input.getParentId());
}
- }, null);
+ }));
+ }
+
+ private static RuleDebt toRuleDebt(RuleDto rule, String characteristicKey) {
+ RuleDebt ruleDebt = new RuleDebt().setRuleKey(RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey()));
+ String function = rule.getRemediationFunction() != null ? rule.getRemediationFunction() : rule.getDefaultRemediationFunction();
+ String factor = rule.getRemediationFactor() != null ? rule.getRemediationFactor() : rule.getDefaultRemediationFactor();
+ String offset = rule.getRemediationOffset() != null ? rule.getRemediationOffset() : rule.getDefaultRemediationOffset();
+ ruleDebt.setCharacteristicKey(characteristicKey);
+ ruleDebt.setFunction(DebtRemediationFunction.Type.valueOf(function));
+ ruleDebt.setFactor(factor);
+ ruleDebt.setOffset(offset);
+ return ruleDebt;
}
private static CharacteristicDto toDto(DebtCharacteristic characteristic, @Nullable Integer parentId) {
@@ -279,6 +351,17 @@ public class DebtModelRestore implements ServerComponent {
.setUpdatedAt(characteristic.updatedAt());
}
+ private static DebtCharacteristic toDebtCharacteristic(CharacteristicDto characteristic) {
+ return new DefaultDebtCharacteristic()
+ .setId(characteristic.getId())
+ .setKey(characteristic.getKey())
+ .setName(characteristic.getName())
+ .setOrder(characteristic.getOrder())
+ .setParentId(characteristic.getParentId())
+ .setCreatedAt(characteristic.getCreatedAt())
+ .setUpdatedAt(characteristic.getUpdatedAt());
+ }
+
private void checkPermission() {
UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
}
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModelRepository.java b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java
index 89031c8304e..a770b93fa4d 100644
--- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModelRepository.java
+++ b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelPluginRepository.java
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.core.technicaldebt;
+package org.sonar.server.debt;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Maps;
@@ -44,8 +44,7 @@ import static com.google.common.collect.Lists.newArrayList;
* they must be named "<pluginKey>-model.xml".
* </p>
*/
-// TODO move it to sonar-server and rename it DebtModelPluginRepository when it will be no more used by the SQALE plugin
-public class TechnicalDebtModelRepository implements ServerExtension, Startable {
+public class DebtModelPluginRepository implements ServerExtension, Startable {
public static final String DEFAULT_MODEL = "technical-debt";
@@ -57,19 +56,19 @@ public class TechnicalDebtModelRepository implements ServerExtension, Startable
private PluginRepository pluginRepository;
private Map<String, ClassLoader> contributingPluginKeyToClassLoader;
- public TechnicalDebtModelRepository(PluginRepository pluginRepository) {
+ public DebtModelPluginRepository(PluginRepository pluginRepository) {
this.pluginRepository = pluginRepository;
this.xmlFilePrefix = XML_FILE_PREFIX;
}
@VisibleForTesting
- TechnicalDebtModelRepository(PluginRepository pluginRepository, String xmlFilePrefix) {
+ DebtModelPluginRepository(PluginRepository pluginRepository, String xmlFilePrefix) {
this.pluginRepository = pluginRepository;
this.xmlFilePrefix = xmlFilePrefix;
}
@VisibleForTesting
- TechnicalDebtModelRepository(Map<String, ClassLoader> contributingPluginKeyToClassLoader, String xmlFilePrefix) {
+ DebtModelPluginRepository(Map<String, ClassLoader> contributingPluginKeyToClassLoader, String xmlFilePrefix) {
this.contributingPluginKeyToClassLoader = contributingPluginKeyToClassLoader;
this.xmlFilePrefix = xmlFilePrefix;
}
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 a0777d8f528..40b1e27f48f 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
@@ -37,12 +37,12 @@ public class DebtModelService implements DebtModel {
private final DebtModelOperations debtModelOperations;
private final DebtModelLookup debtModelLookup;
- private final DebtModelRestore debtModelRestore;
+ private final DebtModelBackup debtModelBackup;
- public DebtModelService(DebtModelOperations debtModelOperations, DebtModelLookup debtModelLookup, DebtModelRestore debtModelRestore) {
+ public DebtModelService(DebtModelOperations debtModelOperations, DebtModelLookup debtModelLookup, DebtModelBackup debtModelBackup) {
this.debtModelOperations = debtModelOperations;
this.debtModelLookup = debtModelLookup;
- this.debtModelRestore = debtModelRestore;
+ this.debtModelBackup = debtModelBackup;
}
public List<DebtCharacteristic> characteristics() {
@@ -85,29 +85,37 @@ public class DebtModelService implements DebtModel {
/**
* Restore from provided model
*/
- public ValidationMessages restore(){
- return debtModelRestore.restore();
+ public void restore() {
+ debtModelBackup.restore();
}
/**
* Restore from plugins providing rules for a given language
*/
- public ValidationMessages restoreFromLanguage(String languageKey) {
- return debtModelRestore.restore(languageKey);
+ public void restoreFromLanguage(String languageKey) {
+ debtModelBackup.restore(languageKey);
}
/**
* Restore from XML
*/
- public ValidationMessages restoreFromXml(String xml){
- return debtModelRestore.restoreFromXml(xml);
+ public ValidationMessages restoreFromXml(String xml) {
+ return debtModelBackup.restoreFromXml(xml);
}
/**
* Restore from XML and a given language
*/
public ValidationMessages restoreFromXmlAndLanguage(String xml, String languageKey) {
- return debtModelRestore.restoreFromXml(xml, languageKey);
+ return debtModelBackup.restoreFromXml(xml, languageKey);
+ }
+
+ public String backup() {
+ return debtModelBackup.backup();
+ }
+
+ public String backupFromLanguage(String languageKey) {
+ return debtModelBackup.backup(languageKey);
}
}
diff --git a/sonar-server/src/main/java/org/sonar/server/debt/DebtModelXMLExporter.java b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelXMLExporter.java
new file mode 100644
index 00000000000..feefe213d77
--- /dev/null
+++ b/sonar-server/src/main/java/org/sonar/server/debt/DebtModelXMLExporter.java
@@ -0,0 +1,321 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.debt;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Ordering;
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.server.debt.DebtCharacteristic;
+import org.sonar.api.server.rule.DebtRemediationFunction;
+import org.xml.sax.InputSource;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.xml.transform.*;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * Export characteristics and rule debt definitions to XML
+ */
+public class DebtModelXMLExporter implements ServerComponent {
+
+ private static final String ROOT = "sqale";
+ private static final String DEFAULT_INDENT = "2";
+
+ public static final String CHARACTERISTIC = "chc";
+ public static final String CHARACTERISTIC_KEY = "key";
+ public static final String CHARACTERISTIC_NAME = "name";
+ public static final String PROPERTY = "prop";
+ public static final String PROPERTY_KEY = "key";
+ public static final String PROPERTY_VALUE = "val";
+ public static final String PROPERTY_TEXT_VALUE = "txt";
+
+ public static final String REPOSITORY_KEY = "rule-repo";
+ public static final String RULE_KEY = "rule-key";
+
+ public static final String PROPERTY_FUNCTION = "remediationFunction";
+ public static final String PROPERTY_FACTOR = "remediationFactor";
+ public static final String PROPERTY_OFFSET = "offset";
+
+ protected String export(DebtModel debtModel, List<RuleDebt> allRules) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<" + ROOT + ">");
+ for (DebtCharacteristic characteristic : debtModel.rootCharacteristics()) {
+ appendCharacteristic(debtModel, characteristic, allRules, sb);
+ }
+ sb.append("</" + ROOT + ">");
+ String xml = sb.toString();
+ xml = prettyFormatXml(xml);
+ return xml;
+ }
+
+ private void appendCharacteristic(DebtModel debtModel, DebtCharacteristic characteristic, List<RuleDebt> allRules, StringBuilder xml) {
+ xml.append("<" + CHARACTERISTIC + ">");
+ if (StringUtils.isNotBlank(characteristic.key())) {
+ xml.append("<" + CHARACTERISTIC_KEY + ">");
+ xml.append(StringEscapeUtils.escapeXml(characteristic.key()));
+ xml.append("</" + CHARACTERISTIC_KEY + "><" + CHARACTERISTIC_NAME + ">");
+ xml.append(StringEscapeUtils.escapeXml(characteristic.name()));
+ xml.append("</" + CHARACTERISTIC_NAME + ">");
+ }
+
+ if (characteristic.parentId() != null) {
+ List<RuleDebt> rules = rules(allRules, characteristic.key());
+ for (RuleDebt ruleDto : rules) {
+ appendRule(ruleDto, xml);
+ }
+ } else {
+ for (DebtCharacteristic child : debtModel.subCharacteristics(characteristic.key())) {
+ appendCharacteristic(debtModel, child, allRules, xml);
+ }
+ }
+ xml.append("</" + CHARACTERISTIC + ">");
+ }
+
+ private void appendRule(RuleDebt rule, StringBuilder xml) {
+ xml.append("<" + CHARACTERISTIC + ">");
+ xml.append("<" + REPOSITORY_KEY + ">");
+ xml.append(StringEscapeUtils.escapeXml(rule.ruleKey().repository()));
+ xml.append("</" + REPOSITORY_KEY + "><" + RULE_KEY + ">");
+ xml.append(StringEscapeUtils.escapeXml(rule.ruleKey().rule()));
+ xml.append("</" + RULE_KEY + ">");
+
+ String factor = rule.factor();
+ String offset = rule.offset();
+
+ appendProperty(PROPERTY_FUNCTION, null, rule.function().name(), xml);
+ if (factor != null) {
+ String[] values = getValues(factor);
+ appendProperty(PROPERTY_FACTOR, values[0], values[1], xml);
+ }
+ if (offset != null) {
+ String[] values = getValues(offset);
+ appendProperty(PROPERTY_OFFSET, values[0], values[1], xml);
+ }
+ xml.append("</" + CHARACTERISTIC + ">");
+ }
+
+ private static String[] getValues(String factorOrOffset) {
+ String[] result = new String[2];
+ Pattern pattern = Pattern.compile("(\\d+)(\\w+)");
+ Matcher matcher = pattern.matcher(factorOrOffset);
+ if (matcher.find()) {
+ String value = matcher.group(1);
+ String unit = matcher.group(2);
+ result[0] = value;
+ result[1] = unit;
+ }
+ return result;
+ }
+
+ private void appendProperty(String key, @Nullable String val, String text, StringBuilder xml) {
+ xml.append("<" + PROPERTY + "><" + PROPERTY_KEY + ">");
+ xml.append(StringEscapeUtils.escapeXml(key));
+ xml.append("</" + PROPERTY_KEY + ">");
+ if (val != null) {
+ xml.append("<" + PROPERTY_VALUE + ">");
+ xml.append(val);
+ xml.append("</" + PROPERTY_VALUE + ">");
+ }
+ if (StringUtils.isNotEmpty(text)) {
+ xml.append("<" + PROPERTY_TEXT_VALUE + ">");
+ xml.append(StringEscapeUtils.escapeXml(text));
+ xml.append("</" + PROPERTY_TEXT_VALUE + ">");
+ }
+ xml.append("</" + PROPERTY + ">");
+ }
+
+ private String prettyFormatXml(String xml) {
+ try {
+ Transformer serializer = SAXTransformerFactory.newInstance().newTransformer();
+ serializer.setOutputProperty(OutputKeys.INDENT, "yes");
+ serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", DEFAULT_INDENT);
+ Source xmlSource = new SAXSource(new InputSource(new ByteArrayInputStream(xml.getBytes())));
+ StreamResult res = new StreamResult(new ByteArrayOutputStream());
+ serializer.transform(xmlSource, res);
+ return new String(((ByteArrayOutputStream) res.getOutputStream()).toByteArray());
+ } catch (TransformerConfigurationException ignored) {
+ // Ignore, raw XML will be returned
+ } catch (TransformerException ignored) {
+ // Ignore, raw XML will be returned
+ }
+ return xml;
+ }
+
+ private List<RuleDebt> rules(List<RuleDebt> rules, final String parentKey) {
+ return newArrayList(Iterables.filter(rules, new Predicate<RuleDebt>() {
+ @Override
+ public boolean apply(RuleDebt input) {
+ return parentKey.equals(input.characteristicKey());
+ }
+ }));
+ }
+
+ public static class DebtModel {
+
+ private Multimap<String, DebtCharacteristic> characteristicsByRootKey;
+
+ public DebtModel() {
+ characteristicsByRootKey = ArrayListMultimap.create();
+ }
+
+ public DebtModel addRootCharacteristic(DebtCharacteristic characteristic) {
+ characteristicsByRootKey.put(null, characteristic);
+ return this;
+ }
+
+ public DebtModel addSubCharacteristic(DebtCharacteristic subCharacteristic, String characteristicKey) {
+ characteristicsByRootKey.put(characteristicKey, subCharacteristic);
+ return this;
+ }
+
+ /**
+ * @return root characteristics sorted by order
+ */
+ public List<DebtCharacteristic> rootCharacteristics() {
+ return sortByOrder(newArrayList(characteristicsByRootKey.get(null)));
+ }
+
+ /**
+ * @return root characteristics sorted by name
+ */
+ public List<DebtCharacteristic> subCharacteristics(String characteristicKey) {
+ return sortByName(newArrayList(characteristicsByRootKey.get(characteristicKey)));
+ }
+
+ @CheckForNull
+ public DebtCharacteristic characteristicByKey(final String key) {
+ return Iterables.find(characteristicsByRootKey.values(), new Predicate<DebtCharacteristic>() {
+ @Override
+ public boolean apply(DebtCharacteristic input) {
+ return key.equals(input.key());
+ }
+ }, null);
+ }
+
+ @CheckForNull
+ public DebtCharacteristic characteristicById(final Integer id) {
+ return Iterables.find(characteristicsByRootKey.values(), new Predicate<DebtCharacteristic>() {
+ @Override
+ public boolean apply(DebtCharacteristic input) {
+ return id.equals(input.id());
+ }
+ }, null);
+ }
+
+ private List<DebtCharacteristic> sortByOrder(List<DebtCharacteristic> characteristics) {
+ Collections.sort(characteristics, new Ordering<DebtCharacteristic>() {
+ public int compare(@Nullable DebtCharacteristic left, @Nullable DebtCharacteristic right) {
+ if (left == null || left.order() == null || right == null || right.order() == null) {
+ return -1;
+ }
+ return left.order() - right.order();
+ }
+ });
+ return characteristics;
+ }
+
+ private List<DebtCharacteristic> sortByName(List<DebtCharacteristic> characteristics) {
+ Collections.sort(characteristics, new Ordering<DebtCharacteristic>() {
+ public int compare(@Nullable DebtCharacteristic left, @Nullable DebtCharacteristic right) {
+ if (left == null || right == null) {
+ return -1;
+ }
+ return StringUtils.defaultString(left.name()).compareTo(StringUtils.defaultString(right.name()));
+ }
+ });
+ return characteristics;
+ }
+ }
+
+ public static class RuleDebt {
+ private RuleKey ruleKey;
+ private String characteristicKey;
+ private DebtRemediationFunction.Type type;
+ private String factor;
+ private String offset;
+
+ public RuleKey ruleKey() {
+ return ruleKey;
+ }
+
+ public RuleDebt setRuleKey(RuleKey ruleKey) {
+ this.ruleKey = ruleKey;
+ return this;
+ }
+
+ public String characteristicKey() {
+ return characteristicKey;
+ }
+
+ public RuleDebt setCharacteristicKey(String characteristicKey) {
+ this.characteristicKey = characteristicKey;
+ return this;
+ }
+
+ public DebtRemediationFunction.Type function() {
+ return type;
+ }
+
+ public RuleDebt setFunction(DebtRemediationFunction.Type type) {
+ this.type = type;
+ return this;
+ }
+
+ @CheckForNull
+ public String factor() {
+ return factor;
+ }
+
+ public RuleDebt setFactor(@Nullable String factor) {
+ this.factor = factor;
+ return this;
+ }
+
+ @CheckForNull
+ public String offset() {
+ return offset;
+ }
+
+ public RuleDebt setOffset(@Nullable String offset) {
+ this.offset = offset;
+ return this;
+ }
+ }
+
+}
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 762b11deacb..32cc6ae4b8f 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,7 +29,7 @@ import org.codehaus.stax2.XMLInputFactory2;
import org.codehaus.staxmate.SMInputFactory;
import org.codehaus.staxmate.in.SMHierarchicCursor;
import org.codehaus.staxmate.in.SMInputCursor;
-import org.sonar.api.ServerExtension;
+import org.sonar.api.ServerComponent;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.rule.DebtRemediationFunction;
import org.sonar.api.utils.Duration;
@@ -45,23 +45,12 @@ import java.io.StringReader;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
+import static org.sonar.server.debt.DebtModelXMLExporter.*;
-public class DebtRulesXMLImporter implements ServerExtension {
-
- public static final String CHARACTERISTIC = "chc";
- public static final String CHARACTERISTIC_KEY = "key";
- public static final String PROPERTY = "prop";
-
- public static final String PROPERTY_KEY = "key";
- public static final String PROPERTY_VALUE = "val";
- public static final String PROPERTY_TEXT_VALUE = "txt";
-
- public static final String REPOSITORY_KEY = "rule-repo";
- public static final String RULE_KEY = "rule-key";
-
- public static final String PROPERTY_FUNCTION = "remediationFunction";
- public static final String PROPERTY_FACTOR = "remediationFactor";
- public static final String PROPERTY_OFFSET = "offset";
+/**
+ * Import rules debt definitions from an XML
+ */
+public class DebtRulesXMLImporter implements ServerComponent {
public List<RuleDebt> importXML(String xml, ValidationMessages validationMessages) {
return importXML(new StringReader(xml), validationMessages);
@@ -107,12 +96,12 @@ public class DebtRulesXMLImporter implements ServerExtension {
process(ruleDebts, parentKey, currentCharacteristicKey, validationMessages, cursor);
} else if (StringUtils.equals(node, REPOSITORY_KEY)) {
RuleDebt ruleDebt = processRule(validationMessages, cursor);
- if (ruleDebt != null) {
+ if (ruleDebt != null && parentKey != null) {
if (rootKey != null) {
- ruleDebt.characteristicKey = parentKey;
+ ruleDebt.setCharacteristicKey(parentKey);
ruleDebts.add(ruleDebt);
} else {
- validationMessages.addWarningText("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.");
}
}
}
@@ -263,59 +252,4 @@ public class DebtRulesXMLImporter implements ServerExtension {
}
}
- public static class RuleDebt {
- private RuleKey ruleKey;
- private String characteristicKey;
- private DebtRemediationFunction.Type type;
- private String factor;
- private String offset;
-
- public RuleKey ruleKey() {
- return ruleKey;
- }
-
- public RuleDebt setRuleKey(RuleKey ruleKey) {
- this.ruleKey = ruleKey;
- return this;
- }
-
- public String characteristicKey() {
- return characteristicKey;
- }
-
- public RuleDebt setCharacteristicKey(String characteristicKey) {
- this.characteristicKey = characteristicKey;
- return this;
- }
-
- public DebtRemediationFunction.Type function() {
- return type;
- }
-
- public RuleDebt setFunction(DebtRemediationFunction.Type type) {
- this.type = type;
- return this;
- }
-
- @CheckForNull
- public String factor() {
- return factor;
- }
-
- public RuleDebt setFactor(@Nullable String factor) {
- this.factor = factor;
- return this;
- }
-
- @CheckForNull
- public String offset() {
- return offset;
- }
-
- public RuleDebt setOffset(@Nullable String offset) {
- this.offset = offset;
- return this;
- }
- }
-
}
diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
index 31b4a0234d8..9cd0c8c7aa7 100644
--- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
+++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
@@ -63,9 +63,6 @@ import org.sonar.core.qualitygate.db.QualityGateConditionDao;
import org.sonar.core.qualitygate.db.QualityGateDao;
import org.sonar.core.resource.DefaultResourcePermissions;
import org.sonar.core.rule.DefaultRuleFinder;
-import org.sonar.core.technicaldebt.TechnicalDebtModelRepository;
-import org.sonar.core.technicaldebt.TechnicalDebtModelSynchronizer;
-import org.sonar.core.technicaldebt.TechnicalDebtXMLImporter;
import org.sonar.core.test.TestPlanPerspectiveLoader;
import org.sonar.core.test.TestablePerspectiveLoader;
import org.sonar.core.timemachine.Periods;
@@ -266,7 +263,7 @@ class ServerComponents {
pico.addSingleton(AddTagsWsHandler.class);
pico.addSingleton(RemoveTagsWsHandler.class);
pico.addSingleton(RulesDefinitionXmlLoader.class);
-
+
// rule tags
pico.addSingleton(ESRuleTags.class);
pico.addSingleton(RuleTagLookup.class);
@@ -352,10 +349,9 @@ class ServerComponents {
pico.addSingleton(DebtModelService.class);
pico.addSingleton(DebtModelOperations.class);
pico.addSingleton(DebtModelLookup.class);
- pico.addSingleton(DebtModelRestore.class);
- pico.addSingleton(TechnicalDebtModelSynchronizer.class);
- pico.addSingleton(TechnicalDebtModelRepository.class);
- pico.addSingleton(TechnicalDebtXMLImporter.class);
+ pico.addSingleton(DebtModelBackup.class);
+ pico.addSingleton(DebtModelPluginRepository.class);
+ pico.addSingleton(DebtModelXMLExporter.class);
pico.addSingleton(DebtRulesXMLImporter.class);
pico.addSingleton(DebtCharacteristicsXMLImporter.class);
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 59b8b8f0a58..d9bd3ea9e34 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
@@ -34,7 +34,8 @@ 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;
+import org.sonar.server.debt.DebtModelPluginRepository;
+import org.sonar.server.debt.DebtModelXMLExporter;
import org.sonar.server.debt.DebtRulesXMLImporter;
import javax.annotation.CheckForNull;
@@ -44,6 +45,7 @@ import java.util.Collection;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
+import static org.sonar.server.debt.DebtModelXMLExporter.RuleDebt;
/**
* Inject deprecated RuleRepository into RuleDefinitions for backward-compatibility.
@@ -57,24 +59,24 @@ public class DeprecatedRulesDefinition implements RulesDefinition {
private final RuleI18nManager i18n;
private final RuleRepository[] repositories;
- private final TechnicalDebtModelRepository languageModelFinder;
+ private final DebtModelPluginRepository languageModelFinder;
private final DebtRulesXMLImporter importer;
- public DeprecatedRulesDefinition(RuleI18nManager i18n, RuleRepository[] repositories, TechnicalDebtModelRepository languageModelFinder, DebtRulesXMLImporter importer) {
+ public DeprecatedRulesDefinition(RuleI18nManager i18n, RuleRepository[] repositories, DebtModelPluginRepository languageModelFinder, DebtRulesXMLImporter importer) {
this.i18n = i18n;
this.repositories = repositories;
this.languageModelFinder = languageModelFinder;
this.importer = importer;
}
- public DeprecatedRulesDefinition(RuleI18nManager i18n, TechnicalDebtModelRepository languageModelFinder, DebtRulesXMLImporter importer) {
+ public DeprecatedRulesDefinition(RuleI18nManager i18n, DebtModelPluginRepository languageModelFinder, DebtRulesXMLImporter importer) {
this(i18n, new RuleRepository[0], languageModelFinder, importer);
}
@Override
public void define(Context context) {
// Load rule debt definitions from xml files provided by plugin
- List<DebtRulesXMLImporter.RuleDebt> ruleDebts = loadRuleDebtList();
+ List<RuleDebt> ruleDebts = loadRuleDebtList();
for (RuleRepository repository : repositories) {
// RuleRepository API does not handle difference between new and extended repositories,
@@ -106,8 +108,8 @@ public class DeprecatedRulesDefinition implements RulesDefinition {
}
}
- private void updateRuleDebtDefinitions(NewRule newRule, String repoKey, String ruleKey, List<DebtRulesXMLImporter.RuleDebt> ruleDebts){
- DebtRulesXMLImporter.RuleDebt ruleDebt = findRequirement(ruleDebts, repoKey, ruleKey);
+ private void updateRuleDebtDefinitions(NewRule newRule, String repoKey, String ruleKey, List<RuleDebt> ruleDebts){
+ RuleDebt ruleDebt = findRequirement(ruleDebts, repoKey, ruleKey);
if (ruleDebt != null) {
newRule.setDebtCharacteristic(ruleDebt.characteristicKey());
switch (ruleDebt.function()) {
@@ -153,20 +155,20 @@ public class DeprecatedRulesDefinition implements RulesDefinition {
return StringUtils.defaultIfBlank(desc, null);
}
- public List<DebtRulesXMLImporter.RuleDebt> loadRuleDebtList() {
- List<DebtRulesXMLImporter.RuleDebt> ruleDebtList = newArrayList();
+ public List<DebtModelXMLExporter.RuleDebt> loadRuleDebtList() {
+ List<RuleDebt> ruleDebtList = newArrayList();
for (String pluginKey : getContributingPluginListWithoutSqale()) {
ruleDebtList.addAll(loadRuleDebtsFromXml(pluginKey));
}
return ruleDebtList;
}
- public List<DebtRulesXMLImporter.RuleDebt> loadRuleDebtsFromXml(String pluginKey) {
+ public List<RuleDebt> loadRuleDebtsFromXml(String pluginKey) {
Reader xmlFileReader = null;
try {
xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey);
ValidationMessages validationMessages = ValidationMessages.create();
- List<DebtRulesXMLImporter.RuleDebt> rules = importer.importXML(xmlFileReader, validationMessages);
+ List<RuleDebt> rules = importer.importXML(xmlFileReader, validationMessages);
validationMessages.log(LOG);
return rules;
} finally {
@@ -176,15 +178,15 @@ public class DeprecatedRulesDefinition implements RulesDefinition {
private Collection<String> getContributingPluginListWithoutSqale() {
Collection<String> pluginList = newArrayList(languageModelFinder.getContributingPluginList());
- pluginList.remove(TechnicalDebtModelRepository.DEFAULT_MODEL);
+ pluginList.remove(DebtModelPluginRepository.DEFAULT_MODEL);
return pluginList;
}
@CheckForNull
- private DebtRulesXMLImporter.RuleDebt findRequirement(List<DebtRulesXMLImporter.RuleDebt> requirements, final String repoKey, final String ruleKey) {
- return Iterables.find(requirements, new Predicate<DebtRulesXMLImporter.RuleDebt>() {
+ private RuleDebt findRequirement(List<RuleDebt> requirements, final String repoKey, final String ruleKey) {
+ return Iterables.find(requirements, new Predicate<RuleDebt>() {
@Override
- public boolean apply(DebtRulesXMLImporter.RuleDebt input) {
+ public boolean apply(RuleDebt input) {
return input.ruleKey().equals(RuleKey.of(repoKey, ruleKey));
}
}, null);
diff --git a/sonar-server/src/main/java/org/sonar/server/startup/RegisterDebtModel.java b/sonar-server/src/main/java/org/sonar/server/startup/RegisterDebtModel.java
index 26a5c94444a..f482fe1117c 100644
--- a/sonar-server/src/main/java/org/sonar/server/startup/RegisterDebtModel.java
+++ b/sonar-server/src/main/java/org/sonar/server/startup/RegisterDebtModel.java
@@ -24,24 +24,24 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.utils.TimeProfiler;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
-import org.sonar.server.debt.DebtModelRestore;
+import org.sonar.server.debt.DebtModelBackup;
public class RegisterDebtModel {
private static final Logger LOGGER = LoggerFactory.getLogger(RegisterDebtModel.class);
private final CharacteristicDao dao;
- private final DebtModelRestore debtModelRestore;
+ private final DebtModelBackup debtModelBackup;
- public RegisterDebtModel(CharacteristicDao dao, DebtModelRestore debtModelRestore) {
+ public RegisterDebtModel(CharacteristicDao dao, DebtModelBackup debtModelBackup) {
this.dao = dao;
- this.debtModelRestore = debtModelRestore;
+ this.debtModelBackup = debtModelBackup;
}
public void start() {
TimeProfiler profiler = new TimeProfiler(LOGGER).start("Register technical debt model");
if (dao.selectEnabledCharacteristics().isEmpty()) {
- debtModelRestore.restore();
+ debtModelBackup.restore();
}
profiler.stop();
}
diff --git a/sonar-server/src/test/java/org/sonar/server/debt/DebtCharacteristicsXMLImporterTest.java b/sonar-server/src/test/java/org/sonar/server/debt/DebtCharacteristicsXMLImporterTest.java
index 3f6224862df..a6b0bd3b8bb 100644
--- a/sonar-server/src/test/java/org/sonar/server/debt/DebtCharacteristicsXMLImporterTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/debt/DebtCharacteristicsXMLImporterTest.java
@@ -30,6 +30,7 @@ import java.util.List;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
+import static org.sonar.server.debt.DebtModelXMLExporter.DebtModel;
public class DebtCharacteristicsXMLImporterTest {
@@ -97,7 +98,7 @@ public class DebtCharacteristicsXMLImporterTest {
private String getFileContent(String file) {
try {
- return Resources.toString(Resources.getResource(DebtCharacteristicsXMLImporterTest.class, "DebtCharacteristicsXMLImporterTest/" + file), Charsets.UTF_8);
+ return Resources.toString(Resources.getResource(this.getClass(), this.getClass().getSimpleName() + "/" + file), Charsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
diff --git a/sonar-server/src/test/java/org/sonar/server/debt/DebtModelRestoreTest.java b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelBackupTest.java
index 05b8ac50a92..2127ac817bb 100644
--- a/sonar-server/src/test/java/org/sonar/server/debt/DebtModelRestoreTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelBackupTest.java
@@ -25,6 +25,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
@@ -39,13 +40,12 @@ import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.rule.RuleDao;
import org.sonar.core.rule.RuleDto;
-import org.sonar.core.technicaldebt.TechnicalDebtModelRepository;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
-import org.sonar.server.rule.RuleRepositories;
import org.sonar.server.user.MockUserSession;
import java.io.Reader;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -55,9 +55,11 @@ import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
+import static org.sonar.server.debt.DebtModelXMLExporter.DebtModel;
+import static org.sonar.server.debt.DebtModelXMLExporter.RuleDebt;
@RunWith(MockitoJUnitRunner.class)
-public class DebtModelRestoreTest {
+public class DebtModelBackupTest {
@Mock
MyBatis myBatis;
@@ -66,7 +68,7 @@ public class DebtModelRestoreTest {
SqlSession session;
@Mock
- TechnicalDebtModelRepository debtModelPluginRepository;
+ DebtModelPluginRepository debtModelPluginRepository;
@Mock
CharacteristicDao dao;
@@ -84,19 +86,29 @@ public class DebtModelRestoreTest {
DebtRulesXMLImporter rulesXMLImporter;
@Mock
- RuleRepositories ruleRepositories;
+ DebtModelXMLExporter debtModelXMLExporter;
@Mock
System2 system2;
+ @Captor
+ ArgumentCaptor<CharacteristicDto> characteristicArgument;
+
+ @Captor
+ ArgumentCaptor<RuleDto> ruleArgument;
+
+ @Captor
+ ArgumentCaptor<ArrayList<RuleDebt>> ruleDebtListCaptor;
+
+ Date oldDate = DateUtils.parseDate("2014-01-01");
Date now = DateUtils.parseDate("2014-03-19");
int currentId;
- DebtModel characteristics = new DebtModel();
- List<DebtRulesXMLImporter.RuleDebt> rules = newArrayList();
+ DebtModel debtModel = new DebtModel();
+ List<DebtModelXMLExporter.RuleDebt> rules = newArrayList();
- DebtModelRestore debtModelRestore;
+ DebtModelBackup debtModelBackup;
@Before
public void setUp() throws Exception {
@@ -119,26 +131,123 @@ public class DebtModelRestoreTest {
Reader defaultModelReader = mock(Reader.class);
when(debtModelPluginRepository.createReaderForXMLFile("technical-debt")).thenReturn(defaultModelReader);
- when(characteristicsXMLImporter.importXML(eq(defaultModelReader))).thenReturn(characteristics);
- when(characteristicsXMLImporter.importXML(anyString())).thenReturn(characteristics);
+ when(characteristicsXMLImporter.importXML(eq(defaultModelReader))).thenReturn(debtModel);
+ when(characteristicsXMLImporter.importXML(anyString())).thenReturn(debtModel);
when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(rules);
- debtModelRestore = new DebtModelRestore(myBatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, ruleRepositories, characteristicsXMLImporter, rulesXMLImporter,
- system2);
+ debtModelBackup = new DebtModelBackup(myBatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, characteristicsXMLImporter, rulesXMLImporter,
+ debtModelXMLExporter, system2);
+ }
+
+ @Test
+ public void backup() throws Exception {
+ when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1)
+ ));
+
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+ // Rule with overridden debt values
+ new RuleDto().setRepositoryKey("squid").setRuleKey("UselessImportCheck").setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min"),
+
+ // Rule with default debt values
+ new RuleDto().setRepositoryKey("squid").setRuleKey("AvoidNPE").setDefaultCharacteristicId(2).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
+ ));
+
+ debtModelBackup.backup();
+
+ ArgumentCaptor<DebtModel> debtModelArgument = ArgumentCaptor.forClass(DebtModel.class);
+ verify(debtModelXMLExporter).export(debtModelArgument.capture(), ruleDebtListCaptor.capture());
+ assertThat(debtModelArgument.getValue().rootCharacteristics()).hasSize(1);
+ assertThat(debtModelArgument.getValue().subCharacteristics("PORTABILITY")).hasSize(1);
+
+ List<RuleDebt> rules = ruleDebtListCaptor.getValue();
+ assertThat(rules).hasSize(2);
+
+ RuleDebt rule = rules.get(0);
+ assertThat(rule.ruleKey().repository()).isEqualTo("squid");
+ assertThat(rule.ruleKey().rule()).isEqualTo("UselessImportCheck");
+ assertThat(rule.characteristicKey()).isEqualTo("COMPILER");
+ assertThat(rule.function().name()).isEqualTo("LINEAR_OFFSET");
+ assertThat(rule.factor()).isEqualTo("2h");
+ assertThat(rule.offset()).isEqualTo("15min");
+
+ rule = rules.get(1);
+ assertThat(rule.ruleKey().repository()).isEqualTo("squid");
+ assertThat(rule.ruleKey().rule()).isEqualTo("AvoidNPE");
+ assertThat(rule.characteristicKey()).isEqualTo("COMPILER");
+ assertThat(rule.function().name()).isEqualTo("LINEAR");
+ assertThat(rule.factor()).isEqualTo("2h");
+ assertThat(rule.offset()).isNull();
+ }
+
+ @Test
+ public void backup_with_disabled_rules() throws Exception {
+ when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1)
+ ));
+
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+ // Debt disabled
+ new RuleDto().setRepositoryKey("squid").setRuleKey("UselessImportCheck").setCharacteristicId(RuleDto.DISABLED_CHARACTERISTIC_ID),
+
+ // Not debt
+ new RuleDto().setRepositoryKey("squid").setRuleKey("AvoidNPE")
+ ));
+
+ debtModelBackup.backup();
+
+ verify(debtModelXMLExporter).export(any(DebtModel.class), ruleDebtListCaptor.capture());
+
+ assertThat(ruleDebtListCaptor.getValue()).isEmpty();
+ }
+
+ @Test
+ public void backup_from_language() throws Exception {
+ when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1)
+ ));
+
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+ new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck").setLanguage("java")
+ .setCharacteristicId(2).setRemediationFunction("CONSTANT_ISSUE").setRemediationOffset("15min")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate),
+ // Should be ignored
+ new RuleDto().setId(2).setRepositoryKey("checkstyle").setLanguage("java2")
+ .setCharacteristicId(3).setRemediationFunction("LINEAR").setRemediationFactor("2h")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelBackup.backup("java");
+
+ verify(debtModelXMLExporter).export(any(DebtModel.class), ruleDebtListCaptor.capture());
+
+ List<RuleDebt> rules = ruleDebtListCaptor.getValue();
+ assertThat(rules).hasSize(1);
+
+ RuleDebt rule = rules.get(0);
+ assertThat(rule.ruleKey().repository()).isEqualTo("squid");
+ assertThat(rule.ruleKey().rule()).isEqualTo("UselessImportCheck");
+ assertThat(rule.characteristicKey()).isEqualTo("COMPILER");
+ assertThat(rule.function().name()).isEqualTo("CONSTANT_ISSUE");
+ assertThat(rule.factor()).isNull();
+ assertThat(rule.offset()).isEqualTo("15min");
}
@Test
public void create_characteristics_when_restoring_characteristics() throws Exception {
- debtModelRestore.restoreCharacteristics(
+ when(dao.selectEnabledCharacteristics(session)).thenReturn(Collections.<CharacteristicDto>emptyList());
+
+ debtModelBackup.restoreCharacteristics(
new DebtModel()
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
.addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"),
- Collections.<CharacteristicDto>emptyList(),
now,
session
);
- ArgumentCaptor<CharacteristicDto> characteristicArgument = ArgumentCaptor.forClass(CharacteristicDto.class);
verify(dao, times(2)).insert(characteristicArgument.capture(), eq(session));
CharacteristicDto dto1 = characteristicArgument.getAllValues().get(0);
@@ -162,21 +271,19 @@ public class DebtModelRestoreTest {
@Test
public void update_characteristics_when_restoring_characteristics() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
+ when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate).setUpdatedAt(oldDate),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
- debtModelRestore.restoreCharacteristics(
+ debtModelBackup.restoreCharacteristics(
new DebtModel()
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
.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").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate).setUpdatedAt(oldDate)
- ),
now,
session
);
- ArgumentCaptor<CharacteristicDto> characteristicArgument = ArgumentCaptor.forClass(CharacteristicDto.class);
verify(dao, times(2)).update(characteristicArgument.capture(), eq(session));
CharacteristicDto dto1 = characteristicArgument.getAllValues().get(0);
@@ -203,7 +310,9 @@ public class DebtModelRestoreTest {
CharacteristicDto dto1 = new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1);
CharacteristicDto dto2 = new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1);
- debtModelRestore.restoreCharacteristics(new DebtModel(), newArrayList(dto1, dto2), now, session);
+ when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(dto1, dto2));
+
+ debtModelBackup.restoreCharacteristics(new DebtModel(), now, session);
verify(debtModelOperations).disableCharacteristic(dto1, now, session);
verify(debtModelOperations).disableCharacteristic(dto2, now, session);
@@ -211,13 +320,11 @@ public class DebtModelRestoreTest {
@Test
public void restore_from_provided_model() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
+ debtModel
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
.addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate),
new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate)
));
@@ -227,14 +334,13 @@ public class DebtModelRestoreTest {
.setCreatedAt(oldDate).setUpdatedAt(oldDate)
));
- debtModelRestore.restore();
+ debtModelBackup.restore();
- verify(dao).selectEnabledCharacteristics();
+ verify(dao).selectEnabledCharacteristics(session);
verify(dao, times(2)).update(any(CharacteristicDto.class), eq(session));
verifyNoMoreInteractions(dao);
verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
verify(ruleDao).update(ruleArgument.capture(), eq(session));
verifyNoMoreInteractions(ruleDao);
@@ -250,39 +356,32 @@ public class DebtModelRestoreTest {
@Test
public void restore_from_language() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
+ debtModel
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
.addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate),
new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate)
));
when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
- new RuleDto().setId(1).setRepositoryKey("squid")
+ new RuleDto().setId(1).setRepositoryKey("squid").setLanguage("java")
.setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
.setCreatedAt(oldDate).setUpdatedAt(oldDate),
// Should be ignored
- new RuleDto().setId(2).setRepositoryKey("checkstyle")
+ new RuleDto().setId(2).setRepositoryKey("checkstyle").setLanguage("java2")
.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));
+ debtModelBackup.restore("java");
- debtModelRestore.restore("java");
-
- verify(dao).selectEnabledCharacteristics();
+ verify(dao).selectEnabledCharacteristics(session);
verify(dao, times(2)).update(any(CharacteristicDto.class), eq(session));
verifyNoMoreInteractions(dao);
verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
verify(ruleDao).update(ruleArgument.capture(), eq(session));
verifyNoMoreInteractions(ruleDao);
@@ -294,17 +393,15 @@ public class DebtModelRestoreTest {
@Test
public void restore_from_xml_with_different_characteristic_and_same_function() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
+ debtModel
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
.addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ when(dao.selectEnabledCharacteristics(session)).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()
+ rules.add(new RuleDebt()
.setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h"));
when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
@@ -313,10 +410,9 @@ public class DebtModelRestoreTest {
.setCreatedAt(oldDate).setUpdatedAt(oldDate)
));
- debtModelRestore.restoreFromXml("<xml/>");
+ debtModelBackup.restoreFromXml("<xml/>");
verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
verify(ruleDao).update(ruleArgument.capture(), eq(session));
verifyNoMoreInteractions(ruleDao);
@@ -333,17 +429,15 @@ public class DebtModelRestoreTest {
@Test
public void restore_from_xml_with_same_characteristic_and_different_function() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
+ debtModel
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
.addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ when(dao.selectEnabledCharacteristics(session)).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()
+ rules.add(new RuleDebt()
.setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET).setFactor("12h").setOffset("11min"));
when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
@@ -352,10 +446,9 @@ public class DebtModelRestoreTest {
.setCreatedAt(oldDate).setUpdatedAt(oldDate)
));
- debtModelRestore.restoreFromXml("<xml/>");
+ debtModelBackup.restoreFromXml("<xml/>");
verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
verify(ruleDao).update(ruleArgument.capture(), eq(session));
verifyNoMoreInteractions(ruleDao);
@@ -372,17 +465,15 @@ public class DebtModelRestoreTest {
@Test
public void restore_from_xml_with_same_characteristic_and_same_function() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
+ debtModel
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
.addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ when(dao.selectEnabledCharacteristics(session)).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()
+ rules.add(new RuleDebt()
.setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET).setFactor("2h").setOffset("15min"));
when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
@@ -391,10 +482,9 @@ public class DebtModelRestoreTest {
.setCreatedAt(oldDate).setUpdatedAt(oldDate)
));
- debtModelRestore.restoreFromXml("<xml/>");
+ debtModelBackup.restoreFromXml("<xml/>");
verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
verify(ruleDao).update(ruleArgument.capture(), eq(session));
verifyNoMoreInteractions(ruleDao);
@@ -410,14 +500,12 @@ public class DebtModelRestoreTest {
}
@Test
- public void restore_from_xml_disable_rule_debt_when_not_in_xml() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
+ public void restore_from_xml_disable_rule_debt_when_not_in_xml_and_rule_have_default_debt_values() throws Exception {
+ debtModel
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
.addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ when(dao.selectEnabledCharacteristics(session)).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)));
@@ -427,10 +515,9 @@ public class DebtModelRestoreTest {
.setCreatedAt(oldDate).setUpdatedAt(oldDate)
));
- debtModelRestore.restoreFromXml("<xml/>");
+ debtModelBackup.restoreFromXml("<xml/>");
verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
verify(ruleDao).update(ruleArgument.capture(), eq(session));
verifyNoMoreInteractions(ruleDao);
@@ -446,38 +533,64 @@ public class DebtModelRestoreTest {
}
@Test
- public void restore_from_xml_and_language() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
+ public void restore_from_xml_set_no_rule_debt_when_not_in_xml_and_rule_has_no_default_debt_values() throws Exception {
+ debtModel
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
+
+ when(dao.selectEnabledCharacteristics(session)).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")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelBackup.restoreFromXml("<xml/>");
- characteristics
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+ // As rule has no debt value, characteristic is set to null
+ 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_and_language() throws Exception {
+ debtModel
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
.addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ when(dao.selectEnabledCharacteristics(session)).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()
+ rules.add(new 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")
+ new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck").setLanguage("java")
.setDefaultCharacteristicId(10).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
.setCreatedAt(oldDate).setUpdatedAt(oldDate),
// Should be ignored
- new RuleDto().setId(2).setRepositoryKey("checkstyle")
+ new RuleDto().setId(2).setRepositoryKey("checkstyle").setLanguage("java2")
.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");
+ debtModelBackup.restoreFromXml("<xml/>", "java");
verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
verify(ruleDao).update(ruleArgument.capture(), eq(session));
verifyNoMoreInteractions(ruleDao);
@@ -489,22 +602,20 @@ public class DebtModelRestoreTest {
@Test
public void add_warning_message_when_rule_from_xml_is_not_found() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
+ debtModel
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
.addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
+ when(dao.selectEnabledCharacteristics(session)).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()
+ rules.add(new 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/>");
+ ValidationMessages validationMessages = debtModelBackup.restoreFromXml("<xml/>");
assertThat(validationMessages.getWarnings()).hasSize(1);
diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelRepositoryTest.java b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelPluginRepositoryTest.java
index f74e95f2e7a..0197ce7ec9a 100644
--- a/sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtModelRepositoryTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelPluginRepositoryTest.java
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.core.technicaldebt;
+package org.sonar.server.debt;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -46,11 +46,11 @@ import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-public class TechnicalDebtModelRepositoryTest {
+public class DebtModelPluginRepositoryTest {
- private static final String TEST_XML_PREFIX_PATH = "org/sonar/core/technicaldebt/TechnicalDebtModelRepositoryTest/";
+ private static final String TEST_XML_PREFIX_PATH = "org/sonar/server/debt/DebtModelPluginRepositoryTest/";
- private TechnicalDebtModelRepository modelFinder;
+ private DebtModelPluginRepository modelFinder;
@Test
public void test_component_initialization() throws Exception {
@@ -66,7 +66,7 @@ public class TechnicalDebtModelRepositoryTest {
when(repository.getMetadata()).thenReturn(Lists.newArrayList(csharpPluginMetadata, phpPluginMetadata));
FakePlugin fakePlugin = new FakePlugin();
when(repository.getPlugin(anyString())).thenReturn(fakePlugin);
- modelFinder = new TechnicalDebtModelRepository(repository, TEST_XML_PREFIX_PATH);
+ modelFinder = new DebtModelPluginRepository(repository, TEST_XML_PREFIX_PATH);
// when
modelFinder.start();
@@ -110,7 +110,7 @@ public class TechnicalDebtModelRepositoryTest {
@Test
public void contain_default_model() throws Exception {
- modelFinder = new TechnicalDebtModelRepository(mock(PluginRepository.class));
+ modelFinder = new DebtModelPluginRepository(mock(PluginRepository.class));
modelFinder.start();
assertThat(modelFinder.getContributingPluginKeyToClassLoader().keySet()).containsOnly("technical-debt");
}
@@ -119,7 +119,7 @@ public class TechnicalDebtModelRepositoryTest {
Map<String, ClassLoader> contributingPluginKeyToClassLoader = Maps.newHashMap();
contributingPluginKeyToClassLoader.put("csharp", newClassLoader());
contributingPluginKeyToClassLoader.put("java", newClassLoader());
- modelFinder = new TechnicalDebtModelRepository(contributingPluginKeyToClassLoader, TEST_XML_PREFIX_PATH);
+ modelFinder = new DebtModelPluginRepository(contributingPluginKeyToClassLoader, TEST_XML_PREFIX_PATH);
}
private ClassLoader newClassLoader() throws MalformedURLException {
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 59051abf5f6..ee08ba6ca95 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
@@ -37,13 +37,13 @@ public class DebtModelServiceTest {
DebtModelLookup debtModelLookup;
@Mock
- DebtModelRestore debtModelRestore;
+ DebtModelBackup debtModelBackup;
DebtModelService service;
@Before
public void setUp() throws Exception {
- service = new DebtModelService(debtModelOperations, debtModelLookup, debtModelRestore);
+ service = new DebtModelService(debtModelOperations, debtModelLookup, debtModelBackup);
}
@Test
@@ -97,25 +97,36 @@ public class DebtModelServiceTest {
@Test
public void restore_provided_model() {
service.restore();
- verify(debtModelRestore).restore();
+ verify(debtModelBackup).restore();
}
@Test
public void restore_from_language() {
service.restoreFromLanguage("xoo");
- verify(debtModelRestore).restore("xoo");
+ verify(debtModelBackup).restore("xoo");
}
@Test
public void restore_xml() {
service.restoreFromXml("<xml/>");
- verify(debtModelRestore).restoreFromXml("<xml/>");
+ verify(debtModelBackup).restoreFromXml("<xml/>");
}
@Test
public void restore_from_xml_and_language() {
service.restoreFromXmlAndLanguage("<xml/>", "xoo");
- verify(debtModelRestore).restoreFromXml("<xml/>", "xoo");
+ verify(debtModelBackup).restoreFromXml("<xml/>", "xoo");
}
+ @Test
+ public void backup() {
+ service.backup();
+ verify(debtModelBackup).backup();
+ }
+
+ @Test
+ public void backup_fom_language() {
+ service.backupFromLanguage("xoo");
+ verify(debtModelBackup).backup("xoo");
+ }
}
diff --git a/sonar-server/src/test/java/org/sonar/server/debt/DebtModelXMLExporterTest.java b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelXMLExporterTest.java
new file mode 100644
index 00000000000..a40df68a64f
--- /dev/null
+++ b/sonar-server/src/test/java/org/sonar/server/debt/DebtModelXMLExporterTest.java
@@ -0,0 +1,142 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.debt;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import org.apache.commons.lang.SystemUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.api.server.rule.DebtRemediationFunction;
+import org.sonar.test.TestUtils;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.sonar.server.debt.DebtModelXMLExporter.DebtModel;
+import static org.sonar.server.debt.DebtModelXMLExporter.RuleDebt;
+
+public class DebtModelXMLExporterTest {
+
+ private DebtModelXMLExporter xmlExporter;
+
+ @Before
+ public void setup() {
+ xmlExporter = new DebtModelXMLExporter();
+ }
+
+ @Test
+ public void export_empty() {
+ assertThat(xmlExporter.export(new DebtModel(), Collections.<RuleDebt>emptyList())).isEqualTo("<sqale/>" + SystemUtils.LINE_SEPARATOR);
+ }
+
+ @Test
+ public void export_xml() throws Exception {
+ DebtModel debtModel = new DebtModel()
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setId(1).setKey("USABILITY").setName("Usability").setOrder(1))
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setId(2).setKey("EFFICIENCY").setName("Efficiency").setOrder(2))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setId(3).setKey("MEMORY_USE").setName("Memory use").setParentId(2), "EFFICIENCY");
+
+ List<RuleDebt> rules = newArrayList(
+ new RuleDebt().setRuleKey(RuleKey.of("checkstyle", "Regexp"))
+ .setCharacteristicKey("MEMORY_USE").setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET).setFactor("3d").setOffset("15min")
+ );
+
+ TestUtils.assertSimilarXml(getFileContent("export_xml.xml"), xmlExporter.export(debtModel, rules));
+ }
+
+ @Test
+ public void sort_root_characteristics_by_order_and_sub_characteristics_by_name() throws Exception {
+ DebtModel debtModel = new DebtModel()
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("EFFICIENCY").setName("Efficiency").setOrder(4))
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("USABILITY").setName("Usability").setOrder(3))
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(2))
+
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("RAM_EFFICIENCY").setName("RAM Efficiency"), "EFFICIENCY")
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("CPU_EFFICIENCY").setName("CPU Efficiency"), "EFFICIENCY")
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("OTHER_EFFICIENCY").setName("Other Efficiency"), "EFFICIENCY");
+
+ String xml = xmlExporter.export(debtModel, Collections.<RuleDebt>emptyList());
+
+ // root characteristics are sorted by the column "characteristic_order"
+ Pattern regex = Pattern.compile(".*USABILITY.*PORTABILITY.*EFFICIENCY.*", Pattern.DOTALL);
+ assertThat(regex.matcher(xml).matches());
+
+ // sub characteristics are sorted by name
+ regex = Pattern.compile(".*CPU Efficiency.*Other Efficiency.*RAM Efficiency.*", Pattern.DOTALL);
+ assertThat(regex.matcher(xml).matches());
+ }
+
+ @Test
+ public void pretty_print_exported_xml() throws Exception {
+ DebtModel debtModel = new DebtModel()
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setId(1).setKey("USABILITY").setName("Usability").setOrder(1))
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setId(2).setKey("EFFICIENCY").setName("Efficiency").setOrder(2))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setId(3).setKey("MEMORY_USE").setName("Memory use").setParentId(2), "EFFICIENCY");
+
+ List<RuleDebt> rules = newArrayList(
+ new RuleDebt().setRuleKey(RuleKey.of("checkstyle", "Regexp"))
+ .setCharacteristicKey("MEMORY_USE").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("3d")
+ );
+ assertThat(xmlExporter.export(debtModel, rules)).isEqualTo(
+ "<sqale>" + SystemUtils.LINE_SEPARATOR +
+ " <chc>" + SystemUtils.LINE_SEPARATOR +
+ " <key>USABILITY</key>" + SystemUtils.LINE_SEPARATOR +
+ " <name>Usability</name>" + SystemUtils.LINE_SEPARATOR +
+ " </chc>" + SystemUtils.LINE_SEPARATOR +
+ " <chc>" + SystemUtils.LINE_SEPARATOR +
+ " <key>EFFICIENCY</key>" + SystemUtils.LINE_SEPARATOR +
+ " <name>Efficiency</name>" + SystemUtils.LINE_SEPARATOR +
+ " <chc>" + SystemUtils.LINE_SEPARATOR +
+ " <key>MEMORY_USE</key>" + SystemUtils.LINE_SEPARATOR +
+ " <name>Memory use</name>" + SystemUtils.LINE_SEPARATOR +
+ " <chc>" + SystemUtils.LINE_SEPARATOR +
+ " <rule-repo>checkstyle</rule-repo>" + SystemUtils.LINE_SEPARATOR +
+ " <rule-key>Regexp</rule-key>" + SystemUtils.LINE_SEPARATOR +
+ " <prop>" + SystemUtils.LINE_SEPARATOR +
+ " <key>remediationFunction</key>" + SystemUtils.LINE_SEPARATOR +
+ " <txt>LINEAR</txt>" + SystemUtils.LINE_SEPARATOR +
+ " </prop>" + SystemUtils.LINE_SEPARATOR +
+ " <prop>" + SystemUtils.LINE_SEPARATOR +
+ " <key>remediationFactor</key>" + SystemUtils.LINE_SEPARATOR +
+ " <val>3</val>" + SystemUtils.LINE_SEPARATOR +
+ " <txt>d</txt>" + SystemUtils.LINE_SEPARATOR +
+ " </prop>" + SystemUtils.LINE_SEPARATOR +
+ " </chc>" + SystemUtils.LINE_SEPARATOR +
+ " </chc>" + SystemUtils.LINE_SEPARATOR +
+ " </chc>" + SystemUtils.LINE_SEPARATOR +
+ "</sqale>" + SystemUtils.LINE_SEPARATOR
+ );
+ }
+
+ private String getFileContent(String file) {
+ try {
+ return Resources.toString(Resources.getResource(this.getClass(), this.getClass().getSimpleName() + "/" + file), Charsets.UTF_8);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
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 e1e8f82b2bc..390636ac0f6 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
@@ -32,6 +32,7 @@ import java.util.List;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
+import static org.sonar.server.debt.DebtModelXMLExporter.RuleDebt;
public class DebtRulesXMLImporterTest {
@@ -42,7 +43,7 @@ public class DebtRulesXMLImporterTest {
public void import_rules() {
String xml = getFileContent("import_rules.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(2);
assertThat(validationMessages.getErrors()).isEmpty();
@@ -53,10 +54,10 @@ public class DebtRulesXMLImporterTest {
public void import_linear() {
String xml = getFileContent("import_linear.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
- DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
+ RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
assertThat(ruleDebt.ruleKey()).isEqualTo(RuleKey.of("checkstyle", "Regexp"));
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
@@ -68,10 +69,10 @@ 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, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
- DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
+ RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
assertThat(ruleDebt.ruleKey()).isEqualTo(RuleKey.of("checkstyle", "Regexp"));
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
@@ -83,10 +84,10 @@ public class DebtRulesXMLImporterTest {
public void import_linear_with_offset() {
String xml = getFileContent("import_linear_with_offset.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
- DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
+ RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
assertThat(ruleDebt.factor()).isEqualTo("3h");
@@ -97,10 +98,10 @@ public class DebtRulesXMLImporterTest {
public void import_constant_issue() {
String xml = getFileContent("import_constant_issue.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
- DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
+ RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE);
assertThat(ruleDebt.factor()).isNull();
@@ -111,10 +112,10 @@ 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, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
- DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
+ RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
assertThat(ruleDebt.factor()).isEqualTo("3d");
@@ -125,10 +126,10 @@ public class DebtRulesXMLImporterTest {
public void replace_mn_by_min() {
String xml = getFileContent("replace_mn_by_min.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
- DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
+ RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
assertThat(ruleDebt.factor()).isEqualTo("3min");
@@ -136,13 +137,28 @@ public class DebtRulesXMLImporterTest {
}
@Test
+ public void read_integer() {
+ String xml = getFileContent("read_integer.xml");
+
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
+ assertThat(results).hasSize(1);
+
+ RuleDebt ruleDebt = results.get(0);
+ assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
+ assertThat(ruleDebt.ruleKey()).isEqualTo(RuleKey.of("checkstyle", "Regexp"));
+ assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
+ assertThat(ruleDebt.factor()).isEqualTo("3h");
+ assertThat(ruleDebt.offset()).isNull();
+ }
+
+ @Test
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, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
- DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
+ RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.LINEAR);
assertThat(ruleDebt.factor()).isEqualTo("3h");
@@ -155,10 +171,10 @@ public class DebtRulesXMLImporterTest {
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, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
- DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
+ RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
assertThat(ruleDebt.function()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE);
assertThat(ruleDebt.factor()).isNull();
@@ -169,7 +185,7 @@ 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, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).isEmpty();
assertThat(validationMessages.getWarnings()).isNotEmpty();
@@ -179,7 +195,7 @@ public class DebtRulesXMLImporterTest {
public void ignore_rule_on_root_characteristics() {
String xml = getFileContent("ignore_rule_on_root_characteristics.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).isEmpty();
assertThat(validationMessages.getWarnings()).isNotEmpty();
@@ -189,10 +205,10 @@ public class DebtRulesXMLImporterTest {
public void import_badly_formatted_xml() {
String xml = getFileContent("import_badly_formatted_xml.xml");
- List<DebtRulesXMLImporter.RuleDebt> results = importer.importXML(xml, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).hasSize(1);
- DebtRulesXMLImporter.RuleDebt ruleDebt = results.get(0);
+ RuleDebt ruleDebt = results.get(0);
assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY");
assertThat(ruleDebt.ruleKey()).isEqualTo(RuleKey.of("checkstyle", "Regexp"));
assertThat(ruleDebt.function()).isEqualTo(org.sonar.api.server.rule.DebtRemediationFunction.Type.LINEAR);
@@ -203,7 +219,7 @@ 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, validationMessages);
+ List<RuleDebt> results = importer.importXML(xml, validationMessages);
assertThat(results).isEmpty();
assertThat(validationMessages.getErrors()).isNotEmpty();
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 cfad5d4927b..78d35bf975d 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
@@ -33,7 +33,8 @@ 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.DebtModelPluginRepository;
+import org.sonar.server.debt.DebtModelXMLExporter;
import org.sonar.server.debt.DebtRulesXMLImporter;
import java.io.Reader;
@@ -54,7 +55,7 @@ public class DeprecatedRulesDefinitionTest {
RuleI18nManager i18n;
@Mock
- TechnicalDebtModelRepository debtModelRepository;
+ DebtModelPluginRepository debtModelRepository;
@Mock
DebtRulesXMLImporter importer;
@@ -156,8 +157,8 @@ public class DeprecatedRulesDefinitionTest {
public void define_rule_debt() throws Exception {
RulesDefinition.Context context = new RulesDefinition.Context();
- List<DebtRulesXMLImporter.RuleDebt> ruleDebts = newArrayList(
- new DebtRulesXMLImporter.RuleDebt()
+ List<DebtModelXMLExporter.RuleDebt> ruleDebts = newArrayList(
+ new DebtModelXMLExporter.RuleDebt()
.setCharacteristicKey("MEMORY_EFFICIENCY")
.setRuleKey(RuleKey.of("checkstyle", "ConstantName"))
.setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET)
diff --git a/sonar-server/src/test/java/org/sonar/server/startup/RegisterDebtModelTest.java b/sonar-server/src/test/java/org/sonar/server/startup/RegisterDebtModelTest.java
index e4d207bff92..456889bd9a0 100644
--- a/sonar-server/src/test/java/org/sonar/server/startup/RegisterDebtModelTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/startup/RegisterDebtModelTest.java
@@ -27,7 +27,7 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
-import org.sonar.server.debt.DebtModelRestore;
+import org.sonar.server.debt.DebtModelBackup;
import java.util.Collections;
@@ -41,13 +41,13 @@ public class RegisterDebtModelTest {
CharacteristicDao dao;
@Mock
- DebtModelRestore debtModelRestore;
+ DebtModelBackup debtModelBackup;
RegisterDebtModel registerDebtModel;
@Before
public void setUp() throws Exception {
- registerDebtModel = new RegisterDebtModel(dao, debtModelRestore);
+ registerDebtModel = new RegisterDebtModel(dao, debtModelBackup);
}
@Test
@@ -56,7 +56,7 @@ public class RegisterDebtModelTest {
registerDebtModel.start();
- verify(debtModelRestore).restore();
+ verify(debtModelBackup).restore();
}
@Test
@@ -65,6 +65,6 @@ public class RegisterDebtModelTest {
registerDebtModel.start();
- verifyZeroInteractions(debtModelRestore);
+ verifyZeroInteractions(debtModelBackup);
}
}
diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/TechnicalDebtModelRepositoryTest/csharp-model.xml b/sonar-server/src/test/resources/org/sonar/server/debt/DebtModelPluginRepositoryTest/csharp-model.xml
index e4569a2a7bf..e4569a2a7bf 100644
--- a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/TechnicalDebtModelRepositoryTest/csharp-model.xml
+++ b/sonar-server/src/test/resources/org/sonar/server/debt/DebtModelPluginRepositoryTest/csharp-model.xml
diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/TechnicalDebtModelRepositoryTest/java-model.xml b/sonar-server/src/test/resources/org/sonar/server/debt/DebtModelPluginRepositoryTest/java-model.xml
index 0b37f562107..0b37f562107 100644
--- a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/TechnicalDebtModelRepositoryTest/java-model.xml
+++ b/sonar-server/src/test/resources/org/sonar/server/debt/DebtModelPluginRepositoryTest/java-model.xml
diff --git a/sonar-server/src/test/resources/org/sonar/server/debt/DebtModelXMLExporterTest/export_xml.xml b/sonar-server/src/test/resources/org/sonar/server/debt/DebtModelXMLExporterTest/export_xml.xml
new file mode 100644
index 00000000000..58efbf3938c
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/debt/DebtModelXMLExporterTest/export_xml.xml
@@ -0,0 +1,32 @@
+<sqale>
+ <chc>
+ <key>USABILITY</key>
+ <name>Usability</name>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+ <chc>
+ <key>MEMORY_USE</key>
+ <name>Memory use</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>Regexp</rule-key>
+ <prop>
+ <key>remediationFunction</key>
+ <txt>LINEAR_OFFSET</txt>
+ </prop>
+ <prop>
+ <key>remediationFactor</key>
+ <val>3</val>
+ <txt>d</txt>
+ </prop>
+ <prop>
+ <key>offset</key>
+ <val>15</val>
+ <txt>min</txt>
+ </prop>
+ </chc>
+ </chc>
+ </chc>
+</sqale>
diff --git a/sonar-server/src/test/resources/org/sonar/server/debt/DebtRulesXMLImporterTest/read_integer.xml b/sonar-server/src/test/resources/org/sonar/server/debt/DebtRulesXMLImporterTest/read_integer.xml
new file mode 100644
index 00000000000..483a98bebd3
--- /dev/null
+++ b/sonar-server/src/test/resources/org/sonar/server/debt/DebtRulesXMLImporterTest/read_integer.xml
@@ -0,0 +1,29 @@
+<sqale>
+ <chc>
+ <key>USABILITY</key>
+ <name>Usability</name>
+ <desc>Estimate usability</desc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+ <chc>
+ <key>MEMORY_EFFICIENCY</key>
+ <name>Memory use</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>Regexp</rule-key>
+ <prop>
+ <key>remediationFactor</key>
+ <val>3</val>
+ <txt>h</txt>
+ </prop>
+ <prop>
+ <key>remediationFunction</key>
+ <txt>linear</txt>
+ </prop>
+ </chc>
+ </chc>
+ </chc>
+
+</sqale>