+++ /dev/null
-<!--
-
- SonarQube, open source software quality management tool.
- Copyright (C) 2008-2013 SonarSource
- mailto:contact AT sonarsource DOT com
-
- SonarQube is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- SonarQube is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
--->
-<sqale>
- <chc>
- <key>PORTABILITY</key>
- <name>Portability</name>
- <chc>
- <key>COMPILER_RELATED_PORTABILITY</key>
- <name>Compiler</name>
- </chc>
- <chc>
- <key>HARDWARE_RELATED_PORTABILITY</key>
- <name>Hardware</name>
- </chc>
- <chc>
- <key>LANGUAGE_RELATED_PORTABILITY</key>
- <name>Language</name>
- </chc>
- <chc>
- <key>OS_RELATED_PORTABILITY</key>
- <name>OS</name>
- </chc>
- <chc>
- <key>SOFTWARE_RELATED_PORTABILITY</key>
- <name>Software</name>
- </chc>
- <chc>
- <key>TIME_ZONE_RELATED_PORTABILITY</key>
- <name>Time zone</name>
- </chc>
- </chc>
- <chc>
- <key>MAINTAINABILITY</key>
- <name>Maintainability</name>
- <chc>
- <key>READABILITY</key>
- <name>Readability</name>
- </chc>
- <chc>
- <key>UNDERSTANDABILITY</key>
- <name>Understandability</name>
- </chc>
- </chc>
- <chc>
- <key>SECURITY</key>
- <name>Security</name>
- <chc>
- <key>API_ABUSE</key>
- <name>API abuse</name>
- </chc>
- <chc>
- <key>ERRORS</key>
- <name>Errors</name>
- </chc>
- <chc>
- <key>INPUT_VALIDATION_AND_REPRESENTATION</key>
- <name>Input validation and representation</name>
- </chc>
- <chc>
- <key>SECURITY_FEATURES</key>
- <name>Security features</name>
- </chc>
- </chc>
- <chc>
- <key>EFFICIENCY</key>
- <name>Efficiency</name>
- <chc>
- <key>MEMORY_EFFICIENCY</key>
- <name>Memory use</name>
- </chc>
- <chc>
- <key>CPU_EFFICIENCY</key>
- <name>Processor use</name>
- </chc>
- </chc>
- <chc>
- <key>CHANGEABILITY</key>
- <name>Changeability</name>
- <chc>
- <key>ARCHITECTURE_CHANGEABILITY</key>
- <name>Architecture</name>
- </chc>
- <chc>
- <key>DATA_CHANGEABILITY</key>
- <name>Data</name>
- </chc>
- <chc>
- <key>LOGIC_CHANGEABILITY</key>
- <name>Logic</name>
- </chc>
- </chc>
- <chc>
- <key>RELIABILITY</key>
- <name>Reliability</name>
- <chc>
- <key>ARCHITECTURE_RELIABILITY</key>
- <name>Architecture</name>
- </chc>
- <chc>
- <key>DATA_RELIABILITY</key>
- <name>Data</name>
- </chc>
- <chc>
- <key>EXCEPTION_HANDLING</key>
- <name>Exception handling</name>
- </chc>
- <chc>
- <key>FAULT_TOLERANCE</key>
- <name>Fault tolerance</name>
- </chc>
- <chc>
- <key>INSTRUCTION_RELIABILITY</key>
- <name>Instruction</name>
- </chc>
- <chc>
- <key>LOGIC_RELIABILITY</key>
- <name>Logic</name>
- </chc>
- <chc>
- <key>SYNCHRONIZATION_RELIABILITY</key>
- <name>Synchronization</name>
- </chc>
- <chc>
- <key>UNIT_TESTS</key>
- <name>Unit tests</name>
- </chc>
- </chc>
- <chc>
- <key>TESTABILITY</key>
- <name>Testability</name>
- <chc>
- <key>INTEGRATION_TESTABILITY</key>
- <name>Integration level</name>
- </chc>
- <chc>
- <key>UNIT_TESTABILITY</key>
- <name>Unit level</name>
- </chc>
- </chc>
-</sqale>
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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.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;
-
-public class RuleCache {
-
- private final RuleFinder ruleFinder;
-
- private Map<String, Map<String, Rule>> cachedRules;
-
- public RuleCache(RuleFinder ruleFinder) {
- this.ruleFinder = ruleFinder;
- }
-
- @CheckForNull
- public Rule getRule(String repository, String ruleKey) {
- if(cachedRules == null) {
- loadRules();
- }
- return lookUpRuleInCache(repository, ruleKey);
- }
-
- private void loadRules() {
- cachedRules = 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);
- }
- }
- }
-
- 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-2013 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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.ServerExtension;
-import org.sonar.api.database.DatabaseSession;
-import org.sonar.api.qualitymodel.Characteristic;
-import org.sonar.api.qualitymodel.Model;
-import org.sonar.api.qualitymodel.ModelFinder;
-import org.sonar.api.utils.ValidationMessages;
-import org.sonar.jpa.session.DatabaseSessionFactory;
-
-import java.io.Reader;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * TODO test merge properties + property.value + text_value
- */
-
-public class TechnicalDebtManager implements ServerExtension {
-
- private static final Logger LOG = LoggerFactory.getLogger(TechnicalDebtManager.class);
-
- private DatabaseSessionFactory sessionFactory;
- private ModelFinder modelFinder;
- private TechnicalDebtModelFinder languageModelFinder;
- private XMLImporter importer;
-
- public TechnicalDebtManager(DatabaseSessionFactory sessionFactory, ModelFinder modelFinder,
- TechnicalDebtModelFinder languageModelFinder, XMLImporter importer) {
- this.sessionFactory = sessionFactory;
- this.modelFinder = modelFinder;
- this.languageModelFinder = languageModelFinder;
- this.importer = importer;
- }
-
- public void restore(Model model, ValidationMessages messages, RuleCache rulesCache) {
- Model persisted = modelFinder.findByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- if (persisted != null) {
- disable(persisted);
- }
- merge(model, messages, rulesCache);
- }
-
- public Model resetModel(ValidationMessages messages, RuleCache ruleCache) {
- Model persisted = modelFinder.findByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- if (persisted != null) {
- disable(persisted);
- }
-
- Model model = modelFinder.findByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- importDefaultSqaleModel(model, messages, ruleCache);
- populateModelWithInitialValues(model, messages, ruleCache);
-
- DatabaseSession session = sessionFactory.getSession();
- session.save(model);
- session.commit();
-
- return model;
- }
-
- public Model createInitialModel(ValidationMessages messages, RuleCache ruleCache) {
- Model initialModel = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- importDefaultSqaleModel(initialModel, messages, ruleCache);
- populateModelWithInitialValues(initialModel, messages, ruleCache);
- return initialModel;
- }
-
- public void merge(List<String> pluginKeys, ValidationMessages messages, RuleCache ruleCache) {
- for (String pluginKey : pluginKeys) {
- ValidationMessages currentMessages = ValidationMessages.create();
- Model model = null;
- Reader xmlFileReader = null;
- try {
- xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey);
- model = importer.importXML(xmlFileReader, currentMessages, ruleCache);
- } finally {
- IOUtils.closeQuietly(xmlFileReader);
- }
- if (!currentMessages.hasErrors()) {
- merge(model, messages, ruleCache);
- } else {
- for (String error : currentMessages.getErrors()) {
- messages.addErrorText(error);
- }
- }
- }
- }
-
- public void merge(Model with, ValidationMessages messages, RuleCache ruleCache) {
- DatabaseSession session = sessionFactory.getSession();
- Model sqale = modelFinder.findByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- if (sqale == null) {
- sqale = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- session.saveWithoutFlush(sqale);
- }
- new TechnicalDebtModel(sqale).mergeWith(with, messages, ruleCache);
- session.saveWithoutFlush(sqale);
- session.commit();
- }
-
- private void populateModelWithInitialValues(Model initialModel, ValidationMessages messages, RuleCache ruleCache) {
- for (String pluginKey : getContributingPluginListWithoutSqale()) {
- mergePlugin(initialModel, pluginKey, messages, ruleCache);
- }
- }
-
- private void mergePlugin(Model initialModel, String pluginKey, ValidationMessages messages, RuleCache ruleCache) {
- Model model = null;
- Reader xmlFileReader = null;
- try {
- xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey);
-
- model = importer.importXML(xmlFileReader, messages, ruleCache);
- } finally {
- IOUtils.closeQuietly(xmlFileReader);
- }
- messages.log(LOG);
- if (!messages.hasErrors()) {
- new TechnicalDebtModel(initialModel).mergeWith(model, messages, ruleCache);
- messages.log(LOG);
- }
- }
-
- private void disable(Model persisted) {
- for (Characteristic root : persisted.getRootCharacteristics()) {
- persisted.removeCharacteristic(root);
- }
- sessionFactory.getSession().commit();
- }
-
- private Collection<String> getContributingPluginListWithoutSqale(){
- return languageModelFinder.getContributingPluginList();
- }
-
- private void importDefaultSqaleModel(Model initialModel, ValidationMessages messages, RuleCache ruleCache) {
- mergePlugin(initialModel, TechnicalDebtModelFinder.DEFAULT_MODEL, messages, ruleCache);
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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.sonar.api.qualitymodel.Characteristic;
-import org.sonar.api.qualitymodel.CharacteristicProperty;
-import org.sonar.api.qualitymodel.Model;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.utils.ValidationMessages;
-
-public class TechnicalDebtModel {
-
- private Model model;
-
- public TechnicalDebtModel(Model model) {
- this.model = model;
- }
-
- public void mergeWith(Model with, ValidationMessages messages, RuleCache ruleCache) {
- for (Characteristic characteristic : with.getCharacteristics()) {
- if (isRequirement(characteristic)) {
- mergeRequirement(model, characteristic, messages, ruleCache);
- } else {
- mergeCharacteristic(model, characteristic, messages);
- }
- }
- }
-
- private Characteristic mergeCharacteristic(Model target, Characteristic characteristic, ValidationMessages messages) {
- Characteristic targetCharacteristic = target.getCharacteristicByKey(characteristic.getKey());
- if (targetCharacteristic == null) {
- targetCharacteristic = target.addCharacteristic(clone(characteristic));
- if (!characteristic.getParents().isEmpty()) {
- Characteristic parentTargetCharacteristic = mergeCharacteristic(target, characteristic.getParents().get(0), messages);
- parentTargetCharacteristic.addChild(targetCharacteristic);
- }
- }
- return targetCharacteristic;
- }
-
- private void mergeRequirement(Model target, Characteristic requirement, ValidationMessages messages,
- RuleCache ruleCache) {
- Characteristic targetRequirement = target.getCharacteristicByRule(requirement.getRule());
- if (targetRequirement == null && !requirement.getParents().isEmpty()) {
- Rule rule = ruleCache.getRule(requirement.getRule().getRepositoryKey(), requirement.getRule().getKey());
- if (rule == null) {
- messages.addWarningText("The rule " + requirement.getRule() + " does not exist.");
-
- } else {
- Characteristic parent = mergeCharacteristic(target, requirement.getParents().get(0), messages);
- requirement = target.addCharacteristic(clone(requirement));
- requirement.setRule(rule);
- parent.addChild(requirement);
- }
- }
- }
-
- private boolean isRequirement(Characteristic characteristic) {
- return characteristic.hasRule();
- }
-
- private Characteristic clone(Characteristic c) {
- Characteristic clone = Characteristic.create();
- clone.setRule(c.getRule());
- clone.setDescription(c.getDescription());
- clone.setKey(c.getKey());
- clone.setName(c.getName(), false);
- for (CharacteristicProperty property : c.getProperties()) {
- clone.setProperty(property.getKey(), property.getTextValue()).setValue(property.getValue());
- }
- return clone;
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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.sonar.api.qualitymodel.Model;
-import org.sonar.api.qualitymodel.ModelDefinition;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.utils.ValidationMessages;
-
-public final class TechnicalDebtModelDefinition extends ModelDefinition {
-
- public static final String TECHNICAL_DEBT_MODEL = "TECHNICAL_DEBT";
-
- private final TechnicalDebtManager technicalDebtManager;
- private final RuleFinder ruleFinder;
-
- public TechnicalDebtModelDefinition(TechnicalDebtManager technicalDebtManager, RuleFinder ruleFinder) {
- super(TECHNICAL_DEBT_MODEL);
- this.technicalDebtManager = technicalDebtManager;
- this.ruleFinder = ruleFinder;
- }
-
- @Override
- public Model createModel() {
- RuleCache ruleCache = new RuleCache(ruleFinder);
- return technicalDebtManager.createInitialModel(ValidationMessages.create(), ruleCache);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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.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 TechnicalDebtModelFinder 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;
-
- /**
- *
- * @param pluginRepository
- */
- public TechnicalDebtModelFinder(PluginRepository pluginRepository) {
- this.pluginRepository = pluginRepository;
- this.xmlFilePrefix = XML_FILE_PREFIX;
- }
-
- @VisibleForTesting
- TechnicalDebtModelFinder(PluginRepository pluginRepository, String xmlFilePrefix) {
- this.pluginRepository = pluginRepository;
- this.xmlFilePrefix = xmlFilePrefix;
- }
-
- @VisibleForTesting
- TechnicalDebtModelFinder(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();
- for (PluginMetadata metadata : pluginRepository.getMetadata()) {
- String pluginKey = metadata.getKey();
- ClassLoader classLoader = pluginRepository.getPlugin(pluginKey).getClass().getClassLoader();
- if (classLoader.getResource(getXMLFilePathForPlugin(pluginKey)) != null) {
- contributingPluginKeyToClassLoader.put(pluginKey, classLoader);
- }
- }
- }
- contributingPluginKeyToClassLoader = Collections.unmodifiableMap(contributingPluginKeyToClassLoader);
- }
-
- protected String getXMLFilePathForPlugin(String pluginKey) {
- return xmlFilePrefix + pluginKey + XML_FILE_SUFFIX;
- }
-
- /**
- * Returns the list of plugins that can contribute to the SQALE model (without the default model provided by this plugin).
- *
- * @return the list of plugin keys
- */
- public Collection<String> getContributingPluginList() {
- Collection<String> contributingPlugins = newArrayList(contributingPluginKeyToClassLoader.keySet());
- contributingPlugins.remove(DEFAULT_MODEL);
- return contributingPlugins;
- }
-
- /**
- * 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 = getXMLFilePathForPlugin(pluginKey);
- return new InputStreamReader(classLoader.getResourceAsStream(xmlFilePath));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void stop() {
- // Nothing to do
- }
-
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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;
-
-public final class XMLConstants {
-
- private XMLConstants(){
- // Utility 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 CHARACTERISTIC_DESCRIPTION = "desc";
- 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";
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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.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.qualitymodel.Characteristic;
-import org.sonar.api.qualitymodel.Model;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.utils.ValidationMessages;
-
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamException;
-
-import java.io.Reader;
-import java.io.StringReader;
-import java.util.List;
-
-public class XMLImporter implements ServerExtension {
-
- private static final Logger LOG = LoggerFactory.getLogger(XMLImporter.class);
-
- public Model importXML(String xml, ValidationMessages messages, RuleCache ruleCache) {
- return importXML(new StringReader(xml), messages, ruleCache);
- }
-
- public Model importXML(Reader xml, ValidationMessages messages, RuleCache repositoryCache) {
- Model sqale = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- try {
- SMInputFactory inputFactory = initStax();
- SMHierarchicCursor cursor = inputFactory.rootElementCursor(xml);
-
- // advance to <sqale>
- cursor.advance();
- SMInputCursor chcCursor = cursor.childElementCursor(XMLConstants.CHARACTERISTIC);
-
- while (chcCursor.getNext() != null) {
- processCharacteristic(sqale, 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 sqale;
- }
-
- 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 Characteristic processCharacteristic(Model sqale, SMInputCursor chcCursor, ValidationMessages messages,
- RuleCache ruleCache) throws XMLStreamException {
- Characteristic characteristic = Characteristic.create();
- SMInputCursor cursor = chcCursor.childElementCursor();
-
- String ruleRepositoryKey = null, ruleKey = null;
- List<Characteristic> children = Lists.newArrayList();
- while (cursor.getNext() != null) {
- String node = cursor.getLocalName();
- if (StringUtils.equals(node, XMLConstants.CHARACTERISTIC_KEY)) {
- characteristic.setKey(cursor.collectDescendantText().trim());
-
- } else if (StringUtils.equals(node, XMLConstants.CHARACTERISTIC_NAME)) {
- characteristic.setName(cursor.collectDescendantText().trim(), false);
-
- } else if (StringUtils.equals(node, XMLConstants.CHARACTERISTIC_DESCRIPTION)) {
- characteristic.setDescription(cursor.collectDescendantText().trim());
-
- } else if (StringUtils.equals(node, XMLConstants.PROPERTY)) {
- processProperty(characteristic, cursor, messages);
-
- } else if (StringUtils.equals(node, XMLConstants.CHARACTERISTIC)) {
- children.add(processCharacteristic(sqale, cursor, messages, ruleCache));
-
- } else if (StringUtils.equals(node, "rule-repo")) {
- ruleRepositoryKey = cursor.collectDescendantText().trim();
-
- } else if (StringUtils.equals(node, "rule-key")) {
- ruleKey = cursor.collectDescendantText().trim();
- }
- }
- fillRule(characteristic, ruleRepositoryKey, ruleKey, messages, ruleCache);
-
- if (StringUtils.isNotBlank(characteristic.getKey()) || characteristic.getRule() != null) {
- addCharacteristicToModel(sqale, characteristic, children);
- return characteristic;
- }
- return null;
- }
-
- private void fillRule(Characteristic characteristic, String ruleRepositoryKey, String ruleKey, ValidationMessages messages,
- RuleCache ruleCache) {
- if (StringUtils.isNotBlank(ruleRepositoryKey) && StringUtils.isNotBlank(ruleKey)) {
- Rule rule = ruleCache.getRule(ruleRepositoryKey, ruleKey);
- if (rule != null) {
- characteristic.setRule(rule);
- } else {
- messages.addWarningText("Rule not found: [repository=" + ruleRepositoryKey + ", key=" + ruleKey + "]");
- }
- }
- }
-
- private void addCharacteristicToModel(Model sqale, Characteristic characteristic, List<Characteristic> children) {
- sqale.addCharacteristic(characteristic);
- for (Characteristic child : children) {
- if (child != null) {
- sqale.addCharacteristic(child);
- characteristic.addChild(child);
- }
- }
- }
-
- private void processProperty(Characteristic characteristic, SMInputCursor cursor, ValidationMessages messages) throws XMLStreamException {
- SMInputCursor c = cursor.childElementCursor();
- String key = null;
- Double value = null;
- String textValue = null;
- while (c.getNext() != null) {
- String node = c.getLocalName();
- if (StringUtils.equals(node, XMLConstants.PROPERTY_KEY)) {
- key = c.collectDescendantText().trim();
-
- } else if (StringUtils.equals(node, XMLConstants.PROPERTY_VALUE)) {
- String s = c.collectDescendantText().trim();
- try {
- value = NumberUtils.createDouble(s);
- } catch (NumberFormatException ex) {
- String message = String.format("Cannot import value '%s' for field %s - Expected a numeric value instead", s, key);
- LOG.error(message, ex);
- messages.addErrorText(message);
- }
- } else if (StringUtils.equals(node, XMLConstants.PROPERTY_TEXT_VALUE)) {
- textValue = c.collectDescendantText().trim();
- }
- }
- if (StringUtils.isNotBlank(key)) {
- characteristic.setProperty(key, textValue).setValue(value);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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.
- */
-
-@ParametersAreNonnullByDefault
-package org.sonar.core.technicaldebt;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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.junit.Test;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.rules.RuleQuery;
-
-import java.util.Collections;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
-
-public class RuleCacheTest {
-
- @Test
- public void should_lazy_load_rules_on_first_call() throws Exception {
-
- RuleFinder ruleFinder = mock(RuleFinder.class);
- when(ruleFinder.findAll(any(RuleQuery.class))).thenReturn(Collections.EMPTY_LIST);
-
- RuleCache ruleCache = new RuleCache(ruleFinder);
- ruleCache.getRule("", "");
- ruleCache.getRule("", "");
-
- verify(ruleFinder, times(1)).findAll(any(RuleQuery.class));
- }
-
- @Test
- public void should_return_matching_rule() throws Exception {
-
- Rule rule1 = Rule.create("repo1", "rule1");
- Rule rule2 = Rule.create("repo2", "rule2");
-
- RuleFinder ruleFinder = mock(RuleFinder.class);
- when(ruleFinder.findAll(any(RuleQuery.class))).thenReturn(Lists.newArrayList(rule1, rule2));
-
- RuleCache ruleCache = new RuleCache(ruleFinder);
- Rule actualRule1 = ruleCache.getRule("repo1", "rule1");
- Rule actualRule2 = ruleCache.getRule("repo2", "rule2");
-
- assertThat(actualRule1).isEqualTo(rule1);
- assertThat(actualRule2).isEqualTo(rule2);
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.common.io.Resources;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.qualitymodel.Characteristic;
-import org.sonar.api.qualitymodel.Model;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.utils.ValidationMessages;
-import org.sonar.core.qualitymodel.DefaultModelFinder;
-import org.sonar.core.rule.DefaultRuleFinder;
-import org.sonar.jpa.test.AbstractDbUnitTestCase;
-
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.util.List;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class TechnicalDebtManagerTest extends AbstractDbUnitTestCase {
-
- private TechnicalDebtManager manager;
- private TechnicalDebtModelFinder TechnicalDebtModelFinder;
-
- @Before
- public void init() throws Exception {
- TechnicalDebtModelFinder = mock(TechnicalDebtModelFinder.class);
- when(TechnicalDebtModelFinder.getContributingPluginList()).thenReturn(ImmutableList.of("java", "technical-debt"));
- when(TechnicalDebtModelFinder.createReaderForXMLFile("java")).thenReturn(
- new FileReader(Resources.getResource(TechnicalDebtManagerTest.class, "TechnicalDebtManagerTest/fake-java-model.xml").getPath()));
- // Mock default sqale model
- when(TechnicalDebtModelFinder.createReaderForXMLFile("technical-debt")).thenReturn(
- new FileReader(Resources.getResource(TechnicalDebtManagerTest.class, "TechnicalDebtManagerTest/fake-default-model.xml").getPath()));
-
- manager = new TechnicalDebtManager(getSessionFactory(), new DefaultModelFinder(getSessionFactory()), TechnicalDebtModelFinder, new XMLImporter());
- }
-
- @Test
- public void reset_model() {
- setupData("reset_model");
-
- Model model = manager.resetModel(ValidationMessages.create(), defaultRuleCache());
- assertThat(model.getCharacteristics().size()).isGreaterThan(3);
- assertThat(model.getCharacteristics().size()).isGreaterThan(model.getRootCharacteristics().size());
- for (Characteristic portabilityCharacteristic : model.getCharacteristicByKey("PORTABILITY").getChildren()) {
- assertThat(portabilityCharacteristic.getName()).contains("portability");
- Characteristic requirement = portabilityCharacteristic.getChildren().get(0);
- assertThat(requirement).isNotNull();
- Rule rule = requirement.getRule();
- assertThat(rule).isNotNull();
- assertThat(rule.getName()).isEqualTo("Regular exp");
- }
- assertThat(model.getCharacteristicByKey("testability")).isNull();
- assertThat(model.getCharacteristicByKey("unit_testability")).isNull();
- }
-
- @Test
- public void provided_plugin_should_not_override_default_model_when_resetting_model() throws FileNotFoundException {
- setupData("reset_model");
-
- Model model = manager.resetModel(ValidationMessages.create(), defaultRuleCache());
- // Default model values
- assertThat(model.getCharacteristicByKey("PORTABILITY").getName()).isEqualTo("Portability");
- assertThat(model.getCharacteristicByKey("COMPILER_RELATED_PORTABILITY").getName()).isEqualTo("Compiler related portability");
- assertThat(model.getCharacteristicByKey("HARDWARE_RELATED_PORTABILITY").getName()).isEqualTo("Hardware related portability");
- assertThat(model.getCharacteristicByKey("MAINTAINABILITY").getName()).isEqualTo("Maintainability");
-
- // Plugin has renamed it the value stay as defined by default model
- assertThat(model.getCharacteristicByKey("READABILITY").getName()).isEqualTo("Readability");
-
- // Characteristic provided only by the plugin
- assertThat(model.getCharacteristicByKey("UNDERSTANDABILITY").getName()).isEqualTo("Understandability related maintainability");
- }
-
- @Test
- public void not_fail_if_unknown_rule_when_resetting_model() {
- setupData("reset_model");
-
- RuleFinder ruleFinder = mock(RuleFinder.class);
- when(ruleFinder.findByKey(anyString(), anyString())).thenReturn(null);
-
- manager = new TechnicalDebtManager(getSessionFactory(), new DefaultModelFinder(getSessionFactory()),
- TechnicalDebtModelFinder, new XMLImporter());
-
- Model model = manager.resetModel(ValidationMessages.create(), new RuleCache(ruleFinder));
- assertThat(model.getCharacteristics().size()).isGreaterThanOrEqualTo(3);
- assertThat(model.getCharacteristics().size()).isGreaterThan(model.getRootCharacteristics().size());
- List<Characteristic> hardwareControls = model.getCharacteristicByKey("HARDWARE_RELATED_PORTABILITY").getChildren();
- assertThat(hardwareControls.isEmpty()).isTrue();
- }
-
- @Test
- public void create_initial_model() {
- Model model = manager.createInitialModel(ValidationMessages.create(), defaultRuleCache());
-
- // Default model values
- assertThat(model.getCharacteristicByKey("PORTABILITY").getName()).isEqualTo("Portability");
- assertThat(model.getCharacteristicByKey("COMPILER_RELATED_PORTABILITY").getName()).isEqualTo("Compiler related portability");
- assertThat(model.getCharacteristicByKey("HARDWARE_RELATED_PORTABILITY").getName()).isEqualTo("Hardware related portability");
- assertThat(model.getCharacteristicByKey("MAINTAINABILITY").getName()).isEqualTo("Maintainability");
-
- // Plugin has renamed it the value stay as defined by default model
- assertThat(model.getCharacteristicByKey("READABILITY").getName()).isEqualTo("Readability");
-
- // Characteristic provided only by the plugin
- assertThat(model.getCharacteristicByKey("UNDERSTANDABILITY").getName()).isEqualTo("Understandability related maintainability");
- }
-
- @Test
- public void persist_merge() {
- setupData("persist_merge");
-
- Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- Characteristic efficiency = with.createCharacteristicByKey("efficiency", "Efficiency");
- Characteristic ramEfficiency = with.createCharacteristicByKey("ram-efficiency", "RAM Efficiency");
- efficiency.addChild(ramEfficiency);
- ramEfficiency.addChild(with.createCharacteristicByRule(newRegexpRule()));
-
- manager.merge(with, ValidationMessages.create(), defaultRuleCache());
-
- checkTables("persist_merge", "quality_models", "characteristics", "characteristic_edges");
- }
-
- @Test
- public void persist_merge_with_plugin_files() throws Exception {
- setupData("persist_merge");
-
- manager.merge(Lists.newArrayList("java"), ValidationMessages.create(), defaultRuleCache());
-
- Model model = (new DefaultModelFinder(getSessionFactory())).findByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- assertThat(model.getCharacteristics()).contains(Characteristic.createByKey("PORTABILITY", "Portability"));
- }
-
- @Test
- public void persist_restore() {
- setupData("persist_restore");
-
- Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- Characteristic efficiency = with.createCharacteristicByKey("efficiency", "Efficiency");
- Characteristic ramEfficiency = with.createCharacteristicByKey("ram-efficiency", "RAM Efficiency");
- efficiency.addChild(ramEfficiency);
- ramEfficiency.addChild(with.createCharacteristicByRule(newRegexpRule()));
-
- manager.restore(with, ValidationMessages.create(), defaultRuleCache());
-
- checkTables("persist_restore", "quality_models", "characteristics", "characteristic_edges");
- }
-
- @Test
- public void warn_when_restoring_unknown_rule() {
- setupData("warn_when_restoring_unknown_rule");
-
- Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- Characteristic efficiency = with.createCharacteristicByKey("efficiency", "Efficiency");
- efficiency.addChild(with.createCharacteristicByRule(newRegexpRule()));
-
- ValidationMessages messages = ValidationMessages.create();
- manager.restore(with, messages, defaultRuleCache());
-
- checkTables("warn_when_restoring_unknown_rule", "quality_models", "characteristics", "characteristic_edges");
- assertThat(messages.getWarnings()).hasSize(1);
- assertThat(messages.getWarnings().get(0)).contains("regexp");
- }
-
- private RuleCache defaultRuleCache() {
- return new RuleCache(new DefaultRuleFinder(getSessionFactory()));
- }
-
- private Rule newRegexpRule() {
- return Rule.create("checkstyle", "regexp", "Regular expression");
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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.junit.Test;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.utils.ValidationMessages;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
-
-public class TechnicalDebtModelDefinitionTest {
-
- @Test
- public void shouldCreateModel() throws Exception {
- TechnicalDebtManager technicalDebtManager = mock(TechnicalDebtManager.class);
- RuleFinder ruleFinder = mock(RuleFinder.class);
- TechnicalDebtModelDefinition sqaleDefinition = new TechnicalDebtModelDefinition(technicalDebtManager, ruleFinder);
-
- sqaleDefinition.createModel();
-
- verify(technicalDebtManager, times(1)).createInitialModel(any(ValidationMessages.class), any(RuleCache.class));
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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 TechnicalDebtModelFinderTest {
-
- private static final String TEST_XML_PREFIX_PATH = "org/sonar/core/technicaldebt/TechnicalDebtModelFinderTest/";
-
- private TechnicalDebtModelFinder 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 TechnicalDebtModelFinder(repository, TEST_XML_PREFIX_PATH);
-
- // when
- modelFinder.start();
-
- // assert
- Collection<String> contributingPluginList = modelFinder.getContributingPluginList();
- assertThat(contributingPluginList.size()).isEqualTo(1);
- assertThat(contributingPluginList).containsOnly("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.getXMLFilePathForPlugin("foo")).isEqualTo(TEST_XML_PREFIX_PATH + "foo-model.xml");
- }
-
- private void initModel() throws MalformedURLException {
- Map<String, ClassLoader> contributingPluginKeyToClassLoader = Maps.newHashMap();
- contributingPluginKeyToClassLoader.put("csharp", newClassLoader());
- contributingPluginKeyToClassLoader.put("java", newClassLoader());
- modelFinder = new TechnicalDebtModelFinder(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-2013 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.junit.Before;
-import org.junit.Test;
-import org.sonar.api.qualitymodel.Characteristic;
-import org.sonar.api.qualitymodel.Model;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.rules.RuleQuery;
-import org.sonar.api.utils.ValidationMessages;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class TechnicalDebtModelTest {
-
- private Model model;
- private TechnicalDebtModel technicalDebtModel;
-
- @Before
- public void setUpModel() {
- model = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- technicalDebtModel = new TechnicalDebtModel(model);
- }
-
- @Test
- public void shouldMergeWithEmptyModel() {
- Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- Characteristic efficiency = with.createCharacteristicByKey("efficiency", "Efficiency");
- efficiency.addChild(with.createCharacteristicByKey("ram-efficiency", "RAM Efficiency"));
- with.createCharacteristicByKey("usability", "Usability");
-
- ValidationMessages messages = ValidationMessages.create();
-
- technicalDebtModel.mergeWith(with, messages, mockRuleCache());
-
- assertThat(model.getCharacteristics()).hasSize(3);
- assertThat(model.getRootCharacteristics()).hasSize(2);
- assertThat(model.getCharacteristicByKey("ram-efficiency").getDepth()).isEqualTo(Characteristic.ROOT_DEPTH + 1);
- assertThat(messages.getErrors()).isEmpty();
- }
-
- @Test
- public void shouldNotUpdateExistingCharacteristics() {
- model.createCharacteristicByKey("efficiency", "Efficiency");
-
- Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- with.createCharacteristicByKey("efficiency", "New efficiency");
-
- technicalDebtModel.mergeWith(with, ValidationMessages.create(), mockRuleCache());
-
- assertThat(model.getCharacteristics()).hasSize(1);
- assertThat(model.getRootCharacteristics()).hasSize(1);
- assertThat(model.getCharacteristicByKey("efficiency").getName()).isEqualTo("Efficiency");
- }
-
- @Test
- public void shouldWarnOnMissingRule() {
- Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
- Characteristic efficiency = with.createCharacteristicByKey("efficiency", "Efficiency");
- Rule fooRule = Rule.create("foo", "bar", "Bar");
- Characteristic requirement = with.createCharacteristicByRule(fooRule);
- efficiency.addChild(requirement);
-
- ValidationMessages messages = ValidationMessages.create();
-
- technicalDebtModel.mergeWith(with, messages, mockRuleCache());
-
- assertThat(model.getCharacteristics()).hasSize(1);
- assertThat(model.getCharacteristicByKey("efficiency").getName()).isEqualTo("Efficiency");
- assertThat(model.getCharacteristicByRule(fooRule)).isNull();
- assertThat(messages.getWarnings()).hasSize(1);
- assertThat(messages.getWarnings().get(0)).contains("foo"); // warning: the rule foo does not exist
- }
-
- private RuleCache mockRuleCache() {
- RuleFinder ruleFinder = mock(RuleFinder.class);
- when(ruleFinder.findAll(any(RuleQuery.class))).thenReturn(Lists.newArrayList(newRegexpRule()));
- return new RuleCache(ruleFinder);
- }
-
- private Rule newRegexpRule() {
- return Rule.create("checkstyle", "regexp", "Regular expression");
- }
-}
-
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 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.sonar.api.qualitymodel.Characteristic;
-import org.sonar.api.qualitymodel.Model;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.rules.RuleQuery;
-import org.sonar.api.utils.ValidationMessages;
-
-import java.io.IOException;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class XMLImporterTest {
-
- @Test
- public void shouldImportXML() {
- RuleCache ruleCache = mockRuleCache();
-
- String xml = getFileContent("shouldImportXML.xml");
-
- ValidationMessages messages = ValidationMessages.create();
- Model sqale = new XMLImporter().importXML(xml, messages, ruleCache);
-
- checkXmlCorrectlyImported(sqale, messages);
- }
-
- @Test
- public void shouldBadlyFormattedImportXML() {
- RuleCache ruleCache = mockRuleCache();
- String xml = getFileContent("shouldImportXML_badly-formatted.xml");
-
- ValidationMessages messages = ValidationMessages.create();
- Model sqale = new XMLImporter().importXML(xml, messages, ruleCache);
-
- checkXmlCorrectlyImported(sqale, messages);
- }
-
- @Test
- public void shouldLogWarningIfRuleNotFound() {
- RuleCache ruleCache = mockRuleCache();
- String xml = getFileContent("shouldLogWarningIfRuleNotFound.xml");
- ValidationMessages messages = ValidationMessages.create();
-
- Model sqale = new XMLImporter().importXML(xml, messages, ruleCache);
-
- assertThat(messages.getWarnings()).hasSize(1);
-
- // characteristics
- assertThat(sqale.getRootCharacteristics()).hasSize(1);
- Characteristic efficiency = sqale.getCharacteristicByKey("EFFICIENCY");
- assertThat(efficiency.getChildren()).isEmpty();
- assertThat(messages.getWarnings().get(0)).contains("findbugs");
- }
-
- @Test
- public void shouldNotifyOnUnexpectedValueTypeInXml() throws Exception {
-
- RuleCache ruleCache = mockRuleCache();
-
- String xml = getFileContent("shouldRejectXML_with_invalid_value.xml");
- ValidationMessages messages = ValidationMessages.create();
-
- new XMLImporter().importXML(xml, messages, ruleCache);
-
- assertThat(messages.getErrors()).hasSize(1);
- assertThat(messages.getErrors().get(0)).isEqualTo("Cannot import value 'abc' for field factor - Expected a numeric value instead");
- }
-
- private RuleCache mockRuleCache() {
- RuleFinder finder = mock(RuleFinder.class);
- when(finder.findAll(any(RuleQuery.class))).thenReturn(Lists.newArrayList(Rule.create("checkstyle", "Regexp", "Regular expression")));
- return new RuleCache(finder);
- }
-
- private void checkXmlCorrectlyImported(Model sqale, ValidationMessages messages) {
-
- assertThat(messages.getErrors()).isEmpty();
- assertThat(sqale.getName()).isEqualTo(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
-
- // characteristics
- assertThat(sqale.getRootCharacteristics()).hasSize(2);
- assertThat(sqale.getCharacteristicByKey("USABILITY").getDescription()).isEqualTo("Estimate usability");
- Characteristic efficiency = sqale.getCharacteristicByKey("EFFICIENCY");
- assertThat(efficiency.getName()).isEqualTo("Efficiency");
-
- // sub-characteristics
- assertThat(efficiency.getChildren()).hasSize(1);
- Characteristic requirement = efficiency.getChildren().get(0);
- assertThat(requirement.getRule().getRepositoryKey()).isEqualTo("checkstyle");
- assertThat(requirement.getRule().getKey()).isEqualTo("Regexp");
- assertThat(requirement.getPropertyTextValue("function", null)).isEqualTo("linear");
- assertThat(requirement.getPropertyValue("factor", null)).isEqualTo(3.2);
- }
-
- private String getFileContent(String file) {
- try {
- return Resources.toString(Resources.getResource(XMLImporterTest.class, "XMLImporterTest/" + file), Charsets.UTF_8);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
-}
+++ /dev/null
-<sqale>
- <chc>
- <key>PORTABILITY</key>
- <name>Portability</name>
- <chc>
- <key>COMPILER_RELATED_PORTABILITY</key>
- <name>Compiler related portability</name>
- </chc>
- <chc>
- <key>HARDWARE_RELATED_PORTABILITY</key>
- <name>Hardware related portability</name>
- </chc>
- </chc>
- <chc>
- <chc>
- <key>MAINTAINABILITY</key>
- <name>Maintainability</name>
- <chc>
- <key>READABILITY</key>
- <name>Readability</name>
- </chc>
- </chc>
- </chc>
-</sqale>
+++ /dev/null
-<sqale>
- <chc>
- <key>PORTABILITY</key>
- <name>Portability</name>
- <chc>
- <key>COMPILER_RELATED_PORTABILITY</key>
- <name>Compiler related portability</name>
- <chc>
- <rule-repo>checkstyle</rule-repo>
- <rule-key>import</rule-key>
- <prop>
- <key>remediationFactor</key>
- <val>30.0</val>
- <txt>mn</txt>
- </prop>
- <prop>
- <key>remediationFunction</key>
- <txt>linear</txt>
- </prop>
- </chc>
- </chc>
- <chc>
- <key>HARDWARE_RELATED_PORTABILITY</key>
- <name>Hardware related portability</name>
- <chc>
- <rule-repo>checkstyle</rule-repo>
- <rule-key>export</rule-key>
- <prop>
- <key>remediationFactor</key>
- <val>1.0</val>
- <txt>h</txt>
- </prop>
- <prop>
- <key>remediationFunction</key>
- <txt>linear</txt>
- </prop>
- </chc>
- </chc>
- </chc>
- <chc>
- <key>MAINTAINABILITY</key>
- <name>Maintainability</name>
- <chc>
- <key>READABILITY</key>
- <name>Readability related maintainability</name>
- <chc>
- <rule-repo>checkstyle</rule-repo>
- <rule-key>ConstantNameCheck</rule-key>
- <prop>
- <key>remediationFactor</key>
- <val>10.0</val>
- <txt>mn</txt>
- </prop>
- <prop>
- <key>remediationFunction</key>
- <txt>linear</txt>
- </prop>
- </chc>
- </chc>
- <chc>
- <key>UNDERSTANDABILITY</key>
- <name>Understandability related maintainability</name>
- <chc>
- <rule-repo>checkstyle</rule-repo>
- <rule-key>JavadocMethodCheck</rule-key>
- <prop>
- <key>remediationFactor</key>
- <val>30.0</val>
- <txt>mn</txt>
- </prop>
- <prop>
- <key>remediationFunction</key>
- <txt>linear</txt>
- </prop>
- </chc>
- </chc>
- </chc>
-</sqale>
+++ /dev/null
-<dataset>
- <!-- existing models -->
- <quality_models id="1" name="TECHNICAL_DEBT" />
- <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
- <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
-
- <characteristics id="3" kee="efficiency" name="Efficiency" quality_model_id="1" rule_id="[null]" characteristic_order="3" depth="1" description="[null]" enabled="true" />
- <characteristics id="4" kee="ram-efficiency" name="RAM Efficiency" quality_model_id="1" rule_id="[null]" characteristic_order="4" depth="2" description="[null]" enabled="true" />
- <characteristics id="5" kee="[null]" name="[null]" quality_model_id="1" rule_id="2" characteristic_order="5" depth="3" description="[null]" enabled="true" />
-
- <characteristic_edges child_id="2" parent_id="1"/>
- <characteristic_edges child_id="4" parent_id="3"/>
- <characteristic_edges child_id="5" parent_id="4"/>
-
- <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
-
- <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
- <rules id="2" plugin_rule_key="regexp" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
-</dataset>
+++ /dev/null
-<dataset>
- <!-- existing models -->
- <quality_models id="1" name="TECHNICAL_DEBT" />
-
- <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
- <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
- <characteristic_edges child_id="2" parent_id="1"/>
- <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
-
- <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
- <rules id="2" plugin_rule_key="regexp" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
-</dataset>
+++ /dev/null
-<dataset>
- <quality_models id="1" name="TECHNICAL_DEBT" />
-
- <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="false" />
- <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="false" />
- <characteristics id="3" kee="[null]" name="[null]" quality_model_id="1" rule_id="1" characteristic_order="3" depth="3" description="[null]" enabled="false" />
-
- <characteristics id="4" kee="efficiency" name="Efficiency" quality_model_id="1" rule_id="[null]" characteristic_order="4" depth="1" description="[null]" enabled="true" />
- <characteristics id="5" kee="ram-efficiency" name="RAM Efficiency" quality_model_id="1" rule_id="[null]" characteristic_order="5" depth="2" description="[null]" enabled="true" />
- <characteristics id="6" kee="[null]" name="[null]" quality_model_id="1" rule_id="2" characteristic_order="6" depth="3" description="[null]" enabled="true" />
-
- <characteristic_edges child_id="2" parent_id="1"/>
- <characteristic_edges child_id="3" parent_id="2"/>
- <characteristic_edges child_id="5" parent_id="4"/>
- <characteristic_edges child_id="6" parent_id="5"/>
-
- <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
-
- <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
- <rules id="2" plugin_rule_key="regexp" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
-</dataset>
+++ /dev/null
-<dataset>
- <quality_models id="1" name="TECHNICAL_DEBT" />
-
- <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
- <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
- <characteristics id="3" kee="[null]" name="[null]" quality_model_id="1" rule_id="1" characteristic_order="3" depth="3" description="[null]" enabled="true" />
-
- <characteristic_edges child_id="2" parent_id="1"/>
- <characteristic_edges child_id="3" parent_id="2"/>
-
- <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
-
- <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
- <rules id="2" plugin_rule_key="regexp" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
-</dataset>
+++ /dev/null
-<dataset>
- <!-- existing models -->
- <quality_models id="1" name="TECHNICAL_DEBT" />
- <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
- <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
- <characteristic_edges child_id="2" parent_id="1"/>
-
- <rules id="1" plugin_rule_key="import" plugin_config_key="import" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
- <rules id="2" plugin_rule_key="export" plugin_config_key="export" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
- <rules id="3" plugin_rule_key="ConstantNameCheck" plugin_config_key="ConstantNameCheck" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
- <rules id="4" plugin_rule_key="JavadocMethodCheck" plugin_config_key="JavadocMethodCheck" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
-</dataset>
+++ /dev/null
-<dataset>
- <quality_models id="1" name="foo" />
- <quality_models id="2" name="TECHNICAL_DEBT" />
-
- <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
- <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
-
- <characteristics id="3" kee="efficiency" name="Efficiency" quality_model_id="2" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
-
- <characteristic_edges child_id="2" parent_id="1"/>
- <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
-
- <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
-</dataset>
+++ /dev/null
-<dataset>
- <quality_models id="1" name="foo" />
- <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
- <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
- <characteristic_edges child_id="2" parent_id="1"/>
- <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
-
- <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
-</dataset>
+++ /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>
- <desc>Estimate usability</desc>
- </chc>
- <chc>
- <key>EFFICIENCY</key>
- <name>Efficiency</name>
-
- <chc>
- <rule-repo>checkstyle</rule-repo>
- <rule-key>Regexp</rule-key>
- <prop>
- <key>factor</key>
- <val>3.2</val>
- </prop>
- <prop>
- <key>function</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>checkstyle
- </rule-repo>
- <rule-key>Regexp
- </rule-key>
- <prop>
- <key>factor
- </key>
- <val>3.2
- </val>
- </prop>
- <prop>
- <key>function
- </key>
- <txt>linear
- </txt>
- </prop>
- </chc>
- </chc>
-
-</sqale>
\ No newline at end of file
+++ /dev/null
-<sqale>
- <chc>
- <key>EFFICIENCY</key>
- <name>Efficiency</name>
-
- <chc>
- <rule-repo>findbugs</rule-repo>
- <rule-key>Foo</rule-key>
- </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>checkstyle</rule-repo>
- <rule-key>Regexp</rule-key>
- <prop>
- <key>factor</key>
- <val>abc</val>
- </prop>
- <prop>
- <key>function</key>
- <txt>linear</txt>
- </prop>
- </chc>
- </chc>
-
-</sqale>
--- /dev/null
+<!--
+
+ SonarQube, open source software quality management tool.
+ Copyright (C) 2008-2013 SonarSource
+ mailto:contact AT sonarsource DOT com
+
+ SonarQube is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ SonarQube is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+-->
+<sqale>
+ <chc>
+ <key>PORTABILITY</key>
+ <name>Portability</name>
+ <chc>
+ <key>COMPILER_RELATED_PORTABILITY</key>
+ <name>Compiler</name>
+ </chc>
+ <chc>
+ <key>HARDWARE_RELATED_PORTABILITY</key>
+ <name>Hardware</name>
+ </chc>
+ <chc>
+ <key>LANGUAGE_RELATED_PORTABILITY</key>
+ <name>Language</name>
+ </chc>
+ <chc>
+ <key>OS_RELATED_PORTABILITY</key>
+ <name>OS</name>
+ </chc>
+ <chc>
+ <key>SOFTWARE_RELATED_PORTABILITY</key>
+ <name>Software</name>
+ </chc>
+ <chc>
+ <key>TIME_ZONE_RELATED_PORTABILITY</key>
+ <name>Time zone</name>
+ </chc>
+ </chc>
+ <chc>
+ <key>MAINTAINABILITY</key>
+ <name>Maintainability</name>
+ <chc>
+ <key>READABILITY</key>
+ <name>Readability</name>
+ </chc>
+ <chc>
+ <key>UNDERSTANDABILITY</key>
+ <name>Understandability</name>
+ </chc>
+ </chc>
+ <chc>
+ <key>SECURITY</key>
+ <name>Security</name>
+ <chc>
+ <key>API_ABUSE</key>
+ <name>API abuse</name>
+ </chc>
+ <chc>
+ <key>ERRORS</key>
+ <name>Errors</name>
+ </chc>
+ <chc>
+ <key>INPUT_VALIDATION_AND_REPRESENTATION</key>
+ <name>Input validation and representation</name>
+ </chc>
+ <chc>
+ <key>SECURITY_FEATURES</key>
+ <name>Security features</name>
+ </chc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+ <chc>
+ <key>MEMORY_EFFICIENCY</key>
+ <name>Memory use</name>
+ </chc>
+ <chc>
+ <key>CPU_EFFICIENCY</key>
+ <name>Processor use</name>
+ </chc>
+ </chc>
+ <chc>
+ <key>CHANGEABILITY</key>
+ <name>Changeability</name>
+ <chc>
+ <key>ARCHITECTURE_CHANGEABILITY</key>
+ <name>Architecture</name>
+ </chc>
+ <chc>
+ <key>DATA_CHANGEABILITY</key>
+ <name>Data</name>
+ </chc>
+ <chc>
+ <key>LOGIC_CHANGEABILITY</key>
+ <name>Logic</name>
+ </chc>
+ </chc>
+ <chc>
+ <key>RELIABILITY</key>
+ <name>Reliability</name>
+ <chc>
+ <key>ARCHITECTURE_RELIABILITY</key>
+ <name>Architecture</name>
+ </chc>
+ <chc>
+ <key>DATA_RELIABILITY</key>
+ <name>Data</name>
+ </chc>
+ <chc>
+ <key>EXCEPTION_HANDLING</key>
+ <name>Exception handling</name>
+ </chc>
+ <chc>
+ <key>FAULT_TOLERANCE</key>
+ <name>Fault tolerance</name>
+ </chc>
+ <chc>
+ <key>INSTRUCTION_RELIABILITY</key>
+ <name>Instruction</name>
+ </chc>
+ <chc>
+ <key>LOGIC_RELIABILITY</key>
+ <name>Logic</name>
+ </chc>
+ <chc>
+ <key>SYNCHRONIZATION_RELIABILITY</key>
+ <name>Synchronization</name>
+ </chc>
+ <chc>
+ <key>UNIT_TESTS</key>
+ <name>Unit tests</name>
+ </chc>
+ </chc>
+ <chc>
+ <key>TESTABILITY</key>
+ <name>Testability</name>
+ <chc>
+ <key>INTEGRATION_TESTABILITY</key>
+ <name>Integration level</name>
+ </chc>
+ <chc>
+ <key>UNIT_TESTABILITY</key>
+ <name>Unit level</name>
+ </chc>
+ </chc>
+</sqale>
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.technicaldebt;
+
+import com.google.common.collect.Maps;
+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;
+
+public class RuleCache {
+
+ private final RuleFinder ruleFinder;
+
+ private Map<String, Map<String, Rule>> cachedRules;
+
+ public RuleCache(RuleFinder ruleFinder) {
+ this.ruleFinder = ruleFinder;
+ }
+
+ @CheckForNull
+ public Rule getRule(String repository, String ruleKey) {
+ if(cachedRules == null) {
+ loadRules();
+ }
+ return lookUpRuleInCache(repository, ruleKey);
+ }
+
+ private void loadRules() {
+ cachedRules = 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);
+ }
+ }
+ }
+
+ 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-2013 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.technicaldebt;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.ServerExtension;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.qualitymodel.Characteristic;
+import org.sonar.api.qualitymodel.Model;
+import org.sonar.api.qualitymodel.ModelFinder;
+import org.sonar.api.utils.ValidationMessages;
+import org.sonar.jpa.session.DatabaseSessionFactory;
+
+import java.io.Reader;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * TODO test merge properties + property.value + text_value
+ */
+
+public class TechnicalDebtManager implements ServerExtension {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TechnicalDebtManager.class);
+
+ private DatabaseSessionFactory sessionFactory;
+ private ModelFinder modelFinder;
+ private TechnicalDebtModelFinder languageModelFinder;
+ private XMLImporter importer;
+
+ public TechnicalDebtManager(DatabaseSessionFactory sessionFactory, ModelFinder modelFinder,
+ TechnicalDebtModelFinder languageModelFinder, XMLImporter importer) {
+ this.sessionFactory = sessionFactory;
+ this.modelFinder = modelFinder;
+ this.languageModelFinder = languageModelFinder;
+ this.importer = importer;
+ }
+
+ public void restore(Model model, ValidationMessages messages, RuleCache rulesCache) {
+ Model persisted = modelFinder.findByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ if (persisted != null) {
+ disable(persisted);
+ }
+ merge(model, messages, rulesCache);
+ }
+
+ public Model resetModel(ValidationMessages messages, RuleCache ruleCache) {
+ Model persisted = modelFinder.findByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ if (persisted != null) {
+ disable(persisted);
+ }
+
+ Model model = modelFinder.findByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ importDefaultSqaleModel(model, messages, ruleCache);
+ populateModelWithInitialValues(model, messages, ruleCache);
+
+ DatabaseSession session = sessionFactory.getSession();
+ session.save(model);
+ session.commit();
+
+ return model;
+ }
+
+ public Model createInitialModel(ValidationMessages messages, RuleCache ruleCache) {
+ Model initialModel = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ importDefaultSqaleModel(initialModel, messages, ruleCache);
+ populateModelWithInitialValues(initialModel, messages, ruleCache);
+ return initialModel;
+ }
+
+ public void merge(List<String> pluginKeys, ValidationMessages messages, RuleCache ruleCache) {
+ for (String pluginKey : pluginKeys) {
+ ValidationMessages currentMessages = ValidationMessages.create();
+ Model model = null;
+ Reader xmlFileReader = null;
+ try {
+ xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey);
+ model = importer.importXML(xmlFileReader, currentMessages, ruleCache);
+ } finally {
+ IOUtils.closeQuietly(xmlFileReader);
+ }
+ if (!currentMessages.hasErrors()) {
+ merge(model, messages, ruleCache);
+ } else {
+ for (String error : currentMessages.getErrors()) {
+ messages.addErrorText(error);
+ }
+ }
+ }
+ }
+
+ public void merge(Model with, ValidationMessages messages, RuleCache ruleCache) {
+ DatabaseSession session = sessionFactory.getSession();
+ Model sqale = modelFinder.findByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ if (sqale == null) {
+ sqale = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ session.saveWithoutFlush(sqale);
+ }
+ new TechnicalDebtModel(sqale).mergeWith(with, messages, ruleCache);
+ session.saveWithoutFlush(sqale);
+ session.commit();
+ }
+
+ private void populateModelWithInitialValues(Model initialModel, ValidationMessages messages, RuleCache ruleCache) {
+ for (String pluginKey : getContributingPluginListWithoutSqale()) {
+ mergePlugin(initialModel, pluginKey, messages, ruleCache);
+ }
+ }
+
+ private void mergePlugin(Model initialModel, String pluginKey, ValidationMessages messages, RuleCache ruleCache) {
+ Model model = null;
+ Reader xmlFileReader = null;
+ try {
+ xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey);
+
+ model = importer.importXML(xmlFileReader, messages, ruleCache);
+ } finally {
+ IOUtils.closeQuietly(xmlFileReader);
+ }
+ messages.log(LOG);
+ if (!messages.hasErrors()) {
+ new TechnicalDebtModel(initialModel).mergeWith(model, messages, ruleCache);
+ messages.log(LOG);
+ }
+ }
+
+ private void disable(Model persisted) {
+ for (Characteristic root : persisted.getRootCharacteristics()) {
+ persisted.removeCharacteristic(root);
+ }
+ sessionFactory.getSession().commit();
+ }
+
+ private Collection<String> getContributingPluginListWithoutSqale(){
+ return languageModelFinder.getContributingPluginList();
+ }
+
+ private void importDefaultSqaleModel(Model initialModel, ValidationMessages messages, RuleCache ruleCache) {
+ mergePlugin(initialModel, TechnicalDebtModelFinder.DEFAULT_MODEL, messages, ruleCache);
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.technicaldebt;
+
+import org.sonar.api.qualitymodel.Characteristic;
+import org.sonar.api.qualitymodel.CharacteristicProperty;
+import org.sonar.api.qualitymodel.Model;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.utils.ValidationMessages;
+
+public class TechnicalDebtModel {
+
+ private Model model;
+
+ public TechnicalDebtModel(Model model) {
+ this.model = model;
+ }
+
+ public void mergeWith(Model with, ValidationMessages messages, RuleCache ruleCache) {
+ for (Characteristic characteristic : with.getCharacteristics()) {
+ if (isRequirement(characteristic)) {
+ mergeRequirement(model, characteristic, messages, ruleCache);
+ } else {
+ mergeCharacteristic(model, characteristic, messages);
+ }
+ }
+ }
+
+ private Characteristic mergeCharacteristic(Model target, Characteristic characteristic, ValidationMessages messages) {
+ Characteristic targetCharacteristic = target.getCharacteristicByKey(characteristic.getKey());
+ if (targetCharacteristic == null) {
+ targetCharacteristic = target.addCharacteristic(clone(characteristic));
+ if (!characteristic.getParents().isEmpty()) {
+ Characteristic parentTargetCharacteristic = mergeCharacteristic(target, characteristic.getParents().get(0), messages);
+ parentTargetCharacteristic.addChild(targetCharacteristic);
+ }
+ }
+ return targetCharacteristic;
+ }
+
+ private void mergeRequirement(Model target, Characteristic requirement, ValidationMessages messages,
+ RuleCache ruleCache) {
+ Characteristic targetRequirement = target.getCharacteristicByRule(requirement.getRule());
+ if (targetRequirement == null && !requirement.getParents().isEmpty()) {
+ Rule rule = ruleCache.getRule(requirement.getRule().getRepositoryKey(), requirement.getRule().getKey());
+ if (rule == null) {
+ messages.addWarningText("The rule " + requirement.getRule() + " does not exist.");
+
+ } else {
+ Characteristic parent = mergeCharacteristic(target, requirement.getParents().get(0), messages);
+ requirement = target.addCharacteristic(clone(requirement));
+ requirement.setRule(rule);
+ parent.addChild(requirement);
+ }
+ }
+ }
+
+ private boolean isRequirement(Characteristic characteristic) {
+ return characteristic.hasRule();
+ }
+
+ private Characteristic clone(Characteristic c) {
+ Characteristic clone = Characteristic.create();
+ clone.setRule(c.getRule());
+ clone.setDescription(c.getDescription());
+ clone.setKey(c.getKey());
+ clone.setName(c.getName(), false);
+ for (CharacteristicProperty property : c.getProperties()) {
+ clone.setProperty(property.getKey(), property.getTextValue()).setValue(property.getValue());
+ }
+ return clone;
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.technicaldebt;
+
+import org.sonar.api.qualitymodel.Model;
+import org.sonar.api.qualitymodel.ModelDefinition;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.utils.ValidationMessages;
+
+public final class TechnicalDebtModelDefinition extends ModelDefinition {
+
+ public static final String TECHNICAL_DEBT_MODEL = "TECHNICAL_DEBT";
+
+ private final TechnicalDebtManager technicalDebtManager;
+ private final RuleFinder ruleFinder;
+
+ public TechnicalDebtModelDefinition(TechnicalDebtManager technicalDebtManager, RuleFinder ruleFinder) {
+ super(TECHNICAL_DEBT_MODEL);
+ this.technicalDebtManager = technicalDebtManager;
+ this.ruleFinder = ruleFinder;
+ }
+
+ @Override
+ public Model createModel() {
+ RuleCache ruleCache = new RuleCache(ruleFinder);
+ return technicalDebtManager.createInitialModel(ValidationMessages.create(), ruleCache);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.technicaldebt;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
+import org.picocontainer.Startable;
+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 TechnicalDebtModelFinder 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;
+
+ /**
+ *
+ * @param pluginRepository
+ */
+ public TechnicalDebtModelFinder(PluginRepository pluginRepository) {
+ this.pluginRepository = pluginRepository;
+ this.xmlFilePrefix = XML_FILE_PREFIX;
+ }
+
+ @VisibleForTesting
+ TechnicalDebtModelFinder(PluginRepository pluginRepository, String xmlFilePrefix) {
+ this.pluginRepository = pluginRepository;
+ this.xmlFilePrefix = xmlFilePrefix;
+ }
+
+ @VisibleForTesting
+ TechnicalDebtModelFinder(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();
+ for (PluginMetadata metadata : pluginRepository.getMetadata()) {
+ String pluginKey = metadata.getKey();
+ ClassLoader classLoader = pluginRepository.getPlugin(pluginKey).getClass().getClassLoader();
+ if (classLoader.getResource(getXMLFilePathForPlugin(pluginKey)) != null) {
+ contributingPluginKeyToClassLoader.put(pluginKey, classLoader);
+ }
+ }
+ }
+ contributingPluginKeyToClassLoader = Collections.unmodifiableMap(contributingPluginKeyToClassLoader);
+ }
+
+ protected String getXMLFilePathForPlugin(String pluginKey) {
+ return xmlFilePrefix + pluginKey + XML_FILE_SUFFIX;
+ }
+
+ /**
+ * Returns the list of plugins that can contribute to the SQALE model (without the default model provided by this plugin).
+ *
+ * @return the list of plugin keys
+ */
+ public Collection<String> getContributingPluginList() {
+ Collection<String> contributingPlugins = newArrayList(contributingPluginKeyToClassLoader.keySet());
+ contributingPlugins.remove(DEFAULT_MODEL);
+ return contributingPlugins;
+ }
+
+ /**
+ * 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 = getXMLFilePathForPlugin(pluginKey);
+ return new InputStreamReader(classLoader.getResourceAsStream(xmlFilePath));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void stop() {
+ // Nothing to do
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.technicaldebt;
+
+public final class XMLConstants {
+
+ private XMLConstants(){
+ // Utility 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 CHARACTERISTIC_DESCRIPTION = "desc";
+ 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";
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.technicaldebt;
+
+import com.google.common.collect.Lists;
+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.qualitymodel.Characteristic;
+import org.sonar.api.qualitymodel.Model;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.utils.ValidationMessages;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.List;
+
+public class XMLImporter implements ServerExtension {
+
+ private static final Logger LOG = LoggerFactory.getLogger(XMLImporter.class);
+
+ public Model importXML(String xml, ValidationMessages messages, RuleCache ruleCache) {
+ return importXML(new StringReader(xml), messages, ruleCache);
+ }
+
+ public Model importXML(Reader xml, ValidationMessages messages, RuleCache repositoryCache) {
+ Model sqale = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ try {
+ SMInputFactory inputFactory = initStax();
+ SMHierarchicCursor cursor = inputFactory.rootElementCursor(xml);
+
+ // advance to <sqale>
+ cursor.advance();
+ SMInputCursor chcCursor = cursor.childElementCursor(XMLConstants.CHARACTERISTIC);
+
+ while (chcCursor.getNext() != null) {
+ processCharacteristic(sqale, 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 sqale;
+ }
+
+ 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 Characteristic processCharacteristic(Model sqale, SMInputCursor chcCursor, ValidationMessages messages,
+ RuleCache ruleCache) throws XMLStreamException {
+ Characteristic characteristic = Characteristic.create();
+ SMInputCursor cursor = chcCursor.childElementCursor();
+
+ String ruleRepositoryKey = null, ruleKey = null;
+ List<Characteristic> children = Lists.newArrayList();
+ while (cursor.getNext() != null) {
+ String node = cursor.getLocalName();
+ if (StringUtils.equals(node, XMLConstants.CHARACTERISTIC_KEY)) {
+ characteristic.setKey(cursor.collectDescendantText().trim());
+
+ } else if (StringUtils.equals(node, XMLConstants.CHARACTERISTIC_NAME)) {
+ characteristic.setName(cursor.collectDescendantText().trim(), false);
+
+ } else if (StringUtils.equals(node, XMLConstants.CHARACTERISTIC_DESCRIPTION)) {
+ characteristic.setDescription(cursor.collectDescendantText().trim());
+
+ } else if (StringUtils.equals(node, XMLConstants.PROPERTY)) {
+ processProperty(characteristic, cursor, messages);
+
+ } else if (StringUtils.equals(node, XMLConstants.CHARACTERISTIC)) {
+ children.add(processCharacteristic(sqale, cursor, messages, ruleCache));
+
+ } else if (StringUtils.equals(node, "rule-repo")) {
+ ruleRepositoryKey = cursor.collectDescendantText().trim();
+
+ } else if (StringUtils.equals(node, "rule-key")) {
+ ruleKey = cursor.collectDescendantText().trim();
+ }
+ }
+ fillRule(characteristic, ruleRepositoryKey, ruleKey, messages, ruleCache);
+
+ if (StringUtils.isNotBlank(characteristic.getKey()) || characteristic.getRule() != null) {
+ addCharacteristicToModel(sqale, characteristic, children);
+ return characteristic;
+ }
+ return null;
+ }
+
+ private void fillRule(Characteristic characteristic, String ruleRepositoryKey, String ruleKey, ValidationMessages messages,
+ RuleCache ruleCache) {
+ if (StringUtils.isNotBlank(ruleRepositoryKey) && StringUtils.isNotBlank(ruleKey)) {
+ Rule rule = ruleCache.getRule(ruleRepositoryKey, ruleKey);
+ if (rule != null) {
+ characteristic.setRule(rule);
+ } else {
+ messages.addWarningText("Rule not found: [repository=" + ruleRepositoryKey + ", key=" + ruleKey + "]");
+ }
+ }
+ }
+
+ private void addCharacteristicToModel(Model sqale, Characteristic characteristic, List<Characteristic> children) {
+ sqale.addCharacteristic(characteristic);
+ for (Characteristic child : children) {
+ if (child != null) {
+ sqale.addCharacteristic(child);
+ characteristic.addChild(child);
+ }
+ }
+ }
+
+ private void processProperty(Characteristic characteristic, SMInputCursor cursor, ValidationMessages messages) throws XMLStreamException {
+ SMInputCursor c = cursor.childElementCursor();
+ String key = null;
+ Double value = null;
+ String textValue = null;
+ while (c.getNext() != null) {
+ String node = c.getLocalName();
+ if (StringUtils.equals(node, XMLConstants.PROPERTY_KEY)) {
+ key = c.collectDescendantText().trim();
+
+ } else if (StringUtils.equals(node, XMLConstants.PROPERTY_VALUE)) {
+ String s = c.collectDescendantText().trim();
+ try {
+ value = NumberUtils.createDouble(s);
+ } catch (NumberFormatException ex) {
+ String message = String.format("Cannot import value '%s' for field %s - Expected a numeric value instead", s, key);
+ LOG.error(message, ex);
+ messages.addErrorText(message);
+ }
+ } else if (StringUtils.equals(node, XMLConstants.PROPERTY_TEXT_VALUE)) {
+ textValue = c.collectDescendantText().trim();
+ }
+ }
+ if (StringUtils.isNotBlank(key)) {
+ characteristic.setProperty(key, textValue).setValue(value);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.
+ */
+
+@ParametersAreNonnullByDefault
+package org.sonar.server.technicaldebt;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.technicaldebt;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RuleQuery;
+
+import java.util.Collections;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.*;
+
+public class RuleCacheTest {
+
+ @Test
+ public void should_lazy_load_rules_on_first_call() throws Exception {
+
+ RuleFinder ruleFinder = mock(RuleFinder.class);
+ when(ruleFinder.findAll(any(RuleQuery.class))).thenReturn(Collections.EMPTY_LIST);
+
+ RuleCache ruleCache = new RuleCache(ruleFinder);
+ ruleCache.getRule("", "");
+ ruleCache.getRule("", "");
+
+ verify(ruleFinder, times(1)).findAll(any(RuleQuery.class));
+ }
+
+ @Test
+ public void should_return_matching_rule() throws Exception {
+
+ Rule rule1 = Rule.create("repo1", "rule1");
+ Rule rule2 = Rule.create("repo2", "rule2");
+
+ RuleFinder ruleFinder = mock(RuleFinder.class);
+ when(ruleFinder.findAll(any(RuleQuery.class))).thenReturn(Lists.newArrayList(rule1, rule2));
+
+ RuleCache ruleCache = new RuleCache(ruleFinder);
+ Rule actualRule1 = ruleCache.getRule("repo1", "rule1");
+ Rule actualRule2 = ruleCache.getRule("repo2", "rule2");
+
+ assertThat(actualRule1).isEqualTo(rule1);
+ assertThat(actualRule2).isEqualTo(rule2);
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.technicaldebt;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.io.Resources;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.qualitymodel.Characteristic;
+import org.sonar.api.qualitymodel.Model;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.utils.ValidationMessages;
+import org.sonar.core.qualitymodel.DefaultModelFinder;
+import org.sonar.core.rule.DefaultRuleFinder;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class TechnicalDebtManagerTest extends AbstractDbUnitTestCase {
+
+ private TechnicalDebtManager manager;
+ private TechnicalDebtModelFinder TechnicalDebtModelFinder;
+
+ @Before
+ public void init() throws Exception {
+ TechnicalDebtModelFinder = mock(TechnicalDebtModelFinder.class);
+ when(TechnicalDebtModelFinder.getContributingPluginList()).thenReturn(ImmutableList.of("java", "technical-debt"));
+ when(TechnicalDebtModelFinder.createReaderForXMLFile("java")).thenReturn(
+ new FileReader(Resources.getResource(TechnicalDebtManagerTest.class, "TechnicalDebtManagerTest/fake-java-model.xml").getPath()));
+ // Mock default sqale model
+ when(TechnicalDebtModelFinder.createReaderForXMLFile("technical-debt")).thenReturn(
+ new FileReader(Resources.getResource(TechnicalDebtManagerTest.class, "TechnicalDebtManagerTest/fake-default-model.xml").getPath()));
+
+ manager = new TechnicalDebtManager(getSessionFactory(), new DefaultModelFinder(getSessionFactory()), TechnicalDebtModelFinder, new XMLImporter());
+ }
+
+ @Test
+ public void reset_model() {
+ setupData("reset_model");
+
+ Model model = manager.resetModel(ValidationMessages.create(), defaultRuleCache());
+ assertThat(model.getCharacteristics().size()).isGreaterThan(3);
+ assertThat(model.getCharacteristics().size()).isGreaterThan(model.getRootCharacteristics().size());
+ for (Characteristic portabilityCharacteristic : model.getCharacteristicByKey("PORTABILITY").getChildren()) {
+ assertThat(portabilityCharacteristic.getName()).contains("portability");
+ Characteristic requirement = portabilityCharacteristic.getChildren().get(0);
+ assertThat(requirement).isNotNull();
+ Rule rule = requirement.getRule();
+ assertThat(rule).isNotNull();
+ assertThat(rule.getName()).isEqualTo("Regular exp");
+ }
+ assertThat(model.getCharacteristicByKey("testability")).isNull();
+ assertThat(model.getCharacteristicByKey("unit_testability")).isNull();
+ }
+
+ @Test
+ public void provided_plugin_should_not_override_default_model_when_resetting_model() throws FileNotFoundException {
+ setupData("reset_model");
+
+ Model model = manager.resetModel(ValidationMessages.create(), defaultRuleCache());
+ // Default model values
+ assertThat(model.getCharacteristicByKey("PORTABILITY").getName()).isEqualTo("Portability");
+ assertThat(model.getCharacteristicByKey("COMPILER_RELATED_PORTABILITY").getName()).isEqualTo("Compiler related portability");
+ assertThat(model.getCharacteristicByKey("HARDWARE_RELATED_PORTABILITY").getName()).isEqualTo("Hardware related portability");
+ assertThat(model.getCharacteristicByKey("MAINTAINABILITY").getName()).isEqualTo("Maintainability");
+
+ // Plugin has renamed it the value stay as defined by default model
+ assertThat(model.getCharacteristicByKey("READABILITY").getName()).isEqualTo("Readability");
+
+ // Characteristic provided only by the plugin
+ assertThat(model.getCharacteristicByKey("UNDERSTANDABILITY").getName()).isEqualTo("Understandability related maintainability");
+ }
+
+ @Test
+ public void not_fail_if_unknown_rule_when_resetting_model() {
+ setupData("reset_model");
+
+ RuleFinder ruleFinder = mock(RuleFinder.class);
+ when(ruleFinder.findByKey(anyString(), anyString())).thenReturn(null);
+
+ manager = new TechnicalDebtManager(getSessionFactory(), new DefaultModelFinder(getSessionFactory()),
+ TechnicalDebtModelFinder, new XMLImporter());
+
+ Model model = manager.resetModel(ValidationMessages.create(), new RuleCache(ruleFinder));
+ assertThat(model.getCharacteristics().size()).isGreaterThanOrEqualTo(3);
+ assertThat(model.getCharacteristics().size()).isGreaterThan(model.getRootCharacteristics().size());
+ List<Characteristic> hardwareControls = model.getCharacteristicByKey("HARDWARE_RELATED_PORTABILITY").getChildren();
+ assertThat(hardwareControls.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void create_initial_model() {
+ Model model = manager.createInitialModel(ValidationMessages.create(), defaultRuleCache());
+
+ // Default model values
+ assertThat(model.getCharacteristicByKey("PORTABILITY").getName()).isEqualTo("Portability");
+ assertThat(model.getCharacteristicByKey("COMPILER_RELATED_PORTABILITY").getName()).isEqualTo("Compiler related portability");
+ assertThat(model.getCharacteristicByKey("HARDWARE_RELATED_PORTABILITY").getName()).isEqualTo("Hardware related portability");
+ assertThat(model.getCharacteristicByKey("MAINTAINABILITY").getName()).isEqualTo("Maintainability");
+
+ // Plugin has renamed it the value stay as defined by default model
+ assertThat(model.getCharacteristicByKey("READABILITY").getName()).isEqualTo("Readability");
+
+ // Characteristic provided only by the plugin
+ assertThat(model.getCharacteristicByKey("UNDERSTANDABILITY").getName()).isEqualTo("Understandability related maintainability");
+ }
+
+ @Test
+ public void persist_merge() {
+ setupData("persist_merge");
+
+ Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ Characteristic efficiency = with.createCharacteristicByKey("efficiency", "Efficiency");
+ Characteristic ramEfficiency = with.createCharacteristicByKey("ram-efficiency", "RAM Efficiency");
+ efficiency.addChild(ramEfficiency);
+ ramEfficiency.addChild(with.createCharacteristicByRule(newRegexpRule()));
+
+ manager.merge(with, ValidationMessages.create(), defaultRuleCache());
+
+ checkTables("persist_merge", "quality_models", "characteristics", "characteristic_edges");
+ }
+
+ @Test
+ public void persist_merge_with_plugin_files() throws Exception {
+ setupData("persist_merge");
+
+ manager.merge(Lists.newArrayList("java"), ValidationMessages.create(), defaultRuleCache());
+
+ Model model = (new DefaultModelFinder(getSessionFactory())).findByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ assertThat(model.getCharacteristics()).contains(Characteristic.createByKey("PORTABILITY", "Portability"));
+ }
+
+ @Test
+ public void persist_restore() {
+ setupData("persist_restore");
+
+ Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ Characteristic efficiency = with.createCharacteristicByKey("efficiency", "Efficiency");
+ Characteristic ramEfficiency = with.createCharacteristicByKey("ram-efficiency", "RAM Efficiency");
+ efficiency.addChild(ramEfficiency);
+ ramEfficiency.addChild(with.createCharacteristicByRule(newRegexpRule()));
+
+ manager.restore(with, ValidationMessages.create(), defaultRuleCache());
+
+ checkTables("persist_restore", "quality_models", "characteristics", "characteristic_edges");
+ }
+
+ @Test
+ public void warn_when_restoring_unknown_rule() {
+ setupData("warn_when_restoring_unknown_rule");
+
+ Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ Characteristic efficiency = with.createCharacteristicByKey("efficiency", "Efficiency");
+ efficiency.addChild(with.createCharacteristicByRule(newRegexpRule()));
+
+ ValidationMessages messages = ValidationMessages.create();
+ manager.restore(with, messages, defaultRuleCache());
+
+ checkTables("warn_when_restoring_unknown_rule", "quality_models", "characteristics", "characteristic_edges");
+ assertThat(messages.getWarnings()).hasSize(1);
+ assertThat(messages.getWarnings().get(0)).contains("regexp");
+ }
+
+ private RuleCache defaultRuleCache() {
+ return new RuleCache(new DefaultRuleFinder(getSessionFactory()));
+ }
+
+ private Rule newRegexpRule() {
+ return Rule.create("checkstyle", "regexp", "Regular expression");
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.technicaldebt;
+
+import org.junit.Test;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.utils.ValidationMessages;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.*;
+
+public class TechnicalDebtModelDefinitionTest {
+
+ @Test
+ public void shouldCreateModel() throws Exception {
+ TechnicalDebtManager technicalDebtManager = mock(TechnicalDebtManager.class);
+ RuleFinder ruleFinder = mock(RuleFinder.class);
+ TechnicalDebtModelDefinition sqaleDefinition = new TechnicalDebtModelDefinition(technicalDebtManager, ruleFinder);
+
+ sqaleDefinition.createModel();
+
+ verify(technicalDebtManager, times(1)).createInitialModel(any(ValidationMessages.class), any(RuleCache.class));
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.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 TechnicalDebtModelFinderTest {
+
+ private static final String TEST_XML_PREFIX_PATH = "org/sonar/server/technicaldebt/TechnicalDebtModelFinderTest/";
+
+ private TechnicalDebtModelFinder 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 TechnicalDebtModelFinder(repository, TEST_XML_PREFIX_PATH);
+
+ // when
+ modelFinder.start();
+
+ // assert
+ Collection<String> contributingPluginList = modelFinder.getContributingPluginList();
+ assertThat(contributingPluginList.size()).isEqualTo(1);
+ assertThat(contributingPluginList).containsOnly("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.getXMLFilePathForPlugin("foo")).isEqualTo(TEST_XML_PREFIX_PATH + "foo-model.xml");
+ }
+
+ private void initModel() throws MalformedURLException {
+ Map<String, ClassLoader> contributingPluginKeyToClassLoader = Maps.newHashMap();
+ contributingPluginKeyToClassLoader.put("csharp", newClassLoader());
+ contributingPluginKeyToClassLoader.put("java", newClassLoader());
+ modelFinder = new TechnicalDebtModelFinder(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-2013 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.technicaldebt;
+
+import com.google.common.collect.Lists;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.qualitymodel.Characteristic;
+import org.sonar.api.qualitymodel.Model;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RuleQuery;
+import org.sonar.api.utils.ValidationMessages;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class TechnicalDebtModelTest {
+
+ private Model model;
+ private TechnicalDebtModel technicalDebtModel;
+
+ @Before
+ public void setUpModel() {
+ model = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ technicalDebtModel = new TechnicalDebtModel(model);
+ }
+
+ @Test
+ public void shouldMergeWithEmptyModel() {
+ Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ Characteristic efficiency = with.createCharacteristicByKey("efficiency", "Efficiency");
+ efficiency.addChild(with.createCharacteristicByKey("ram-efficiency", "RAM Efficiency"));
+ with.createCharacteristicByKey("usability", "Usability");
+
+ ValidationMessages messages = ValidationMessages.create();
+
+ technicalDebtModel.mergeWith(with, messages, mockRuleCache());
+
+ assertThat(model.getCharacteristics()).hasSize(3);
+ assertThat(model.getRootCharacteristics()).hasSize(2);
+ assertThat(model.getCharacteristicByKey("ram-efficiency").getDepth()).isEqualTo(Characteristic.ROOT_DEPTH + 1);
+ assertThat(messages.getErrors()).isEmpty();
+ }
+
+ @Test
+ public void shouldNotUpdateExistingCharacteristics() {
+ model.createCharacteristicByKey("efficiency", "Efficiency");
+
+ Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ with.createCharacteristicByKey("efficiency", "New efficiency");
+
+ technicalDebtModel.mergeWith(with, ValidationMessages.create(), mockRuleCache());
+
+ assertThat(model.getCharacteristics()).hasSize(1);
+ assertThat(model.getRootCharacteristics()).hasSize(1);
+ assertThat(model.getCharacteristicByKey("efficiency").getName()).isEqualTo("Efficiency");
+ }
+
+ @Test
+ public void shouldWarnOnMissingRule() {
+ Model with = Model.createByName(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+ Characteristic efficiency = with.createCharacteristicByKey("efficiency", "Efficiency");
+ Rule fooRule = Rule.create("foo", "bar", "Bar");
+ Characteristic requirement = with.createCharacteristicByRule(fooRule);
+ efficiency.addChild(requirement);
+
+ ValidationMessages messages = ValidationMessages.create();
+
+ technicalDebtModel.mergeWith(with, messages, mockRuleCache());
+
+ assertThat(model.getCharacteristics()).hasSize(1);
+ assertThat(model.getCharacteristicByKey("efficiency").getName()).isEqualTo("Efficiency");
+ assertThat(model.getCharacteristicByRule(fooRule)).isNull();
+ assertThat(messages.getWarnings()).hasSize(1);
+ assertThat(messages.getWarnings().get(0)).contains("foo"); // warning: the rule foo does not exist
+ }
+
+ private RuleCache mockRuleCache() {
+ RuleFinder ruleFinder = mock(RuleFinder.class);
+ when(ruleFinder.findAll(any(RuleQuery.class))).thenReturn(Lists.newArrayList(newRegexpRule()));
+ return new RuleCache(ruleFinder);
+ }
+
+ private Rule newRegexpRule() {
+ return Rule.create("checkstyle", "regexp", "Regular expression");
+ }
+}
+
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.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.sonar.api.qualitymodel.Characteristic;
+import org.sonar.api.qualitymodel.Model;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RuleQuery;
+import org.sonar.api.utils.ValidationMessages;
+
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class XMLImporterTest {
+
+ @Test
+ public void shouldImportXML() {
+ RuleCache ruleCache = mockRuleCache();
+
+ String xml = getFileContent("shouldImportXML.xml");
+
+ ValidationMessages messages = ValidationMessages.create();
+ Model sqale = new XMLImporter().importXML(xml, messages, ruleCache);
+
+ checkXmlCorrectlyImported(sqale, messages);
+ }
+
+ @Test
+ public void shouldBadlyFormattedImportXML() {
+ RuleCache ruleCache = mockRuleCache();
+ String xml = getFileContent("shouldImportXML_badly-formatted.xml");
+
+ ValidationMessages messages = ValidationMessages.create();
+ Model sqale = new XMLImporter().importXML(xml, messages, ruleCache);
+
+ checkXmlCorrectlyImported(sqale, messages);
+ }
+
+ @Test
+ public void shouldLogWarningIfRuleNotFound() {
+ RuleCache ruleCache = mockRuleCache();
+ String xml = getFileContent("shouldLogWarningIfRuleNotFound.xml");
+ ValidationMessages messages = ValidationMessages.create();
+
+ Model sqale = new XMLImporter().importXML(xml, messages, ruleCache);
+
+ assertThat(messages.getWarnings()).hasSize(1);
+
+ // characteristics
+ assertThat(sqale.getRootCharacteristics()).hasSize(1);
+ Characteristic efficiency = sqale.getCharacteristicByKey("EFFICIENCY");
+ assertThat(efficiency.getChildren()).isEmpty();
+ assertThat(messages.getWarnings().get(0)).contains("findbugs");
+ }
+
+ @Test
+ public void shouldNotifyOnUnexpectedValueTypeInXml() throws Exception {
+
+ RuleCache ruleCache = mockRuleCache();
+
+ String xml = getFileContent("shouldRejectXML_with_invalid_value.xml");
+ ValidationMessages messages = ValidationMessages.create();
+
+ new XMLImporter().importXML(xml, messages, ruleCache);
+
+ assertThat(messages.getErrors()).hasSize(1);
+ assertThat(messages.getErrors().get(0)).isEqualTo("Cannot import value 'abc' for field factor - Expected a numeric value instead");
+ }
+
+ private RuleCache mockRuleCache() {
+ RuleFinder finder = mock(RuleFinder.class);
+ when(finder.findAll(any(RuleQuery.class))).thenReturn(Lists.newArrayList(Rule.create("checkstyle", "Regexp", "Regular expression")));
+ return new RuleCache(finder);
+ }
+
+ private void checkXmlCorrectlyImported(Model sqale, ValidationMessages messages) {
+
+ assertThat(messages.getErrors()).isEmpty();
+ assertThat(sqale.getName()).isEqualTo(TechnicalDebtModelDefinition.TECHNICAL_DEBT_MODEL);
+
+ // characteristics
+ assertThat(sqale.getRootCharacteristics()).hasSize(2);
+ assertThat(sqale.getCharacteristicByKey("USABILITY").getDescription()).isEqualTo("Estimate usability");
+ Characteristic efficiency = sqale.getCharacteristicByKey("EFFICIENCY");
+ assertThat(efficiency.getName()).isEqualTo("Efficiency");
+
+ // sub-characteristics
+ assertThat(efficiency.getChildren()).hasSize(1);
+ Characteristic requirement = efficiency.getChildren().get(0);
+ assertThat(requirement.getRule().getRepositoryKey()).isEqualTo("checkstyle");
+ assertThat(requirement.getRule().getKey()).isEqualTo("Regexp");
+ assertThat(requirement.getPropertyTextValue("function", null)).isEqualTo("linear");
+ assertThat(requirement.getPropertyValue("factor", null)).isEqualTo(3.2);
+ }
+
+ private String getFileContent(String file) {
+ try {
+ return Resources.toString(Resources.getResource(XMLImporterTest.class, "XMLImporterTest/" + file), Charsets.UTF_8);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
--- /dev/null
+<sqale>
+ <chc>
+ <key>PORTABILITY</key>
+ <name>Portability</name>
+ <chc>
+ <key>COMPILER_RELATED_PORTABILITY</key>
+ <name>Compiler related portability</name>
+ </chc>
+ <chc>
+ <key>HARDWARE_RELATED_PORTABILITY</key>
+ <name>Hardware related portability</name>
+ </chc>
+ </chc>
+ <chc>
+ <chc>
+ <key>MAINTAINABILITY</key>
+ <name>Maintainability</name>
+ <chc>
+ <key>READABILITY</key>
+ <name>Readability</name>
+ </chc>
+ </chc>
+ </chc>
+</sqale>
--- /dev/null
+<sqale>
+ <chc>
+ <key>PORTABILITY</key>
+ <name>Portability</name>
+ <chc>
+ <key>COMPILER_RELATED_PORTABILITY</key>
+ <name>Compiler related portability</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>import</rule-key>
+ <prop>
+ <key>remediationFactor</key>
+ <val>30.0</val>
+ <txt>mn</txt>
+ </prop>
+ <prop>
+ <key>remediationFunction</key>
+ <txt>linear</txt>
+ </prop>
+ </chc>
+ </chc>
+ <chc>
+ <key>HARDWARE_RELATED_PORTABILITY</key>
+ <name>Hardware related portability</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>export</rule-key>
+ <prop>
+ <key>remediationFactor</key>
+ <val>1.0</val>
+ <txt>h</txt>
+ </prop>
+ <prop>
+ <key>remediationFunction</key>
+ <txt>linear</txt>
+ </prop>
+ </chc>
+ </chc>
+ </chc>
+ <chc>
+ <key>MAINTAINABILITY</key>
+ <name>Maintainability</name>
+ <chc>
+ <key>READABILITY</key>
+ <name>Readability related maintainability</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>ConstantNameCheck</rule-key>
+ <prop>
+ <key>remediationFactor</key>
+ <val>10.0</val>
+ <txt>mn</txt>
+ </prop>
+ <prop>
+ <key>remediationFunction</key>
+ <txt>linear</txt>
+ </prop>
+ </chc>
+ </chc>
+ <chc>
+ <key>UNDERSTANDABILITY</key>
+ <name>Understandability related maintainability</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>JavadocMethodCheck</rule-key>
+ <prop>
+ <key>remediationFactor</key>
+ <val>30.0</val>
+ <txt>mn</txt>
+ </prop>
+ <prop>
+ <key>remediationFunction</key>
+ <txt>linear</txt>
+ </prop>
+ </chc>
+ </chc>
+ </chc>
+</sqale>
--- /dev/null
+<dataset>
+ <!-- existing models -->
+ <quality_models id="1" name="TECHNICAL_DEBT" />
+ <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
+ <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
+
+ <characteristics id="3" kee="efficiency" name="Efficiency" quality_model_id="1" rule_id="[null]" characteristic_order="3" depth="1" description="[null]" enabled="true" />
+ <characteristics id="4" kee="ram-efficiency" name="RAM Efficiency" quality_model_id="1" rule_id="[null]" characteristic_order="4" depth="2" description="[null]" enabled="true" />
+ <characteristics id="5" kee="[null]" name="[null]" quality_model_id="1" rule_id="2" characteristic_order="5" depth="3" description="[null]" enabled="true" />
+
+ <characteristic_edges child_id="2" parent_id="1"/>
+ <characteristic_edges child_id="4" parent_id="3"/>
+ <characteristic_edges child_id="5" parent_id="4"/>
+
+ <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
+
+ <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+ <rules id="2" plugin_rule_key="regexp" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+</dataset>
--- /dev/null
+<dataset>
+ <!-- existing models -->
+ <quality_models id="1" name="TECHNICAL_DEBT" />
+
+ <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
+ <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
+ <characteristic_edges child_id="2" parent_id="1"/>
+ <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
+
+ <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+ <rules id="2" plugin_rule_key="regexp" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+</dataset>
--- /dev/null
+<dataset>
+ <quality_models id="1" name="TECHNICAL_DEBT" />
+
+ <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="false" />
+ <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="false" />
+ <characteristics id="3" kee="[null]" name="[null]" quality_model_id="1" rule_id="1" characteristic_order="3" depth="3" description="[null]" enabled="false" />
+
+ <characteristics id="4" kee="efficiency" name="Efficiency" quality_model_id="1" rule_id="[null]" characteristic_order="4" depth="1" description="[null]" enabled="true" />
+ <characteristics id="5" kee="ram-efficiency" name="RAM Efficiency" quality_model_id="1" rule_id="[null]" characteristic_order="5" depth="2" description="[null]" enabled="true" />
+ <characteristics id="6" kee="[null]" name="[null]" quality_model_id="1" rule_id="2" characteristic_order="6" depth="3" description="[null]" enabled="true" />
+
+ <characteristic_edges child_id="2" parent_id="1"/>
+ <characteristic_edges child_id="3" parent_id="2"/>
+ <characteristic_edges child_id="5" parent_id="4"/>
+ <characteristic_edges child_id="6" parent_id="5"/>
+
+ <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
+
+ <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+ <rules id="2" plugin_rule_key="regexp" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+</dataset>
--- /dev/null
+<dataset>
+ <quality_models id="1" name="TECHNICAL_DEBT" />
+
+ <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
+ <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
+ <characteristics id="3" kee="[null]" name="[null]" quality_model_id="1" rule_id="1" characteristic_order="3" depth="3" description="[null]" enabled="true" />
+
+ <characteristic_edges child_id="2" parent_id="1"/>
+ <characteristic_edges child_id="3" parent_id="2"/>
+
+ <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
+
+ <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+ <rules id="2" plugin_rule_key="regexp" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+</dataset>
--- /dev/null
+<dataset>
+ <!-- existing models -->
+ <quality_models id="1" name="TECHNICAL_DEBT" />
+ <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
+ <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
+ <characteristic_edges child_id="2" parent_id="1"/>
+
+ <rules id="1" plugin_rule_key="import" plugin_config_key="import" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+ <rules id="2" plugin_rule_key="export" plugin_config_key="export" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+ <rules id="3" plugin_rule_key="ConstantNameCheck" plugin_config_key="ConstantNameCheck" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+ <rules id="4" plugin_rule_key="JavadocMethodCheck" plugin_config_key="JavadocMethodCheck" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+</dataset>
--- /dev/null
+<dataset>
+ <quality_models id="1" name="foo" />
+ <quality_models id="2" name="TECHNICAL_DEBT" />
+
+ <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
+ <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
+
+ <characteristics id="3" kee="efficiency" name="Efficiency" quality_model_id="2" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
+
+ <characteristic_edges child_id="2" parent_id="1"/>
+ <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
+
+ <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+</dataset>
--- /dev/null
+<dataset>
+ <quality_models id="1" name="foo" />
+ <characteristics id="1" kee="testability" name="Testability" quality_model_id="1" rule_id="[null]" characteristic_order="1" depth="1" description="[null]" enabled="true" />
+ <characteristics id="2" kee="unit_testability" name="Unit tests" quality_model_id="1" rule_id="[null]" characteristic_order="2" depth="2" description="[null]" enabled="true" />
+ <characteristic_edges child_id="2" parent_id="1"/>
+ <characteristic_properties id="1" characteristic_id="1" kee="foo" value="[null]" text_value="bar" />
+
+ <rules id="1" plugin_rule_key="import" plugin_config_key="regexp" plugin_name="checkstyle" description="[null]" priority="3" status="READY" cardinality="SINGLE" parent_id="[null]" name="Regular exp"/>
+</dataset>
--- /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>
+ <desc>Estimate usability</desc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>Regexp</rule-key>
+ <prop>
+ <key>factor</key>
+ <val>3.2</val>
+ </prop>
+ <prop>
+ <key>function</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>checkstyle
+ </rule-repo>
+ <rule-key>Regexp
+ </rule-key>
+ <prop>
+ <key>factor
+ </key>
+ <val>3.2
+ </val>
+ </prop>
+ <prop>
+ <key>function
+ </key>
+ <txt>linear
+ </txt>
+ </prop>
+ </chc>
+ </chc>
+
+</sqale>
\ No newline at end of file
--- /dev/null
+<sqale>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+
+ <chc>
+ <rule-repo>findbugs</rule-repo>
+ <rule-key>Foo</rule-key>
+ </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>checkstyle</rule-repo>
+ <rule-key>Regexp</rule-key>
+ <prop>
+ <key>factor</key>
+ <val>abc</val>
+ </prop>
+ <prop>
+ <key>function</key>
+ <txt>linear</txt>
+ </prop>
+ </chc>
+ </chc>
+
+</sqale>