+++ /dev/null
-/*
- * 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.annotations.VisibleForTesting;
-import com.google.common.collect.Maps;
-import org.picocontainer.Startable;
-import org.sonar.api.Plugin;
-import org.sonar.api.ServerExtension;
-import org.sonar.api.platform.PluginMetadata;
-import org.sonar.api.platform.PluginRepository;
-
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-/**
- * <p>This class is used to find which technical debt model XML files exist in the Sonar instance.</p>
- * <p>
- * Those XML files are provided by language plugins that embed their own contribution to the definition of the Technical debt model.
- * They must be located in the classpath of those language plugins, more specifically in the "com.sonar.sqale" package, and
- * 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 static final String DEFAULT_MODEL = "technical-debt";
-
- private static final String XML_FILE_SUFFIX = "-model.xml";
- private static final String XML_FILE_PREFIX = "com/sonar/sqale/";
-
- private String xmlFilePrefix;
-
- private PluginRepository pluginRepository;
- private Map<String, ClassLoader> contributingPluginKeyToClassLoader;
-
- public TechnicalDebtModelRepository(PluginRepository pluginRepository) {
- this.pluginRepository = pluginRepository;
- this.xmlFilePrefix = XML_FILE_PREFIX;
- }
-
- @VisibleForTesting
- TechnicalDebtModelRepository(PluginRepository pluginRepository, String xmlFilePrefix) {
- this.pluginRepository = pluginRepository;
- this.xmlFilePrefix = xmlFilePrefix;
- }
-
- @VisibleForTesting
- TechnicalDebtModelRepository(Map<String, ClassLoader> contributingPluginKeyToClassLoader, String xmlFilePrefix) {
- this.contributingPluginKeyToClassLoader = contributingPluginKeyToClassLoader;
- this.xmlFilePrefix = xmlFilePrefix;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void start() {
- findAvailableXMLFiles();
- }
-
- protected void findAvailableXMLFiles() {
- if (contributingPluginKeyToClassLoader == null) {
- contributingPluginKeyToClassLoader = Maps.newTreeMap();
- // Add default model
- contributingPluginKeyToClassLoader.put(DEFAULT_MODEL, getClass().getClassLoader());
- for (PluginMetadata metadata : pluginRepository.getMetadata()) {
- String pluginKey = metadata.getKey();
- Plugin plugin = pluginRepository.getPlugin(pluginKey);
- if (plugin != null) {
- ClassLoader classLoader = plugin.getClass().getClassLoader();
- if (classLoader.getResource(getXMLFilePath(pluginKey)) != null) {
- contributingPluginKeyToClassLoader.put(pluginKey, classLoader);
- }
- }
- }
- }
- contributingPluginKeyToClassLoader = Collections.unmodifiableMap(contributingPluginKeyToClassLoader);
- }
-
- @VisibleForTesting
- String getXMLFilePath(String model) {
- return xmlFilePrefix + model + XML_FILE_SUFFIX;
- }
-
- /**
- * Returns the list of plugins that can contribute to the technical debt model.
- *
- * @return the list of plugin keys
- */
- public Collection<String> getContributingPluginList() {
- return newArrayList(contributingPluginKeyToClassLoader.keySet());
- }
-
- /**
- * Creates a new {@link java.io.Reader} for the XML file that contains the model contributed by the given plugin.
- *
- * @param pluginKey the key of the plugin that contributes the XML file
- * @return the reader, that must be closed once its use is finished.
- */
- public Reader createReaderForXMLFile(String pluginKey) {
- ClassLoader classLoader = contributingPluginKeyToClassLoader.get(pluginKey);
- String xmlFilePath = getXMLFilePath(pluginKey);
- return new InputStreamReader(classLoader.getResourceAsStream(xmlFilePath));
- }
-
- @VisibleForTesting
- Map<String, ClassLoader> getContributingPluginKeyToClassLoader(){
- return contributingPluginKeyToClassLoader;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void stop() {
- // Nothing to do
- }
-
-}
+++ /dev/null
-/*
- * 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);
- }
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
- }
-}
+++ /dev/null
-/*
- * 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 com.google.common.collect.Maps;
-import com.google.common.io.Resources;
-import org.apache.commons.io.IOUtils;
-import org.junit.Test;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.sonar.api.SonarPlugin;
-import org.sonar.api.platform.PluginMetadata;
-import org.sonar.api.platform.PluginRepository;
-
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.Reader;
-import java.net.MalformedURLException;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class TechnicalDebtModelRepositoryTest {
-
- private static final String TEST_XML_PREFIX_PATH = "org/sonar/core/technicaldebt/TechnicalDebtModelRepositoryTest/";
-
- private TechnicalDebtModelRepository modelFinder;
-
- @Test
- public void test_component_initialization() throws Exception {
- // we do have the "csharp-model.xml" file in src/test/resources
- PluginMetadata csharpPluginMetadata = mock(PluginMetadata.class);
- when(csharpPluginMetadata.getKey()).thenReturn("csharp");
-
- // but we don' have the "php-model.xml" one
- PluginMetadata phpPluginMetadata = mock(PluginMetadata.class);
- when(phpPluginMetadata.getKey()).thenReturn("php");
-
- PluginRepository repository = mock(PluginRepository.class);
- 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);
-
- // when
- modelFinder.start();
-
- // assert
- Collection<String> contributingPluginList = modelFinder.getContributingPluginList();
- assertThat(contributingPluginList.size()).isEqualTo(2);
- assertThat(contributingPluginList).containsOnly("technical-debt", "csharp");
- }
-
- @Test
- public void contributing_plugin_list() throws Exception {
- initModel();
- Collection<String> contributingPluginList = modelFinder.getContributingPluginList();
- assertThat(contributingPluginList.size()).isEqualTo(2);
- assertThat(contributingPluginList).contains("csharp", "java");
- }
-
- @Test
- public void get_content_for_xml_file() throws Exception {
- initModel();
- Reader xmlFileReader = null;
- try {
- xmlFileReader = modelFinder.createReaderForXMLFile("csharp");
- assertNotNull(xmlFileReader);
- List<String> lines = IOUtils.readLines(xmlFileReader);
- assertThat(lines.size()).isEqualTo(25);
- assertThat(lines.get(0)).isEqualTo("<sqale>");
- } catch (Exception e) {
- fail("Should be able to read the XML file.");
- } finally {
- IOUtils.closeQuietly(xmlFileReader);
- }
- }
-
- @Test
- public void return_xml_file_path_for_plugin() throws Exception {
- initModel();
- assertThat(modelFinder.getXMLFilePath("foo")).isEqualTo(TEST_XML_PREFIX_PATH + "foo-model.xml");
- }
-
- @Test
- public void contain_default_model() throws Exception {
- modelFinder = new TechnicalDebtModelRepository(mock(PluginRepository.class));
- modelFinder.start();
- assertThat(modelFinder.getContributingPluginKeyToClassLoader().keySet()).containsOnly("technical-debt");
- }
-
- private void initModel() throws MalformedURLException {
- Map<String, ClassLoader> contributingPluginKeyToClassLoader = Maps.newHashMap();
- contributingPluginKeyToClassLoader.put("csharp", newClassLoader());
- contributingPluginKeyToClassLoader.put("java", newClassLoader());
- modelFinder = new TechnicalDebtModelRepository(contributingPluginKeyToClassLoader, TEST_XML_PREFIX_PATH);
- }
-
- private ClassLoader newClassLoader() throws MalformedURLException {
- ClassLoader loader = mock(ClassLoader.class);
- when(loader.getResourceAsStream(anyString())).thenAnswer(new Answer<InputStream>() {
- public InputStream answer(InvocationOnMock invocation) throws Throwable {
- return new FileInputStream(Resources.getResource((String) invocation.getArguments()[0]).getPath());
- }
- });
- return loader;
- }
-
- class FakePlugin extends SonarPlugin {
- public List getExtensions() {
- return null;
- }
- }
-
-}
+++ /dev/null
-/*
- * 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);
- }
-
-}
+++ /dev/null
-/*
- * 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();
- }
-}
+++ /dev/null
-/*
- * 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);
- }
- }
-
-}
+++ /dev/null
-<sqale>
- <chc>
- <key>USABILITY</key>
- <name>Usability</name>
- <desc>Estimate usability</desc>
- </chc>
- <chc>
- <key>EFFICIENCY</key>
- <name>Efficiency</name>
- <chc>
- <rule-repo>gendarme</rule-repo>
- <rule-key>EnsureLocalDisposalRule</rule-key>
- <prop>
- <key>remediationFactor</key>
- <val>0.125</val>
- <txt>d</txt>
- </prop>
- <prop>
- <key>remediationFunction</key>
- <txt>linear</txt>
- </prop>
- </chc>
- </chc>
-
-</sqale>
\ No newline at end of file
+++ /dev/null
-<sqale>
- <chc>
- <key>USABILITY</key>
- <name>Usability</name>
- <desc>Estimate usability</desc>
- </chc>
- <chc>
- <key>EFFICIENCY</key>
- <name>Efficiency</name>
- <chc>
- <rule-repo>squid-cobol</rule-repo>
- <rule-key>CheckLoop</rule-key>
- <prop>
- <key>remediationFactor</key>
- <val>0.125</val>
- <txt>d</txt>
- </prop>
- <prop>
- <key>remediationFunction</key>
- <txt>linear</txt>
- </prop>
- </chc>
- </chc>
-
-</sqale>
\ No newline at end of file
/**
* @since 4.1
+ * Used by Views plugin
*/
public interface TechnicalDebtModel {
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;
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));
}
}
}
+
}
+++ /dev/null
-/*
- * 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);
- }
-
-}
--- /dev/null
+/*
+ * 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.annotations.VisibleForTesting;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.ibatis.session.SqlSession;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.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.db.CharacteristicDao;
+import org.sonar.core.technicaldebt.db.CharacteristicDto;
+import org.sonar.server.user.UserSession;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.io.Reader;
+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 DebtModelBackup implements ServerComponent {
+
+ private final MyBatis mybatis;
+ private final CharacteristicDao dao;
+ private final RuleDao ruleDao;
+ private final DebtModelOperations debtModelOperations;
+ private final DebtModelPluginRepository debtModelPluginRepository;
+ private final DebtCharacteristicsXMLImporter characteristicsXMLImporter;
+ private final DebtRulesXMLImporter rulesXMLImporter;
+ private final DebtModelXMLExporter debtModelXMLExporter;
+ private final System2 system2;
+
+ 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
+ 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.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 void restore() {
+ restoreProvided(loadModelFromPlugin(DebtModelPluginRepository.DEFAULT_MODEL), null);
+ }
+
+ /**
+ * Restore from plugins providing rules for a given language
+ */
+ 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);
+ }
+ }
+
+ /**
+ * Restore model from a given XML model
+ */
+ public ValidationMessages restoreFromXml(String xml) {
+ DebtModel debtModel = characteristicsXMLImporter.importXML(xml);
+ ValidationMessages validationMessages = ValidationMessages.create();
+ List<RuleDebt> ruleDebts = rulesXMLImporter.importXML(xml, validationMessages);
+ restore(debtModel, ruleDebts, null, validationMessages);
+ return validationMessages;
+ }
+
+ /**
+ * Restore model from a given XML model and a given language
+ */
+ public ValidationMessages restoreFromXml(String xml, String languageKey) {
+ DebtModel debtModel = characteristicsXMLImporter.importXML(xml);
+ ValidationMessages validationMessages = ValidationMessages.create();
+ List<RuleDebt> ruleDebts = rulesXMLImporter.importXML(xml, validationMessages);
+ restore(debtModel, ruleDebts, languageKey, validationMessages);
+ return 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> characteristicDtos = restoreCharacteristics(modelToImport, updateDate, session);
+ restoreRules(characteristicDtos, languageKey, ruleDebts, validationMessages, updateDate, session);
+
+ session.commit();
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ 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 (languageKey == null || languageKey.equals(rule.getLanguage())) {
+ RuleDebt ruleDebt = ruleDebtByRule(rule, ruleDebts);
+ if (ruleDebt == null) {
+ rule.setCharacteristicId(rule.getDefaultCharacteristicId() != null ? RuleDto.DISABLED_CHARACTERISTIC_ID : null);
+ rule.setRemediationFunction(null);
+ rule.setRemediationFactor(null);
+ rule.setRemediationOffset(null);
+ } else {
+ CharacteristicDto characteristicDto = characteristicByKey(ruleDebt.characteristicKey(), characteristicDtos, false);
+ // Characteristic cannot be null as it has been created just before
+
+ boolean isSameCharacteristic = characteristicDto.getId().equals(rule.getDefaultCharacteristicId());
+ boolean isSameFunction = isSameRemediationFunction(ruleDebt, rule);
+ rule.setCharacteristicId((!isSameCharacteristic ? characteristicDto.getId() : null));
+ rule.setRemediationFunction((!isSameFunction ? ruleDebt.function().name() : null));
+ rule.setRemediationFactor((!isSameFunction ? ruleDebt.factor() : null));
+ rule.setRemediationOffset((!isSameFunction ? ruleDebt.offset() : null));
+ }
+
+ ruleDebts.remove(ruleDebt);
+ rule.setUpdatedAt(updateDate);
+ ruleDao.update(rule, session);
+ // TODO index rules in E/S
+ }
+ }
+
+ for (RuleDebt ruleDebt : ruleDebts) {
+ validationMessages.addWarningText(String.format("The rule '%s' does not exist.", ruleDebt.ruleKey()));
+ }
+ }
+
+ @VisibleForTesting
+ List<CharacteristicDto> restoreCharacteristics(DebtModel targetModel, Date updateDate, SqlSession session) {
+ List<CharacteristicDto> sourceCharacteristics = dao.selectEnabledCharacteristics(session);
+
+ List<CharacteristicDto> result = newArrayList();
+
+ // Restore not existing characteristics
+ for (DebtCharacteristic characteristic : targetModel.rootCharacteristics()) {
+ CharacteristicDto rootCharacteristicDto = restoreCharacteristic(characteristic, null, sourceCharacteristics, updateDate, session);
+ result.add(rootCharacteristicDto);
+ for (DebtCharacteristic subCharacteristic : targetModel.subCharacteristics(characteristic.key())) {
+ result.add(restoreCharacteristic(subCharacteristic, rootCharacteristicDto.getId(), sourceCharacteristics, updateDate, session));
+ }
+ }
+ // Disable no more existing characteristics
+ for (CharacteristicDto sourceCharacteristic : sourceCharacteristics) {
+ if (targetModel.characteristicByKey(sourceCharacteristic.getKey()) == null) {
+ debtModelOperations.disableCharacteristic(sourceCharacteristic, updateDate, session);
+ }
+ }
+ return result;
+ }
+
+ private CharacteristicDto restoreCharacteristic(DebtCharacteristic targetCharacteristic, @Nullable Integer parentId, List<CharacteristicDto> sourceCharacteristics,
+ Date updateDate, SqlSession session) {
+ CharacteristicDto sourceCharacteristic = characteristicByKey(targetCharacteristic.key(), sourceCharacteristics, true);
+ if (sourceCharacteristic == null) {
+ CharacteristicDto newCharacteristic = toDto(targetCharacteristic, parentId).setCreatedAt(updateDate);
+ dao.insert(newCharacteristic, session);
+ return newCharacteristic;
+ } else {
+ // Update only if modifications
+ if (ObjectUtils.notEqual(sourceCharacteristic.getName(), targetCharacteristic.name()) ||
+ ObjectUtils.notEqual(sourceCharacteristic.getOrder(), targetCharacteristic.order())) {
+ sourceCharacteristic.setName(targetCharacteristic.name());
+ sourceCharacteristic.setOrder(targetCharacteristic.order());
+ sourceCharacteristic.setUpdatedAt(updateDate);
+ dao.update(sourceCharacteristic, session);
+ }
+ return sourceCharacteristic;
+ }
+ }
+
+ 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 {
+ xmlFileReader = debtModelPluginRepository.createReaderForXMLFile(pluginKey);
+ return characteristicsXMLImporter.importXML(xmlFileReader);
+ } finally {
+ IOUtils.closeQuietly(xmlFileReader);
+ }
+ }
+
+ @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());
+ }
+ }, null);
+ if (dto == null && !canByNull) {
+ throw new IllegalStateException(String.format("Characteristic with key '%s' has not been found ", key));
+ }
+ return dto;
+ }
+
+ private static List<CharacteristicDto> subCharacteristics(final Integer parentId, List<CharacteristicDto> allCharacteristics) {
+ return newArrayList(Iterables.filter(allCharacteristics, new Predicate<CharacteristicDto>() {
+ @Override
+ public boolean apply(CharacteristicDto input) {
+ return parentId.equals(input.getParentId());
+ }
+ }));
+ }
+
+ 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) {
+ return new CharacteristicDto()
+ .setKey(characteristic.key())
+ .setName(characteristic.name())
+ .setOrder(characteristic.order())
+ .setParentId(parentId)
+ .setEnabled(true)
+ .setCreatedAt(characteristic.createdAt())
+ .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);
+ }
+
+
+}
--- /dev/null
+/*
+ * 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.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
+import org.picocontainer.Startable;
+import org.sonar.api.Plugin;
+import org.sonar.api.ServerExtension;
+import org.sonar.api.platform.PluginMetadata;
+import org.sonar.api.platform.PluginRepository;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * <p>This class is used to find which technical debt model XML files exist in the Sonar instance.</p>
+ * <p>
+ * Those XML files are provided by language plugins that embed their own contribution to the definition of the Technical debt model.
+ * They must be located in the classpath of those language plugins, more specifically in the "com.sonar.sqale" package, and
+ * they must be named "<pluginKey>-model.xml".
+ * </p>
+ */
+public class DebtModelPluginRepository implements ServerExtension, Startable {
+
+ public static final String DEFAULT_MODEL = "technical-debt";
+
+ private static final String XML_FILE_SUFFIX = "-model.xml";
+ private static final String XML_FILE_PREFIX = "com/sonar/sqale/";
+
+ private String xmlFilePrefix;
+
+ private PluginRepository pluginRepository;
+ private Map<String, ClassLoader> contributingPluginKeyToClassLoader;
+
+ public DebtModelPluginRepository(PluginRepository pluginRepository) {
+ this.pluginRepository = pluginRepository;
+ this.xmlFilePrefix = XML_FILE_PREFIX;
+ }
+
+ @VisibleForTesting
+ DebtModelPluginRepository(PluginRepository pluginRepository, String xmlFilePrefix) {
+ this.pluginRepository = pluginRepository;
+ this.xmlFilePrefix = xmlFilePrefix;
+ }
+
+ @VisibleForTesting
+ DebtModelPluginRepository(Map<String, ClassLoader> contributingPluginKeyToClassLoader, String xmlFilePrefix) {
+ this.contributingPluginKeyToClassLoader = contributingPluginKeyToClassLoader;
+ this.xmlFilePrefix = xmlFilePrefix;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void start() {
+ findAvailableXMLFiles();
+ }
+
+ protected void findAvailableXMLFiles() {
+ if (contributingPluginKeyToClassLoader == null) {
+ contributingPluginKeyToClassLoader = Maps.newTreeMap();
+ // Add default model
+ contributingPluginKeyToClassLoader.put(DEFAULT_MODEL, getClass().getClassLoader());
+ for (PluginMetadata metadata : pluginRepository.getMetadata()) {
+ String pluginKey = metadata.getKey();
+ Plugin plugin = pluginRepository.getPlugin(pluginKey);
+ if (plugin != null) {
+ ClassLoader classLoader = plugin.getClass().getClassLoader();
+ if (classLoader.getResource(getXMLFilePath(pluginKey)) != null) {
+ contributingPluginKeyToClassLoader.put(pluginKey, classLoader);
+ }
+ }
+ }
+ }
+ contributingPluginKeyToClassLoader = Collections.unmodifiableMap(contributingPluginKeyToClassLoader);
+ }
+
+ @VisibleForTesting
+ String getXMLFilePath(String model) {
+ return xmlFilePrefix + model + XML_FILE_SUFFIX;
+ }
+
+ /**
+ * Returns the list of plugins that can contribute to the technical debt model.
+ *
+ * @return the list of plugin keys
+ */
+ public Collection<String> getContributingPluginList() {
+ return newArrayList(contributingPluginKeyToClassLoader.keySet());
+ }
+
+ /**
+ * Creates a new {@link java.io.Reader} for the XML file that contains the model contributed by the given plugin.
+ *
+ * @param pluginKey the key of the plugin that contributes the XML file
+ * @return the reader, that must be closed once its use is finished.
+ */
+ public Reader createReaderForXMLFile(String pluginKey) {
+ ClassLoader classLoader = contributingPluginKeyToClassLoader.get(pluginKey);
+ String xmlFilePath = getXMLFilePath(pluginKey);
+ return new InputStreamReader(classLoader.getResourceAsStream(xmlFilePath));
+ }
+
+ @VisibleForTesting
+ Map<String, ClassLoader> getContributingPluginKeyToClassLoader(){
+ return contributingPluginKeyToClassLoader;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void stop() {
+ // Nothing to do
+ }
+
+}
+++ /dev/null
-/*
- * 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.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;
-import org.apache.commons.lang.ObjectUtils;
-import org.apache.commons.lang.builder.EqualsBuilder;
-import org.apache.ibatis.session.SqlSession;
-import org.sonar.api.ServerComponent;
-import org.sonar.api.server.debt.DebtCharacteristic;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.ValidationMessages;
-import org.sonar.core.permission.GlobalPermissions;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.rule.RuleDao;
-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;
-
-public class DebtModelRestore 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 DebtCharacteristicsXMLImporter characteristicsXMLImporter;
- private final DebtRulesXMLImporter rulesXMLImporter;
- private final System2 system2;
-
- public DebtModelRestore(MyBatis mybatis, CharacteristicDao dao, RuleDao ruleDao, DebtModelOperations debtModelOperations, TechnicalDebtModelRepository debtModelPluginRepository,
- RuleRepositories ruleRepositories, DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter) {
- this(mybatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, ruleRepositories, characteristicsXMLImporter, rulesXMLImporter, System2.INSTANCE);
- }
-
- @VisibleForTesting
- DebtModelRestore(MyBatis mybatis, CharacteristicDao dao, RuleDao ruleDao, DebtModelOperations debtModelOperations, TechnicalDebtModelRepository debtModelPluginRepository,
- RuleRepositories ruleRepositories, DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter,
- 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.system2 = system2;
- }
-
- /**
- * 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;
- }
-
- /**
- * 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;
- }
-
- /**
- * Restore model from a given XML model
- */
- public ValidationMessages restoreFromXml(String xml) {
- DebtModel debtModel = characteristicsXMLImporter.importXML(xml);
- ValidationMessages validationMessages = ValidationMessages.create();
- List<DebtRulesXMLImporter.RuleDebt> ruleDebts = rulesXMLImporter.importXML(xml, validationMessages);
- restore(debtModel, ruleDebts, Collections.<RuleRepositories.Repository>emptyList(), true, validationMessages);
- return validationMessages;
- }
-
- /**
- * Restore model from a given XML model and a given language
- */
- public ValidationMessages restoreFromXml(String xml, String languageKey) {
- DebtModel debtModel = characteristicsXMLImporter.importXML(xml);
- ValidationMessages validationMessages = ValidationMessages.create();
- List<DebtRulesXMLImporter.RuleDebt> ruleDebts = rulesXMLImporter.importXML(xml, validationMessages);
- restore(debtModel, ruleDebts, ruleRepositories.repositoriesForLang(languageKey), true, validationMessages);
- return validationMessages;
- }
-
- private void restore(DebtModel modelToImport, List<DebtRulesXMLImporter.RuleDebt> ruleDebts, Collection<RuleRepositories.Repository> repositories,
- boolean disableCharacteristicWhenRuleNotFound, ValidationMessages validationMessages) {
- checkPermission();
-
- Date updateDate = new Date(system2.now());
- SqlSession session = mybatis.openSession();
- try {
- List<CharacteristicDto> persisted = dao.selectEnabledCharacteristics();
- List<CharacteristicDto> characteristicDtos = restoreCharacteristics(modelToImport, persisted, updateDate, session);
- restoreRules(characteristicDtos, repositories, ruleDebts, disableCharacteristicWhenRuleNotFound, validationMessages, updateDate, session);
-
- session.commit();
- } finally {
- MyBatis.closeQuietly(session);
- }
- }
-
- private void restoreRules(List<CharacteristicDto> characteristicDtos, Collection<RuleRepositories.Repository> repositories, List<DebtRulesXMLImporter.RuleDebt> ruleDebts,
- boolean disableCharacteristicWhenRuleNotFound, ValidationMessages validationMessages, Date updateDate, SqlSession session) {
- List<String> repositoryKeys = newArrayList(Iterables.transform(repositories, new Function<RuleRepositories.Repository, String>() {
- @Override
- public String apply(RuleRepositories.Repository input) {
- return input.getKey();
- }
- }));
- for (RuleDto rule : ruleDao.selectEnablesAndNonManual(session)) {
- if (repositories.isEmpty() || repositoryKeys.contains(rule.getRepositoryKey())) {
- DebtRulesXMLImporter.RuleDebt ruleDebt = ruleDebtByRule(rule, ruleDebts);
- if (ruleDebt == null) {
- rule.setCharacteristicId(disableCharacteristicWhenRuleNotFound ? RuleDto.DISABLED_CHARACTERISTIC_ID : null);
- rule.setRemediationFunction(null);
- rule.setRemediationFactor(null);
- rule.setRemediationOffset(null);
- } else {
- CharacteristicDto characteristicDto = characteristicByKey(ruleDebt.characteristicKey(), characteristicDtos, false);
- // Characteristic cannot be null as it has been created just before
-
- boolean isSameCharacteristic = characteristicDto.getId().equals(rule.getDefaultCharacteristicId());
- boolean isSameFunction = isSameRemediationFunction(ruleDebt, rule);
- rule.setCharacteristicId((!isSameCharacteristic ? characteristicDto.getId() : null));
- rule.setRemediationFunction((!isSameFunction ? ruleDebt.function().name() : null));
- rule.setRemediationFactor((!isSameFunction ? ruleDebt.factor() : null));
- rule.setRemediationOffset((!isSameFunction ? ruleDebt.offset() : null));
- }
-
- ruleDebts.remove(ruleDebt);
- rule.setUpdatedAt(updateDate);
- ruleDao.update(rule, session);
- // TODO index rules in E/S
- }
- }
-
- for (DebtRulesXMLImporter.RuleDebt ruleDebt : ruleDebts) {
- validationMessages.addWarningText(String.format("The rule '%s' does not exist.", ruleDebt.ruleKey()));
- }
- }
-
- static boolean isSameRemediationFunction(DebtRulesXMLImporter.RuleDebt ruleDebt, RuleDto rule) {
- return new EqualsBuilder()
- .append(ruleDebt.function().name(), rule.getDefaultRemediationFunction())
- .append(ruleDebt.factor(), rule.getDefaultRemediationFactor())
- .append(ruleDebt.offset(), rule.getDefaultRemediationOffset())
- .isEquals();
- }
-
- @VisibleForTesting
- List<CharacteristicDto> restoreCharacteristics(DebtModel targetModel, List<CharacteristicDto> sourceCharacteristics, Date updateDate, SqlSession session) {
- List<CharacteristicDto> result = newArrayList();
-
- // Restore not existing characteristics
- for (DebtCharacteristic characteristic : targetModel.rootCharacteristics()) {
- CharacteristicDto rootCharacteristicDto = restoreCharacteristic(characteristic, null, sourceCharacteristics, updateDate, session);
- result.add(rootCharacteristicDto);
- for (DebtCharacteristic subCharacteristic : targetModel.subCharacteristics(characteristic.key())) {
- result.add(restoreCharacteristic(subCharacteristic, rootCharacteristicDto.getId(), sourceCharacteristics, updateDate, session));
- }
- }
- // Disable no more existing characteristics
- for (CharacteristicDto sourceCharacteristic : sourceCharacteristics) {
- if (targetModel.characteristicByKey(sourceCharacteristic.getKey()) == null) {
- debtModelOperations.disableCharacteristic(sourceCharacteristic, updateDate, session);
- }
- }
- return result;
- }
-
- private CharacteristicDto restoreCharacteristic(DebtCharacteristic targetCharacteristic, @Nullable Integer parentId, List<CharacteristicDto> sourceCharacteristics,
- Date updateDate, SqlSession session) {
- CharacteristicDto sourceCharacteristic = characteristicByKey(targetCharacteristic.key(), sourceCharacteristics, true);
- if (sourceCharacteristic == null) {
- CharacteristicDto newCharacteristic = toDto(targetCharacteristic, parentId).setCreatedAt(updateDate);
- dao.insert(newCharacteristic, session);
- return newCharacteristic;
- } else {
- // Update only if modifications
- if (ObjectUtils.notEqual(sourceCharacteristic.getName(), targetCharacteristic.name()) ||
- ObjectUtils.notEqual(sourceCharacteristic.getOrder(), targetCharacteristic.order())) {
- sourceCharacteristic.setName(targetCharacteristic.name());
- sourceCharacteristic.setOrder(targetCharacteristic.order());
- sourceCharacteristic.setUpdatedAt(updateDate);
- dao.update(sourceCharacteristic, session);
- }
- return sourceCharacteristic;
- }
- }
-
- private DebtModel loadModelFromPlugin(String pluginKey) {
- Reader xmlFileReader = null;
- try {
- xmlFileReader = debtModelPluginRepository.createReaderForXMLFile(pluginKey);
- return characteristicsXMLImporter.importXML(xmlFileReader);
- } finally {
- IOUtils.closeQuietly(xmlFileReader);
- }
- }
-
- private CharacteristicDto characteristicByKey(final String key, List<CharacteristicDto> existingModel, boolean canByNull) {
- CharacteristicDto dto = Iterables.find(existingModel, new Predicate<CharacteristicDto>() {
- @Override
- public boolean apply(CharacteristicDto input) {
- return key.equals(input.getKey());
- }
- }, null);
- if (dto == null && !canByNull) {
- throw new IllegalStateException(String.format("Characteristic with key '%s' has not been found ", key));
- }
- return dto;
- }
-
- @CheckForNull
- private DebtRulesXMLImporter.RuleDebt ruleDebtByRule(final RuleDto rule, List<DebtRulesXMLImporter.RuleDebt> ruleDebts) {
- if (ruleDebts.isEmpty()) {
- return null;
- }
- return Iterables.find(ruleDebts, new Predicate<DebtRulesXMLImporter.RuleDebt>() {
- @Override
- public boolean apply(DebtRulesXMLImporter.RuleDebt input) {
- return rule.getRepositoryKey().equals(input.ruleKey().repository()) && rule.getRuleKey().equals(input.ruleKey().rule());
- }
- }, null);
- }
-
- private static CharacteristicDto toDto(DebtCharacteristic characteristic, @Nullable Integer parentId) {
- return new CharacteristicDto()
- .setKey(characteristic.key())
- .setName(characteristic.name())
- .setOrder(characteristic.order())
- .setParentId(parentId)
- .setEnabled(true)
- .setCreatedAt(characteristic.createdAt())
- .setUpdatedAt(characteristic.updatedAt());
- }
-
- private void checkPermission() {
- UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
- }
-
-
-}
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() {
/**
* 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);
}
}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+
+}
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;
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);
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.");
}
}
}
}
}
- 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;
- }
- }
-
}
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;
pico.addSingleton(AddTagsWsHandler.class);
pico.addSingleton(RemoveTagsWsHandler.class);
pico.addSingleton(RulesDefinitionXmlLoader.class);
-
+
// rule tags
pico.addSingleton(ESRuleTags.class);
pico.addSingleton(RuleTagLookup.class);
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);
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;
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.
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,
}
}
- 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()) {
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 {
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);
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();
}
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 {
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);
}
--- /dev/null
+/*
+ * 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 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.Captor;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.api.server.rule.DebtRemediationFunction;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.ValidationMessages;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.core.rule.RuleDao;
+import org.sonar.core.rule.RuleDto;
+import org.sonar.core.technicaldebt.db.CharacteristicDao;
+import org.sonar.core.technicaldebt.db.CharacteristicDto;
+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;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+import static org.sonar.server.debt.DebtModelXMLExporter.DebtModel;
+import static org.sonar.server.debt.DebtModelXMLExporter.RuleDebt;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DebtModelBackupTest {
+
+ @Mock
+ MyBatis myBatis;
+
+ @Mock
+ SqlSession session;
+
+ @Mock
+ DebtModelPluginRepository debtModelPluginRepository;
+
+ @Mock
+ CharacteristicDao dao;
+
+ @Mock
+ RuleDao ruleDao;
+
+ @Mock
+ DebtModelOperations debtModelOperations;
+
+ @Mock
+ DebtCharacteristicsXMLImporter characteristicsXMLImporter;
+
+ @Mock
+ DebtRulesXMLImporter rulesXMLImporter;
+
+ @Mock
+ 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 debtModel = new DebtModel();
+ List<DebtModelXMLExporter.RuleDebt> rules = newArrayList();
+
+ DebtModelBackup debtModelBackup;
+
+ @Before
+ public void setUp() throws Exception {
+ MockUserSession.set().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+
+ when(system2.now()).thenReturn(now.getTime());
+
+ currentId = 10;
+ // Associate an id when inserting an object to simulate the db id generator
+ 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));
+
+ when(myBatis.openSession()).thenReturn(session);
+
+ Reader defaultModelReader = mock(Reader.class);
+ when(debtModelPluginRepository.createReaderForXMLFile("technical-debt")).thenReturn(defaultModelReader);
+ when(characteristicsXMLImporter.importXML(eq(defaultModelReader))).thenReturn(debtModel);
+ when(characteristicsXMLImporter.importXML(anyString())).thenReturn(debtModel);
+ when(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(rules);
+
+ 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 {
+ 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"),
+ now,
+ session
+ );
+
+ verify(dao, times(2)).insert(characteristicArgument.capture(), eq(session));
+
+ CharacteristicDto dto1 = characteristicArgument.getAllValues().get(0);
+ assertThat(dto1.getId()).isEqualTo(10);
+ assertThat(dto1.getKey()).isEqualTo("PORTABILITY");
+ assertThat(dto1.getName()).isEqualTo("Portability");
+ assertThat(dto1.getParentId()).isNull();
+ assertThat(dto1.getOrder()).isEqualTo(1);
+ assertThat(dto1.getCreatedAt()).isEqualTo(now);
+ assertThat(dto1.getUpdatedAt()).isNull();
+
+ CharacteristicDto dto2 = characteristicArgument.getAllValues().get(1);
+ assertThat(dto2.getId()).isEqualTo(11);
+ assertThat(dto2.getKey()).isEqualTo("COMPILER");
+ assertThat(dto2.getName()).isEqualTo("Compiler");
+ assertThat(dto2.getParentId()).isEqualTo(10);
+ assertThat(dto2.getOrder()).isNull();
+ assertThat(dto2.getCreatedAt()).isEqualTo(now);
+ assertThat(dto2.getUpdatedAt()).isNull();
+ }
+
+ @Test
+ public void update_characteristics_when_restoring_characteristics() throws Exception {
+ 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)
+ ));
+
+ debtModelBackup.restoreCharacteristics(
+ new DebtModel()
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"),
+ now,
+ session
+ );
+
+ verify(dao, times(2)).update(characteristicArgument.capture(), eq(session));
+
+ CharacteristicDto dto1 = characteristicArgument.getAllValues().get(0);
+ assertThat(dto1.getId()).isEqualTo(1);
+ assertThat(dto1.getKey()).isEqualTo("PORTABILITY");
+ assertThat(dto1.getName()).isEqualTo("Portability");
+ assertThat(dto1.getParentId()).isNull();
+ assertThat(dto1.getOrder()).isEqualTo(1);
+ assertThat(dto1.getCreatedAt()).isEqualTo(oldDate);
+ assertThat(dto1.getUpdatedAt()).isEqualTo(now);
+
+ CharacteristicDto dto2 = characteristicArgument.getAllValues().get(1);
+ assertThat(dto2.getId()).isEqualTo(2);
+ assertThat(dto2.getKey()).isEqualTo("COMPILER");
+ assertThat(dto2.getName()).isEqualTo("Compiler");
+ assertThat(dto2.getParentId()).isEqualTo(1);
+ assertThat(dto2.getOrder()).isNull();
+ assertThat(dto2.getCreatedAt()).isEqualTo(oldDate);
+ assertThat(dto2.getUpdatedAt()).isEqualTo(now);
+ }
+
+ @Test
+ public void disable_no_more_existing_characteristics_when_restoring_characteristics() throws Exception {
+ CharacteristicDto dto1 = new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1);
+ CharacteristicDto dto2 = new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1);
+
+ 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);
+ }
+
+ @Test
+ public void restore_from_provided_model() 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 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().setRepositoryKey("squid").setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelBackup.restore();
+
+ verify(dao).selectEnabledCharacteristics(session);
+ verify(dao, times(2)).update(any(CharacteristicDto.class), eq(session));
+ verifyNoMoreInteractions(dao);
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ 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_language() 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 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").setLanguage("java")
+ .setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").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.restore("java");
+
+ verify(dao).selectEnabledCharacteristics(session);
+ verify(dao, times(2)).update(any(CharacteristicDto.class), eq(session));
+ verifyNoMoreInteractions(dao);
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+
+ verify(session).commit();
+ }
+
+ @Test
+ public void restore_from_xml_with_different_characteristic_and_same_function() throws Exception {
+ 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)));
+
+ 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")
+ .setDefaultCharacteristicId(10).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelBackup.restoreFromXml("<xml/>");
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+ assertThat(rule.getCharacteristicId()).isEqualTo(2);
+ assertThat(rule.getRemediationFunction()).isNull();
+ assertThat(rule.getRemediationFactor()).isNull();
+ assertThat(rule.getRemediationOffset()).isNull();
+ assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
+ verify(session).commit();
+ }
+
+ @Test
+ public void restore_from_xml_with_same_characteristic_and_different_function() throws Exception {
+ 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)));
+
+ 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(
+ new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
+ .setDefaultCharacteristicId(2).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelBackup.restoreFromXml("<xml/>");
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+ assertThat(rule.getCharacteristicId()).isNull();
+ assertThat(rule.getRemediationFunction()).isEqualTo("LINEAR_OFFSET");
+ assertThat(rule.getRemediationFactor()).isEqualTo("12h");
+ assertThat(rule.getRemediationOffset()).isEqualTo("11min");
+ assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
+ verify(session).commit();
+ }
+
+ @Test
+ public void restore_from_xml_with_same_characteristic_and_same_function() throws Exception {
+ 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)));
+
+ 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(
+ new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
+ .setDefaultCharacteristicId(2).setDefaultRemediationFunction("LINEAR_OFFSET").setDefaultRemediationFactor("2h").setDefaultRemediationOffset("15min")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelBackup.restoreFromXml("<xml/>");
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+ assertThat(rule.getCharacteristicId()).isNull();
+ assertThat(rule.getRemediationFunction()).isNull();
+ assertThat(rule.getRemediationFactor()).isNull();
+ assertThat(rule.getRemediationOffset()).isNull();
+ assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
+ verify(session).commit();
+ }
+
+ @Test
+ public void restore_from_xml_disable_rule_debt_when_not_in_xml_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(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")
+ .setDefaultCharacteristicId(2).setDefaultRemediationFunction("LINEAR_OFFSET").setDefaultRemediationFactor("2h").setDefaultRemediationOffset("15min")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelBackup.restoreFromXml("<xml/>");
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+ assertThat(rule.getCharacteristicId()).isEqualTo(-1);
+ assertThat(rule.getRemediationFunction()).isNull();
+ assertThat(rule.getRemediationFactor()).isNull();
+ assertThat(rule.getRemediationOffset()).isNull();
+ assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
+ verify(session).commit();
+ }
+
+ @Test
+ public void restore_from_xml_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/>");
+
+ 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(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 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").setLanguage("java")
+ .setDefaultCharacteristicId(10).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
+ .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.restoreFromXml("<xml/>", "java");
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ verify(ruleDao).update(ruleArgument.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+
+ RuleDto rule = ruleArgument.getValue();
+ assertThat(rule.getId()).isEqualTo(1);
+
+ verify(session).commit();
+ }
+
+ @Test
+ public void add_warning_message_when_rule_from_xml_is_not_found() throws Exception {
+ 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)));
+
+ 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 = debtModelBackup.restoreFromXml("<xml/>");
+
+ assertThat(validationMessages.getWarnings()).hasSize(1);
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ verifyNoMoreInteractions(ruleDao);
+
+ verify(session).commit();
+ }
+
+}
--- /dev/null
+/*
+ * 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.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.io.Resources;
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.sonar.api.SonarPlugin;
+import org.sonar.api.platform.PluginMetadata;
+import org.sonar.api.platform.PluginRepository;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DebtModelPluginRepositoryTest {
+
+ private static final String TEST_XML_PREFIX_PATH = "org/sonar/server/debt/DebtModelPluginRepositoryTest/";
+
+ private DebtModelPluginRepository modelFinder;
+
+ @Test
+ public void test_component_initialization() throws Exception {
+ // we do have the "csharp-model.xml" file in src/test/resources
+ PluginMetadata csharpPluginMetadata = mock(PluginMetadata.class);
+ when(csharpPluginMetadata.getKey()).thenReturn("csharp");
+
+ // but we don' have the "php-model.xml" one
+ PluginMetadata phpPluginMetadata = mock(PluginMetadata.class);
+ when(phpPluginMetadata.getKey()).thenReturn("php");
+
+ PluginRepository repository = mock(PluginRepository.class);
+ when(repository.getMetadata()).thenReturn(Lists.newArrayList(csharpPluginMetadata, phpPluginMetadata));
+ FakePlugin fakePlugin = new FakePlugin();
+ when(repository.getPlugin(anyString())).thenReturn(fakePlugin);
+ modelFinder = new DebtModelPluginRepository(repository, TEST_XML_PREFIX_PATH);
+
+ // when
+ modelFinder.start();
+
+ // assert
+ Collection<String> contributingPluginList = modelFinder.getContributingPluginList();
+ assertThat(contributingPluginList.size()).isEqualTo(2);
+ assertThat(contributingPluginList).containsOnly("technical-debt", "csharp");
+ }
+
+ @Test
+ public void contributing_plugin_list() throws Exception {
+ initModel();
+ Collection<String> contributingPluginList = modelFinder.getContributingPluginList();
+ assertThat(contributingPluginList.size()).isEqualTo(2);
+ assertThat(contributingPluginList).contains("csharp", "java");
+ }
+
+ @Test
+ public void get_content_for_xml_file() throws Exception {
+ initModel();
+ Reader xmlFileReader = null;
+ try {
+ xmlFileReader = modelFinder.createReaderForXMLFile("csharp");
+ assertNotNull(xmlFileReader);
+ List<String> lines = IOUtils.readLines(xmlFileReader);
+ assertThat(lines.size()).isEqualTo(25);
+ assertThat(lines.get(0)).isEqualTo("<sqale>");
+ } catch (Exception e) {
+ fail("Should be able to read the XML file.");
+ } finally {
+ IOUtils.closeQuietly(xmlFileReader);
+ }
+ }
+
+ @Test
+ public void return_xml_file_path_for_plugin() throws Exception {
+ initModel();
+ assertThat(modelFinder.getXMLFilePath("foo")).isEqualTo(TEST_XML_PREFIX_PATH + "foo-model.xml");
+ }
+
+ @Test
+ public void contain_default_model() throws Exception {
+ modelFinder = new DebtModelPluginRepository(mock(PluginRepository.class));
+ modelFinder.start();
+ assertThat(modelFinder.getContributingPluginKeyToClassLoader().keySet()).containsOnly("technical-debt");
+ }
+
+ private void initModel() throws MalformedURLException {
+ Map<String, ClassLoader> contributingPluginKeyToClassLoader = Maps.newHashMap();
+ contributingPluginKeyToClassLoader.put("csharp", newClassLoader());
+ contributingPluginKeyToClassLoader.put("java", newClassLoader());
+ modelFinder = new DebtModelPluginRepository(contributingPluginKeyToClassLoader, TEST_XML_PREFIX_PATH);
+ }
+
+ private ClassLoader newClassLoader() throws MalformedURLException {
+ ClassLoader loader = mock(ClassLoader.class);
+ when(loader.getResourceAsStream(anyString())).thenAnswer(new Answer<InputStream>() {
+ public InputStream answer(InvocationOnMock invocation) throws Throwable {
+ return new FileInputStream(Resources.getResource((String) invocation.getArguments()[0]).getPath());
+ }
+ });
+ return loader;
+ }
+
+ class FakePlugin extends SonarPlugin {
+ public List getExtensions() {
+ return null;
+ }
+ }
+
+}
+++ /dev/null
-/*
- * 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 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.rule.RuleKey;
-import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
-import org.sonar.api.server.rule.DebtRemediationFunction;
-import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.ValidationMessages;
-import org.sonar.core.permission.GlobalPermissions;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.rule.RuleDao;
-import 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.Collections;
-import java.util.Date;
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
-
-@RunWith(MockitoJUnitRunner.class)
-public class DebtModelRestoreTest {
-
- @Mock
- MyBatis myBatis;
-
- @Mock
- SqlSession session;
-
- @Mock
- TechnicalDebtModelRepository debtModelPluginRepository;
-
- @Mock
- CharacteristicDao dao;
-
- @Mock
- RuleDao ruleDao;
-
- @Mock
- DebtModelOperations debtModelOperations;
-
- @Mock
- DebtCharacteristicsXMLImporter characteristicsXMLImporter;
-
- @Mock
- DebtRulesXMLImporter rulesXMLImporter;
-
- @Mock
- RuleRepositories ruleRepositories;
-
- @Mock
- System2 system2;
-
- Date now = DateUtils.parseDate("2014-03-19");
-
- int currentId;
-
- DebtModel characteristics = new DebtModel();
- List<DebtRulesXMLImporter.RuleDebt> rules = newArrayList();
-
- DebtModelRestore debtModelRestore;
-
- @Before
- public void setUp() throws Exception {
- MockUserSession.set().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
-
- when(system2.now()).thenReturn(now.getTime());
-
- currentId = 10;
- // Associate an id when inserting an object to simulate the db id generator
- 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));
-
- when(myBatis.openSession()).thenReturn(session);
-
- 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(rulesXMLImporter.importXML(anyString(), any(ValidationMessages.class))).thenReturn(rules);
-
- debtModelRestore = new DebtModelRestore(myBatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, ruleRepositories, characteristicsXMLImporter, rulesXMLImporter,
- system2);
- }
-
- @Test
- public void create_characteristics_when_restoring_characteristics() throws Exception {
- debtModelRestore.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);
- assertThat(dto1.getId()).isEqualTo(10);
- assertThat(dto1.getKey()).isEqualTo("PORTABILITY");
- assertThat(dto1.getName()).isEqualTo("Portability");
- assertThat(dto1.getParentId()).isNull();
- assertThat(dto1.getOrder()).isEqualTo(1);
- assertThat(dto1.getCreatedAt()).isEqualTo(now);
- assertThat(dto1.getUpdatedAt()).isNull();
-
- CharacteristicDto dto2 = characteristicArgument.getAllValues().get(1);
- assertThat(dto2.getId()).isEqualTo(11);
- assertThat(dto2.getKey()).isEqualTo("COMPILER");
- assertThat(dto2.getName()).isEqualTo("Compiler");
- assertThat(dto2.getParentId()).isEqualTo(10);
- assertThat(dto2.getOrder()).isNull();
- assertThat(dto2.getCreatedAt()).isEqualTo(now);
- assertThat(dto2.getUpdatedAt()).isNull();
- }
-
- @Test
- public void update_characteristics_when_restoring_characteristics() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- debtModelRestore.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);
- assertThat(dto1.getId()).isEqualTo(1);
- assertThat(dto1.getKey()).isEqualTo("PORTABILITY");
- assertThat(dto1.getName()).isEqualTo("Portability");
- assertThat(dto1.getParentId()).isNull();
- assertThat(dto1.getOrder()).isEqualTo(1);
- assertThat(dto1.getCreatedAt()).isEqualTo(oldDate);
- assertThat(dto1.getUpdatedAt()).isEqualTo(now);
-
- CharacteristicDto dto2 = characteristicArgument.getAllValues().get(1);
- assertThat(dto2.getId()).isEqualTo(2);
- assertThat(dto2.getKey()).isEqualTo("COMPILER");
- assertThat(dto2.getName()).isEqualTo("Compiler");
- assertThat(dto2.getParentId()).isEqualTo(1);
- assertThat(dto2.getOrder()).isNull();
- assertThat(dto2.getCreatedAt()).isEqualTo(oldDate);
- assertThat(dto2.getUpdatedAt()).isEqualTo(now);
- }
-
- @Test
- public void disable_no_more_existing_characteristics_when_restoring_characteristics() throws Exception {
- CharacteristicDto dto1 = new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1);
- CharacteristicDto dto2 = new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1);
-
- debtModelRestore.restoreCharacteristics(new DebtModel(), newArrayList(dto1, dto2), now, session);
-
- verify(debtModelOperations).disableCharacteristic(dto1, now, session);
- verify(debtModelOperations).disableCharacteristic(dto2, now, session);
- }
-
- @Test
- public void restore_from_provided_model() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
- .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
-
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
- new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability 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().setRepositoryKey("squid").setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
- .setCreatedAt(oldDate).setUpdatedAt(oldDate)
- ));
-
- debtModelRestore.restore();
-
- verify(dao).selectEnabledCharacteristics();
- 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);
-
- RuleDto rule = ruleArgument.getValue();
- 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_language() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
- .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
-
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
- new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability 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")
- .setCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationFactor("2h").setRemediationOffset("15min")
- .setCreatedAt(oldDate).setUpdatedAt(oldDate),
- // Should be ignored
- new RuleDto().setId(2).setRepositoryKey("checkstyle")
- .setCharacteristicId(3).setRemediationFunction("LINEAR").setRemediationFactor("2h")
- .setCreatedAt(oldDate).setUpdatedAt(oldDate)
- ));
-
- RuleRepositories.Repository squid = mock(RuleRepositories.Repository.class);
- when(squid.getKey()).thenReturn("squid");
- when(ruleRepositories.repositoriesForLang("java")).thenReturn(newArrayList(squid));
-
- debtModelRestore.restore("java");
-
- verify(dao).selectEnabledCharacteristics();
- 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);
-
- RuleDto rule = ruleArgument.getValue();
- assertThat(rule.getId()).isEqualTo(1);
-
- verify(session).commit();
- }
-
- @Test
- public void restore_from_xml_with_different_characteristic_and_same_function() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
- .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
-
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
- new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
- new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
-
- rules.add(new DebtRulesXMLImporter.RuleDebt()
- .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h"));
-
- when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
- new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
- .setDefaultCharacteristicId(10).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
- .setCreatedAt(oldDate).setUpdatedAt(oldDate)
- ));
-
- debtModelRestore.restoreFromXml("<xml/>");
-
- verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
- verify(ruleDao).update(ruleArgument.capture(), eq(session));
- verifyNoMoreInteractions(ruleDao);
-
- RuleDto rule = ruleArgument.getValue();
- assertThat(rule.getId()).isEqualTo(1);
- assertThat(rule.getCharacteristicId()).isEqualTo(2);
- assertThat(rule.getRemediationFunction()).isNull();
- assertThat(rule.getRemediationFactor()).isNull();
- assertThat(rule.getRemediationOffset()).isNull();
- assertThat(rule.getUpdatedAt()).isEqualTo(now);
-
- verify(session).commit();
- }
-
- @Test
- public void restore_from_xml_with_same_characteristic_and_different_function() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
- .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
-
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
- new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
- new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
-
- rules.add(new DebtRulesXMLImporter.RuleDebt()
- .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET).setFactor("12h").setOffset("11min"));
-
- when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
- new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
- .setDefaultCharacteristicId(2).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
- .setCreatedAt(oldDate).setUpdatedAt(oldDate)
- ));
-
- debtModelRestore.restoreFromXml("<xml/>");
-
- verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
- verify(ruleDao).update(ruleArgument.capture(), eq(session));
- verifyNoMoreInteractions(ruleDao);
-
- RuleDto rule = ruleArgument.getValue();
- assertThat(rule.getId()).isEqualTo(1);
- assertThat(rule.getCharacteristicId()).isNull();
- assertThat(rule.getRemediationFunction()).isEqualTo("LINEAR_OFFSET");
- assertThat(rule.getRemediationFactor()).isEqualTo("12h");
- assertThat(rule.getRemediationOffset()).isEqualTo("11min");
- assertThat(rule.getUpdatedAt()).isEqualTo(now);
-
- verify(session).commit();
- }
-
- @Test
- public void restore_from_xml_with_same_characteristic_and_same_function() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
- .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
-
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
- new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
- new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
-
- rules.add(new DebtRulesXMLImporter.RuleDebt()
- .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR_OFFSET).setFactor("2h").setOffset("15min"));
-
- when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
- new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
- .setDefaultCharacteristicId(2).setDefaultRemediationFunction("LINEAR_OFFSET").setDefaultRemediationFactor("2h").setDefaultRemediationOffset("15min")
- .setCreatedAt(oldDate).setUpdatedAt(oldDate)
- ));
-
- debtModelRestore.restoreFromXml("<xml/>");
-
- verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
- verify(ruleDao).update(ruleArgument.capture(), eq(session));
- verifyNoMoreInteractions(ruleDao);
-
- RuleDto rule = ruleArgument.getValue();
- assertThat(rule.getId()).isEqualTo(1);
- assertThat(rule.getCharacteristicId()).isNull();
- assertThat(rule.getRemediationFunction()).isNull();
- assertThat(rule.getRemediationFactor()).isNull();
- assertThat(rule.getRemediationOffset()).isNull();
- assertThat(rule.getUpdatedAt()).isEqualTo(now);
-
- verify(session).commit();
- }
-
- @Test
- public void restore_from_xml_disable_rule_debt_when_not_in_xml() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
- .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
-
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
- new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
- new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
-
- when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
- new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
- .setDefaultCharacteristicId(2).setDefaultRemediationFunction("LINEAR_OFFSET").setDefaultRemediationFactor("2h").setDefaultRemediationOffset("15min")
- .setCreatedAt(oldDate).setUpdatedAt(oldDate)
- ));
-
- debtModelRestore.restoreFromXml("<xml/>");
-
- verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
- verify(ruleDao).update(ruleArgument.capture(), eq(session));
- verifyNoMoreInteractions(ruleDao);
-
- RuleDto rule = ruleArgument.getValue();
- assertThat(rule.getId()).isEqualTo(1);
- assertThat(rule.getCharacteristicId()).isEqualTo(-1);
- assertThat(rule.getRemediationFunction()).isNull();
- assertThat(rule.getRemediationFactor()).isNull();
- assertThat(rule.getRemediationOffset()).isNull();
- assertThat(rule.getUpdatedAt()).isEqualTo(now);
-
- verify(session).commit();
- }
-
- @Test
- public void restore_from_xml_and_language() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
- .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
-
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
- new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
- new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
-
- rules.add(new DebtRulesXMLImporter.RuleDebt()
- .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h"));
-
- when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
- new RuleDto().setId(1).setRepositoryKey("squid").setRuleKey("UselessImportCheck")
- .setDefaultCharacteristicId(10).setDefaultRemediationFunction("LINEAR").setDefaultRemediationFactor("2h")
- .setCreatedAt(oldDate).setUpdatedAt(oldDate),
- // Should be ignored
- new RuleDto().setId(2).setRepositoryKey("checkstyle")
- .setCharacteristicId(3).setRemediationFunction("LINEAR").setRemediationFactor("2h")
- .setCreatedAt(oldDate).setUpdatedAt(oldDate)
- ));
-
- RuleRepositories.Repository squid = mock(RuleRepositories.Repository.class);
- when(squid.getKey()).thenReturn("squid");
- when(ruleRepositories.repositoriesForLang("java")).thenReturn(newArrayList(squid));
-
- debtModelRestore.restoreFromXml("<xml/>", "java");
-
- verify(ruleDao).selectEnablesAndNonManual(session);
- ArgumentCaptor<RuleDto> ruleArgument = ArgumentCaptor.forClass(RuleDto.class);
- verify(ruleDao).update(ruleArgument.capture(), eq(session));
- verifyNoMoreInteractions(ruleDao);
-
- RuleDto rule = ruleArgument.getValue();
- assertThat(rule.getId()).isEqualTo(1);
-
- verify(session).commit();
- }
-
- @Test
- public void add_warning_message_when_rule_from_xml_is_not_found() throws Exception {
- Date oldDate = DateUtils.parseDate("2014-01-01");
-
- characteristics
- .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
- .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY");
-
- when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(
- new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1).setCreatedAt(oldDate),
- new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler").setParentId(1).setCreatedAt(oldDate)));
-
- rules.add(new DebtRulesXMLImporter.RuleDebt()
- .setRuleKey(RuleKey.of("squid", "UselessImportCheck")).setCharacteristicKey("COMPILER").setFunction(DebtRemediationFunction.Type.LINEAR).setFactor("2h"));
-
- when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(Collections.<RuleDto>emptyList());
-
- ValidationMessages validationMessages = debtModelRestore.restoreFromXml("<xml/>");
-
- assertThat(validationMessages.getWarnings()).hasSize(1);
-
- verify(ruleDao).selectEnablesAndNonManual(session);
- verifyNoMoreInteractions(ruleDao);
-
- verify(session).commit();
- }
-
-}
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
@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");
+ }
}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
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 {
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();
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);
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);
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");
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();
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");
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");
assertThat(ruleDebt.offset()).isNull();
}
+ @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");
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();
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();
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();
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);
@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();
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;
RuleI18nManager i18n;
@Mock
- TechnicalDebtModelRepository debtModelRepository;
+ DebtModelPluginRepository debtModelRepository;
@Mock
DebtRulesXMLImporter importer;
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)
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;
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
registerDebtModel.start();
- verify(debtModelRestore).restore();
+ verify(debtModelBackup).restore();
}
@Test
registerDebtModel.start();
- verifyZeroInteractions(debtModelRestore);
+ verifyZeroInteractions(debtModelBackup);
}
}
--- /dev/null
+<sqale>
+ <chc>
+ <key>USABILITY</key>
+ <name>Usability</name>
+ <desc>Estimate usability</desc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+ <chc>
+ <rule-repo>gendarme</rule-repo>
+ <rule-key>EnsureLocalDisposalRule</rule-key>
+ <prop>
+ <key>remediationFactor</key>
+ <val>0.125</val>
+ <txt>d</txt>
+ </prop>
+ <prop>
+ <key>remediationFunction</key>
+ <txt>linear</txt>
+ </prop>
+ </chc>
+ </chc>
+
+</sqale>
\ No newline at end of file
--- /dev/null
+<sqale>
+ <chc>
+ <key>USABILITY</key>
+ <name>Usability</name>
+ <desc>Estimate usability</desc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+ <chc>
+ <rule-repo>squid-cobol</rule-repo>
+ <rule-key>CheckLoop</rule-key>
+ <prop>
+ <key>remediationFactor</key>
+ <val>0.125</val>
+ <txt>d</txt>
+ </prop>
+ <prop>
+ <key>remediationFunction</key>
+ <txt>linear</txt>
+ </prop>
+ </chc>
+ </chc>
+
+</sqale>
\ No newline at end of file
--- /dev/null
+<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>
--- /dev/null
+<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>