diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-03-06 20:11:32 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-03-06 20:11:32 +0100 |
commit | 35ff7a07d5d86e2c0e6f9ce83645d1d6f34fc33a (patch) | |
tree | e9480c3bdefc4012e98c415685cd9323dd62a2a3 | |
parent | 0bfbecd3d0562f62e5e61c42579b2dce45d33a2c (diff) | |
download | sonarqube-35ff7a07d5d86e2c0e6f9ce83645d1d6f34fc33a.tar.gz sonarqube-35ff7a07d5d86e2c0e6f9ce83645d1d6f34fc33a.zip |
SONAR-5056 Synchronize rule debt definitions from plugin debt models
34 files changed, 1798 insertions, 109 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/CharacteristicsXMLImporter.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/CharacteristicsXMLImporter.java new file mode 100644 index 00000000000..9595f961143 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/CharacteristicsXMLImporter.java @@ -0,0 +1,112 @@ +/* + * 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.lang.StringUtils; +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.technicaldebt.batch.internal.DefaultCharacteristic; +import org.sonar.api.utils.ValidationMessages; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; + +import java.io.Reader; +import java.io.StringReader; + +public class CharacteristicsXMLImporter implements ServerExtension { + + private static final Logger LOG = LoggerFactory.getLogger(CharacteristicsXMLImporter.class); + + public static final String CHARACTERISTIC = "chc"; + public static final String CHARACTERISTIC_KEY = "key"; + public static final String CHARACTERISTIC_NAME = "name"; + + public DefaultTechnicalDebtModel importXML(String xml, ValidationMessages messages) { + return importXML(new StringReader(xml), messages); + } + + public DefaultTechnicalDebtModel importXML(Reader xml, ValidationMessages messages) { + DefaultTechnicalDebtModel model = new DefaultTechnicalDebtModel(); + try { + SMInputFactory inputFactory = initStax(); + SMHierarchicCursor cursor = inputFactory.rootElementCursor(xml); + + // advance to <sqale> + cursor.advance(); + SMInputCursor chcCursor = cursor.childElementCursor(CHARACTERISTIC); + + while (chcCursor.getNext() != null) { + processCharacteristic(model, null, chcCursor, messages); + } + + cursor.getStreamReader().closeCompletely(); + + } catch (XMLStreamException e) { + LOG.error("XML is not valid", e); + messages.addErrorText("XML is not valid: " + e.getMessage()); + } + return model; + } + + private SMInputFactory initStax() { + XMLInputFactory xmlFactory = XMLInputFactory2.newInstance(); + xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); + xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE); + xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE); + xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE); + return new SMInputFactory(xmlFactory); + } + + private DefaultCharacteristic processCharacteristic(DefaultTechnicalDebtModel model, DefaultCharacteristic parent, SMInputCursor chcCursor, ValidationMessages messages) throws XMLStreamException { + DefaultCharacteristic characteristic = new DefaultCharacteristic(); + SMInputCursor cursor = chcCursor.childElementCursor(); + while (cursor.getNext() != null) { + String node = cursor.getLocalName(); + if (StringUtils.equals(node, CHARACTERISTIC_KEY)) { + characteristic.setKey(cursor.collectDescendantText().trim()); + // Attached to parent only if a key is existing, otherwise characteristic with empty key can be added. + characteristic.setParent(parent); + + } else if (StringUtils.equals(node, CHARACTERISTIC_NAME)) { + characteristic.setName(cursor.collectDescendantText().trim(), false); + + // <chc> can contain characteristics or requirements + } else if (StringUtils.equals(node, CHARACTERISTIC)) { + processCharacteristic(model, characteristic, cursor, messages); + + } + } + + if (StringUtils.isNotBlank(characteristic.key()) && characteristic.isRoot()) { + characteristic.setOrder(model.rootCharacteristics().size() + 1); + model.addRootCharacteristic(characteristic); + return characteristic; + } + return null; + } + +} diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/DebtCharacteristicsSynchronizer.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/DebtCharacteristicsSynchronizer.java new file mode 100644 index 00000000000..8e8d95a1c55 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/DebtCharacteristicsSynchronizer.java @@ -0,0 +1,112 @@ +/* + * 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.apache.ibatis.session.SqlSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.ServerExtension; +import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.technicaldebt.db.CharacteristicDao; +import org.sonar.core.technicaldebt.db.CharacteristicDto; + +import java.io.Reader; +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; + +public class DebtCharacteristicsSynchronizer implements ServerExtension { + + private static final Logger LOG = LoggerFactory.getLogger(DebtCharacteristicsSynchronizer.class); + + private final MyBatis mybatis; + private final CharacteristicDao dao; + private final TechnicalDebtModelRepository languageModelFinder; + private final CharacteristicsXMLImporter importer; + + public DebtCharacteristicsSynchronizer(MyBatis mybatis, CharacteristicDao dao, TechnicalDebtModelRepository modelRepository, CharacteristicsXMLImporter importer) { + this.mybatis = mybatis; + this.dao = dao; + this.languageModelFinder = modelRepository; + this.importer = importer; + } + + public List<CharacteristicDto> synchronize(ValidationMessages messages) { + SqlSession session = mybatis.openSession(); + + List<CharacteristicDto> model = newArrayList(); + try { + model = synchronize(messages, session); + session.commit(); + } finally { + MyBatis.closeQuietly(session); + } + return model; + } + + public List<CharacteristicDto> synchronize(ValidationMessages messages, SqlSession session) { + DefaultTechnicalDebtModel defaultModel = loadModelFromXml(TechnicalDebtModelRepository.DEFAULT_MODEL, messages); + List<CharacteristicDto> model = loadOrCreateModelFromDb(defaultModel, session); + messages.log(LOG); + return model; + } + + private List<CharacteristicDto> loadOrCreateModelFromDb(DefaultTechnicalDebtModel defaultModel, SqlSession session) { + List<CharacteristicDto> characteristicDtos = loadModel(); + if (characteristicDtos.isEmpty()) { + return createTechnicalDebtModel(defaultModel, session); + } + return characteristicDtos; + } + + private List<CharacteristicDto> loadModel() { + return dao.selectEnabledCharacteristics(); + } + + private List<CharacteristicDto> createTechnicalDebtModel(DefaultTechnicalDebtModel defaultModel, SqlSession session) { + List<CharacteristicDto> characteristics = newArrayList(); + for (DefaultCharacteristic rootCharacteristic : defaultModel.rootCharacteristics()) { + CharacteristicDto rootCharacteristicDto = CharacteristicDto.toDto(rootCharacteristic, null); + dao.insert(rootCharacteristicDto, session); + characteristics.add(rootCharacteristicDto); + for (DefaultCharacteristic characteristic : rootCharacteristic.children()) { + CharacteristicDto characteristicDto = CharacteristicDto.toDto(characteristic, rootCharacteristicDto.getId()); + dao.insert(characteristicDto, session); + characteristics.add(characteristicDto); + } + } + return characteristics; + } + + public DefaultTechnicalDebtModel loadModelFromXml(String pluginKey, ValidationMessages messages) { + Reader xmlFileReader = null; + try { + xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey); + return importer.importXML(xmlFileReader, messages); + } finally { + IOUtils.closeQuietly(xmlFileReader); + } + } + +} diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/RuleDebtXMLImporter.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/RuleDebtXMLImporter.java new file mode 100644 index 00000000000..8ffb8e643a8 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/RuleDebtXMLImporter.java @@ -0,0 +1,335 @@ +/* + * 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.Predicate; +import com.google.common.base.Strings; +import com.google.common.collect.Iterables; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.math.NumberUtils; +import org.codehaus.stax2.XMLInputFactory2; +import org.codehaus.staxmate.SMInputFactory; +import org.codehaus.staxmate.in.SMHierarchicCursor; +import org.codehaus.staxmate.in.SMInputCursor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.ServerExtension; +import org.sonar.api.rule.RemediationFunction; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.Duration; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; + +import java.io.Reader; +import java.io.StringReader; +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; + +public class RuleDebtXMLImporter implements ServerExtension { + + private static final Logger LOG = LoggerFactory.getLogger(RuleDebtXMLImporter.class); + + public static final String CHARACTERISTIC = "chc"; + public static final String CHARACTERISTIC_KEY = "key"; + public static final String PROPERTY = "prop"; + + public static final String PROPERTY_KEY = "key"; + public static final String PROPERTY_VALUE = "val"; + public static final String PROPERTY_TEXT_VALUE = "txt"; + + public static final String REPOSITORY_KEY = "rule-repo"; + public static final String RULE_KEY = "rule-key"; + + public static final String PROPERTY_FUNCTION = "remediationFunction"; + public static final String PROPERTY_FACTOR = "remediationFactor"; + public static final String PROPERTY_OFFSET = "offset"; + + public List<RuleDebt> importXML(String xml) { + return importXML(new StringReader(xml)); + } + + public List<RuleDebt> importXML(Reader xml) { + List<RuleDebt> ruleDebts = newArrayList(); + try { + SMInputFactory inputFactory = initStax(); + SMHierarchicCursor cursor = inputFactory.rootElementCursor(xml); + + // advance to <sqale> + cursor.advance(); + SMInputCursor rootCursor = cursor.childElementCursor(CHARACTERISTIC); + while (rootCursor.getNext() != null) { + processCharacteristic(ruleDebts, null, null, rootCursor); + } + + cursor.getStreamReader().closeCompletely(); + } catch ( + XMLStreamException e + ) + + { + LOG.error("XML is not valid", e); + } + + return ruleDebts; + } + + 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 void processCharacteristic(List<RuleDebt> ruleDebts, @Nullable String rootKey, @Nullable String parentKey, SMInputCursor chcCursor) throws XMLStreamException { + String currentCharacteristicKey = null; + SMInputCursor cursor = chcCursor.childElementCursor(); + while (cursor.getNext() != null) { + String node = cursor.getLocalName(); + if (StringUtils.equals(node, CHARACTERISTIC_KEY)) { + currentCharacteristicKey = cursor.collectDescendantText().trim(); + } else if (StringUtils.equals(node, CHARACTERISTIC)) { + processCharacteristic(ruleDebts, parentKey, currentCharacteristicKey, cursor); + } else if (StringUtils.equals(node, REPOSITORY_KEY)) { + RuleDebt ruleDebt = processRequirement(cursor); + if (ruleDebt != null) { + if (rootKey != null) { + ruleDebt.characteristicKey = parentKey; + ruleDebts.add(ruleDebt); + } else { + LOG.warn("Rule '" + ruleDebt.ruleKey + "' is ignored because it's defined directly under a root characteristic."); + } + } + } + } + } + + private RuleDebt processRequirement(SMInputCursor cursor) + throws XMLStreamException { + + RuleDebt ruleDebt = new RuleDebt(); + String ruleRepositoryKey = cursor.collectDescendantText().trim(); + String ruleKey = null; + Properties properties = new Properties(); + while (cursor.getNext() != null) { + String node = cursor.getLocalName(); + if (StringUtils.equals(node, PROPERTY)) { + properties.add(processProperty(cursor)); + } else if (StringUtils.equals(node, RULE_KEY)) { + ruleKey = cursor.collectDescendantText().trim(); + } + } + if (StringUtils.isNotBlank(ruleRepositoryKey) && StringUtils.isNotBlank(ruleKey)) { + ruleDebt.ruleKey = RuleKey.of(ruleRepositoryKey, ruleKey); + } else { + return null; + } + return processFunctionsOnRequirement(ruleDebt, properties); + } + + private Property processProperty(SMInputCursor cursor) throws XMLStreamException { + SMInputCursor c = cursor.childElementCursor(); + String key = null; + int value = 0; + String textValue = null; + while (c.getNext() != null) { + String node = c.getLocalName(); + if (StringUtils.equals(node, PROPERTY_KEY)) { + key = c.collectDescendantText().trim(); + + } else if (StringUtils.equals(node, PROPERTY_VALUE)) { + String s = c.collectDescendantText().trim(); + try { + Double valueDouble = NumberUtils.createDouble(s); + value = valueDouble.intValue(); + } catch (NumberFormatException ex) { + LOG.error(String.format("Cannot import value '%s' for field %s - Expected a numeric value instead", s, key)); + } + } else if (StringUtils.equals(node, PROPERTY_TEXT_VALUE)) { + textValue = c.collectDescendantText().trim(); + } + } + return new Property(key, value, textValue); + } + + @CheckForNull + private RuleDebt processFunctionsOnRequirement(RuleDebt requirement, Properties properties) { + Property function = properties.function(); + Property factor = properties.factor(); + Property offset = properties.offset(); + + if (function != null) { + // Init with default values + requirement.factor = "0" + Duration.DAY; + requirement.offset = "0" + Duration.DAY; + + String functionKey = function.getTextValue(); + if ("linear_threshold".equals(functionKey)) { + function.setTextValue(RemediationFunction.LINEAR.name().toLowerCase()); + offset.setValue(0); + offset.setTextValue(Duration.DAY); + LOG.warn(String.format("Linear with threshold function is no longer used, remediation function of '%s' is replaced by linear.", requirement.ruleKey)); + } else if ("constant_resource".equals(functionKey)) { + LOG.warn(String.format("Constant/file function is no longer used, technical debt definitions on '%s' are ignored.", requirement.ruleKey)); + return null; + } + + requirement.function = RemediationFunction.valueOf(function.getTextValue().toUpperCase()); + if (factor != null) { + requirement.factor = Integer.toString(factor.getValue()); + requirement.factor += !Strings.isNullOrEmpty(factor.getTextValue()) ? factor.getTextValue() : Duration.DAY; + } + if (offset != null) { + requirement.offset = Integer.toString(offset.getValue()); + requirement.offset += !Strings.isNullOrEmpty(offset.getTextValue()) ? offset.getTextValue() : Duration.DAY; + } + return requirement; + } + return null; + } + + private static class Properties { + List<Property> properties; + + public Properties() { + this.properties = newArrayList(); + } + + public Properties add(Property property) { + this.properties.add(property); + return this; + } + + public Property function() { + return find(PROPERTY_FUNCTION); + } + + public Property factor() { + return find(PROPERTY_FACTOR); + } + + public Property offset() { + return find(PROPERTY_OFFSET); + } + + private Property find(final String key) { + return Iterables.find(properties, new Predicate<Property>() { + @Override + public boolean apply(Property input) { + return input.getKey().equals(key); + } + }, null); + } + } + + private static class Property { + String key; + int value; + String textValue; + + private Property(String key, int value, String textValue) { + this.key = key; + this.value = value; + this.textValue = textValue; + } + + private Property setValue(int value) { + this.value = value; + return this; + } + + private Property setTextValue(String textValue) { + this.textValue = textValue; + return this; + } + + private String getKey() { + return key; + } + + private int getValue() { + return value; + } + + private String getTextValue() { + return "mn".equals(textValue) ? Duration.MINUTE : textValue; + } + } + + public static class RuleDebt { + private RuleKey ruleKey; + private String characteristicKey; + private RemediationFunction function; + private String factor; + private String offset; + + public RuleKey ruleKey() { + return ruleKey; + } + + public RuleDebt setRuleKey(RuleKey ruleKey) { + this.ruleKey = ruleKey; + return this; + } + + public String characteristicKey() { + return characteristicKey; + } + + public RuleDebt setCharacteristicKey(String characteristicKey) { + this.characteristicKey = characteristicKey; + return this; + } + + public RemediationFunction function() { + return function; + } + + public RuleDebt setFunction(RemediationFunction function) { + this.function = function; + return this; + } + + public String factor() { + return factor; + } + + public RuleDebt setFactor(String factor) { + this.factor = factor; + return this; + } + + public String offset() { + return offset; + } + + public RuleDebt setOffset(String offset) { + this.offset = offset; + return this; + } + } + +} diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModelRepository.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModelRepository.java index 4ebddca0627..d103004a595 100644 --- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModelRepository.java +++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/TechnicalDebtModelRepository.java @@ -107,8 +107,7 @@ public class TechnicalDebtModelRepository implements ServerExtension, Startable * @return the list of plugin keys */ public Collection<String> getContributingPluginList() { - Collection<String> contributingPlugins = newArrayList(contributingPluginKeyToClassLoader.keySet()); - return contributingPlugins; + return newArrayList(contributingPluginKeyToClassLoader.keySet()); } /** diff --git a/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicDao.java b/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicDao.java index c94f61d580b..c608784ba68 100644 --- a/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicDao.java +++ b/sonar-core/src/main/java/org/sonar/core/technicaldebt/db/CharacteristicDao.java @@ -26,6 +26,7 @@ import org.sonar.api.ServerComponent; import org.sonar.core.persistence.MyBatis; import javax.annotation.CheckForNull; + import java.util.List; public class CharacteristicDao implements BatchComponent, ServerComponent { @@ -41,14 +42,17 @@ public class CharacteristicDao implements BatchComponent, ServerComponent { */ public List<CharacteristicDto> selectEnabledCharacteristics() { SqlSession session = mybatis.openSession(); - CharacteristicMapper mapper = session.getMapper(CharacteristicMapper.class); try { - return mapper.selectEnabledCharacteristics(); + return selectEnabledCharacteristics(session); } finally { MyBatis.closeQuietly(session); } } + public List<CharacteristicDto> selectEnabledCharacteristics(SqlSession session) { + return session.getMapper(CharacteristicMapper.class).selectEnabledCharacteristics(); + } + /** * @return only enabled root characteristics, order by order */ diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/CharacteristicsXMLImporterTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/CharacteristicsXMLImporterTest.java new file mode 100644 index 00000000000..6c5e26e15a4 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/CharacteristicsXMLImporterTest.java @@ -0,0 +1,93 @@ +/* + * 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.io.Resources; +import org.junit.Test; +import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; +import org.sonar.api.utils.ValidationMessages; + +import java.io.IOException; + +import static org.fest.assertions.Assertions.assertThat; + +public class CharacteristicsXMLImporterTest { + + @Test + public void import_characteristics() { + String xml = getFileContent("import_characteristics.xml"); + + ValidationMessages messages = ValidationMessages.create(); + DefaultTechnicalDebtModel debtModel = new CharacteristicsXMLImporter().importXML(xml, messages); + + assertThat(debtModel.rootCharacteristics()).hasSize(2); + assertThat(debtModel.rootCharacteristics().get(0).key()).isEqualTo("PORTABILITY"); + assertThat(debtModel.rootCharacteristics().get(1).key()).isEqualTo("MAINTAINABILITY"); + + DefaultCharacteristic portability = debtModel.characteristicByKey("PORTABILITY"); + assertThat(portability.order()).isEqualTo(1); + assertThat(portability.children()).hasSize(2); + assertThat(portability.children().get(0).key()).isEqualTo("COMPILER_RELATED_PORTABILITY"); + assertThat(debtModel.characteristicByKey("COMPILER_RELATED_PORTABILITY").parent().key()).isEqualTo("PORTABILITY"); + assertThat(portability.children().get(1).key()).isEqualTo("HARDWARE_RELATED_PORTABILITY"); + assertThat(debtModel.characteristicByKey("HARDWARE_RELATED_PORTABILITY").parent().key()).isEqualTo("PORTABILITY"); + + DefaultCharacteristic maintainability = debtModel.characteristicByKey("MAINTAINABILITY"); + assertThat(maintainability.order()).isEqualTo(2); + assertThat(maintainability.children()).hasSize(1); + assertThat(maintainability.children().get(0).key()).isEqualTo("READABILITY"); + assertThat(debtModel.characteristicByKey("READABILITY").parent().key()).isEqualTo("MAINTAINABILITY"); + } + + @Test + public void import_badly_formatted_xml() { + String xml = getFileContent("import_badly_formatted_xml.xml"); + + ValidationMessages messages = ValidationMessages.create(); + DefaultTechnicalDebtModel debtModel = new CharacteristicsXMLImporter().importXML(xml, messages); + + checkXmlCorrectlyImported(debtModel, messages); + } + + private void checkXmlCorrectlyImported(DefaultTechnicalDebtModel sqale, ValidationMessages messages) { + assertThat(messages.getErrors()).isEmpty(); + + // characteristics + assertThat(sqale.rootCharacteristics()).hasSize(2); + DefaultCharacteristic efficiency = sqale.characteristicByKey("EFFICIENCY"); + assertThat(efficiency.name()).isEqualTo("Efficiency"); + + // sub-characteristics + assertThat(efficiency.children()).hasSize(1); + DefaultCharacteristic memoryEfficiency = sqale.characteristicByKey("MEMORY_EFFICIENCY"); + assertThat(memoryEfficiency.name()).isEqualTo("Memory use"); + } + + private String getFileContent(String file) { + try { + return Resources.toString(Resources.getResource(CharacteristicsXMLImporterTest.class, "CharacteristicsXMLImporterTest/" + file), Charsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/DebtCharacteristicsSynchronizerTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/DebtCharacteristicsSynchronizerTest.java new file mode 100644 index 00000000000..657758bd413 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/DebtCharacteristicsSynchronizerTest.java @@ -0,0 +1,129 @@ +/* + * 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.ibatis.session.SqlSession; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; +import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.technicaldebt.db.CharacteristicDao; +import org.sonar.core.technicaldebt.db.CharacteristicDto; + +import java.io.Reader; +import java.util.Collections; +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class DebtCharacteristicsSynchronizerTest { + + @Mock + MyBatis myBatis; + + @Mock + SqlSession session; + + @Mock + TechnicalDebtModelRepository technicalDebtModelRepository; + + @Mock + CharacteristicDao dao; + + @Mock + CharacteristicsXMLImporter xmlImporter; + + Integer currentId = 1; + + private DefaultTechnicalDebtModel defaultModel; + + private DebtCharacteristicsSynchronizer manager; + + @Before + public void initAndMerge() throws Exception { + when(myBatis.openSession()).thenReturn(session); + + defaultModel = new DefaultTechnicalDebtModel(); + Reader defaultModelReader = mock(Reader.class); + when(technicalDebtModelRepository.createReaderForXMLFile("technical-debt")).thenReturn(defaultModelReader); + when(xmlImporter.importXML(eq(defaultModelReader), any(ValidationMessages.class))).thenReturn(defaultModel); + + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + CharacteristicDto dto = (CharacteristicDto) args[0]; + dto.setId(currentId++); + return null; + } + }).when(dao).insert(any(CharacteristicDto.class), any(SqlSession.class)); + + + manager = new DebtCharacteristicsSynchronizer(myBatis, dao, technicalDebtModelRepository, xmlImporter); + } + + @Test + public void create_default_model_on_first_execution_when_no_plugin() throws Exception { + DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("PORTABILITY"); + new DefaultCharacteristic().setKey("COMPILER_RELATED_PORTABILITY").setParent(rootCharacteristic); + defaultModel.addRootCharacteristic(rootCharacteristic); + + when(technicalDebtModelRepository.getContributingPluginList()).thenReturn(Collections.<String>emptyList()); + when(dao.selectEnabledCharacteristics()).thenReturn(Lists.<CharacteristicDto>newArrayList()); + + manager.synchronize(ValidationMessages.create()); + + verify(dao).selectEnabledCharacteristics(); + ArgumentCaptor<CharacteristicDto> characteristicCaptor = ArgumentCaptor.forClass(CharacteristicDto.class); + verify(dao, times(2)).insert(characteristicCaptor.capture(), eq(session)); + + List<CharacteristicDto> result = characteristicCaptor.getAllValues(); + assertThat(result.get(0).getKey()).isEqualTo("PORTABILITY"); + assertThat(result.get(1).getKey()).isEqualTo("COMPILER_RELATED_PORTABILITY"); + verifyNoMoreInteractions(dao); + } + + @Test + public void not_create_default_model_if_already_exists() throws Exception { + DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("PORTABILITY"); + new DefaultCharacteristic().setKey("COMPILER_RELATED_PORTABILITY").setParent(rootCharacteristic); + defaultModel.addRootCharacteristic(rootCharacteristic); + + when(technicalDebtModelRepository.getContributingPluginList()).thenReturn(Collections.<String>emptyList()); + when(dao.selectEnabledCharacteristics()).thenReturn(Lists.newArrayList(new CharacteristicDto())); + + manager.synchronize(ValidationMessages.create()); + + verify(dao, never()).insert(any(CharacteristicDto.class), eq(session)); + } + +} diff --git a/sonar-core/src/test/java/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest.java b/sonar-core/src/test/java/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest.java new file mode 100644 index 00000000000..f0400461761 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest.java @@ -0,0 +1,177 @@ +/* + * 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.io.Resources; +import org.junit.Test; +import org.sonar.api.rule.RemediationFunction; +import org.sonar.api.rule.RuleKey; + +import java.io.IOException; +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; + +public class RuleDebtXMLImporterTest { + + RuleDebtXMLImporter importer = new RuleDebtXMLImporter(); + + @Test + public void import_rules() { + String xml = getFileContent("import_rules.xml"); + + List<RuleDebtXMLImporter.RuleDebt> results = importer.importXML(xml); + assertThat(results).hasSize(2); + } + + @Test + public void import_linear() { + String xml = getFileContent("import_linear.xml"); + + List<RuleDebtXMLImporter.RuleDebt> results = importer.importXML(xml); + assertThat(results).hasSize(1); + + RuleDebtXMLImporter.RuleDebt ruleDebt = results.get(0); + assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); + assertThat(ruleDebt.ruleKey()).isEqualTo(RuleKey.of("checkstyle", "Regexp")); + assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR); + assertThat(ruleDebt.factor()).isEqualTo("3h"); + assertThat(ruleDebt.offset()).isEqualTo("0d"); + } + + @Test + public void import_linear_with_offset() { + String xml = getFileContent("import_linear_with_offset.xml"); + + List<RuleDebtXMLImporter.RuleDebt> results = importer.importXML(xml); + assertThat(results).hasSize(1); + + RuleDebtXMLImporter.RuleDebt ruleDebt = results.get(0); + assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); + assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR_OFFSET); + assertThat(ruleDebt.factor()).isEqualTo("3h"); + assertThat(ruleDebt.offset()).isEqualTo("1min"); + } + + @Test + public void import_constant_issue() { + String xml = getFileContent("import_constant_issue.xml"); + + List<RuleDebtXMLImporter.RuleDebt> results = importer.importXML(xml); + assertThat(results).hasSize(1); + + RuleDebtXMLImporter.RuleDebt ruleDebt = results.get(0); + assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); + assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.CONSTANT_ISSUE); + assertThat(ruleDebt.factor()).isEqualTo("0d"); + assertThat(ruleDebt.offset()).isEqualTo("3d"); + } + + @Test + public void use_default_unit_when_no_unit() { + String xml = getFileContent("use_default_unit_when_no_unit.xml"); + + List<RuleDebtXMLImporter.RuleDebt> results = importer.importXML(xml); + assertThat(results).hasSize(1); + + RuleDebtXMLImporter.RuleDebt ruleDebt = results.get(0); + assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); + assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR); + assertThat(ruleDebt.factor()).isEqualTo("3d"); + assertThat(ruleDebt.offset()).isEqualTo("1d"); + } + + @Test + public void replace_mn_by_min() { + String xml = getFileContent("replace_mn_by_min.xml"); + + List<RuleDebtXMLImporter.RuleDebt> results = importer.importXML(xml); + assertThat(results).hasSize(1); + + RuleDebtXMLImporter.RuleDebt ruleDebt = results.get(0); + assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); + assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR); + assertThat(ruleDebt.factor()).isEqualTo("3min"); + assertThat(ruleDebt.offset()).isEqualTo("0d"); + } + + @Test + public void convert_deprecated_linear_with_threshold_function_by_linear_function() { + String xml = getFileContent("convert_deprecated_linear_with_threshold_function_by_linear_function.xml"); + + List<RuleDebtXMLImporter.RuleDebt> results = importer.importXML(xml); + assertThat(results).hasSize(1); + + RuleDebtXMLImporter.RuleDebt ruleDebt = results.get(0); + assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); + assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR); + assertThat(ruleDebt.factor()).isEqualTo("3h"); + assertThat(ruleDebt.offset()).isEqualTo("0d"); + } + + @Test + public void ignore_deprecated_constant_per_file_function() { + String xml = getFileContent("ignore_deprecated_constant_per_file_function.xml"); + + List<RuleDebtXMLImporter.RuleDebt> results = importer.importXML(xml); + assertThat(results).isEmpty(); + } + + @Test + public void ignore_rule_on_root_characteristics() { + String xml = getFileContent("ignore_rule_on_root_characteristics.xml"); + + List<RuleDebtXMLImporter.RuleDebt> results = importer.importXML(xml); + assertThat(results).isEmpty(); + } + + @Test + public void import_badly_formatted_xml() { + String xml = getFileContent("import_badly_formatted_xml.xml"); + + List<RuleDebtXMLImporter.RuleDebt> results = importer.importXML(xml); + assertThat(results).hasSize(1); + + RuleDebtXMLImporter.RuleDebt ruleDebt = results.get(0); + assertThat(ruleDebt.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); + assertThat(ruleDebt.ruleKey()).isEqualTo(RuleKey.of("checkstyle", "Regexp")); + assertThat(ruleDebt.function()).isEqualTo(RemediationFunction.LINEAR); + assertThat(ruleDebt.factor()).isEqualTo("3h"); + assertThat(ruleDebt.offset()).isEqualTo("0d"); + } + + @Test + public void ignore_invalid_value() throws Exception { + String xml = getFileContent("ignore_invalid_value.xml"); + List<RuleDebtXMLImporter.RuleDebt> results = importer.importXML(xml); + assertThat(results).isEmpty(); + } + + private String getFileContent(String file) { + try { + return Resources.toString(Resources.getResource(RuleDebtXMLImporterTest.class, "RuleDebtXMLImporterTest/" + file), Charsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/CharacteristicsXMLImporterTest/import_badly_formatted_xml.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/CharacteristicsXMLImporterTest/import_badly_formatted_xml.xml new file mode 100644 index 00000000000..3abb870623d --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/CharacteristicsXMLImporterTest/import_badly_formatted_xml.xml @@ -0,0 +1,43 @@ +<!-- + ~ 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>USABILITY + </key> + <name>Usability + </name> + <desc>Estimate usability + </desc> + </chc> + <chc> + <key>EFFICIENCY + </key> + <name>Efficiency + </name> + <chc> + <key>MEMORY_EFFICIENCY + </key> + <name>Memory use + </name> + </chc> + </chc> + +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/CharacteristicsXMLImporterTest/import_characteristics.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/CharacteristicsXMLImporterTest/import_characteristics.xml new file mode 100644 index 00000000000..bc7f7569560 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/CharacteristicsXMLImporterTest/import_characteristics.xml @@ -0,0 +1,22 @@ +<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> + <key>MAINTAINABILITY</key> + <name>Maintainability</name> + <chc> + <key>READABILITY</key> + <name>Readability</name> + </chc> + </chc> +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/Import_linear_with_offset.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/Import_linear_with_offset.xml new file mode 100644 index 00000000000..d1af28f5a4c --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/Import_linear_with_offset.xml @@ -0,0 +1,54 @@ +<!-- + ~ 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>USABILITY</key> + <name>Usability</name> + <desc>Estimate usability</desc> + </chc> + <chc> + <key>EFFICIENCY</key> + <name>Efficiency</name> + <chc> + <key>MEMORY_EFFICIENCY</key> + <name>Memory use</name> + <chc> + <rule-repo>checkstyle</rule-repo> + <rule-key>Regexp</rule-key> + <prop> + <key>remediationFactor</key> + <val>3.0</val> + <txt>h</txt> + </prop> + <prop> + <key>remediationFunction</key> + <txt>linear_offset</txt> + </prop> + <prop> + <key>offset</key> + <val>1.0</val> + <txt>min</txt> + </prop> + </chc> + </chc> + </chc> + +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/convert_deprecated_linear_with_threshold_function_by_linear_function.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/convert_deprecated_linear_with_threshold_function_by_linear_function.xml new file mode 100644 index 00000000000..9ebc69b94a6 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/convert_deprecated_linear_with_threshold_function_by_linear_function.xml @@ -0,0 +1,36 @@ +<sqale> + <chc> + <key>USABILITY</key> + <name>Usability</name> + <desc>Estimate usability</desc> + </chc> + <chc> + <key>EFFICIENCY</key> + <name>Efficiency</name> + <chc> + <key>MEMORY_EFFICIENCY</key> + <name>Memory use</name> + <chc> + <rule-repo>checkstyle</rule-repo> + <rule-key>Regexp</rule-key> + <prop> + <key>remediationFunction</key> + <!-- Should be replaced by linear --> + <txt>linear_threshold</txt> + </prop> + <prop> + <key>remediationFactor</key> + <val>3.0</val> + <txt>h</txt> + </prop> + <!-- Should be ignored --> + <prop> + <key>offset</key> + <val>1.0</val> + <txt>h</txt> + </prop> + </chc> + </chc> + </chc> + +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/ignore_deprecated_constant_per_file_function.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/ignore_deprecated_constant_per_file_function.xml new file mode 100644 index 00000000000..4b8ae3f6475 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/ignore_deprecated_constant_per_file_function.xml @@ -0,0 +1,25 @@ +<sqale> + <chc> + <key>EFFICIENCY</key> + <name>Efficiency</name> + <chc> + <key>MEMORY_EFFICIENCY</key> + <name>Memory use</name> + <!-- Should be ignored --> + <chc> + <rule-repo>checkstyle</rule-repo> + <rule-key>Regexp</rule-key> + <prop> + <key>remediationFactor</key> + <val>3.0</val> + <txt>h</txt> + </prop> + <prop> + <key>remediationFunction</key> + <txt>constant_resource</txt> + </prop> + </chc> + </chc> + </chc> + +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/ignore_invalid_value.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/ignore_invalid_value.xml new file mode 100644 index 00000000000..bb6bdbb4afb --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/ignore_invalid_value.xml @@ -0,0 +1,28 @@ +<sqale> + <chc> + <key>USABILITY</key> + <name>Usability</name> + <desc>Estimate usability</desc> + </chc> + <chc> + <key>EFFICIENCY</key> + <name>Efficiency</name> + <chc> + <key>MEMORY_EFFICIENCY</key> + <name>Memory use</name> + <chc> + <rule-repo>checkstyle</rule-repo> + <rule-key>Regexp</rule-key> + <prop> + <key>factor</key> + <val>abc</val> + </prop> + <prop> + <key>function</key> + <txt>linear</txt> + </prop> + </chc> + </chc> + </chc> + +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/ignore_rule_on_root_characteristics.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/ignore_rule_on_root_characteristics.xml new file mode 100644 index 00000000000..bcf3ed867d3 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/ignore_rule_on_root_characteristics.xml @@ -0,0 +1,19 @@ +<sqale> + <chc> + <key>EFFICIENCY</key> + <name>Efficiency</name> + <chc> + <rule-repo>checkstyle</rule-repo> + <rule-key>Regexp</rule-key> + <prop> + <key>remediationFactor</key> + <val>3.0</val> + <txt>h</txt> + </prop> + <prop> + <key>remediationFunction</key> + <txt>linear</txt> + </prop> + </chc> + </chc> +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_badly_formatted_xml.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_badly_formatted_xml.xml new file mode 100644 index 00000000000..6c7d153992c --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_badly_formatted_xml.xml @@ -0,0 +1,43 @@ +<sqale> + <chc> + <key>USABILITY + </key> + <name>Usability + </name> + <desc>Estimate usability + </desc> + </chc> + <chc> + <key>EFFICIENCY + </key> + <name>Efficiency + </name> + <chc> + <key>MEMORY_EFFICIENCY + </key> + <name>Memory use + </name> + <chc> + <rule-repo>checkstyle + </rule-repo> + <rule-key>Regexp + </rule-key> + <prop> + <key>remediationFactor + </key> + <val>3.0 + </val> + <txt>h + </txt> + </prop> + <prop> + <key>remediationFunction + </key> + <txt>linear + </txt> + </prop> + </chc> + </chc> + </chc> + +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_constant_issue.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_constant_issue.xml new file mode 100644 index 00000000000..86b1f551fbe --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_constant_issue.xml @@ -0,0 +1,29 @@ +<sqale> + <chc> + <key>USABILITY</key> + <name>Usability</name> + <desc>Estimate usability</desc> + </chc> + <chc> + <key>EFFICIENCY</key> + <name>Efficiency</name> + <chc> + <key>MEMORY_EFFICIENCY</key> + <name>Memory use</name> + <chc> + <rule-repo>checkstyle</rule-repo> + <rule-key>Regexp</rule-key> + <prop> + <key>offset</key> + <val>3.0</val> + <txt>d</txt> + </prop> + <prop> + <key>remediationFunction</key> + <txt>constant_issue</txt> + </prop> + </chc> + </chc> + </chc> + +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_linear.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_linear.xml new file mode 100644 index 00000000000..f641a5185ec --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_linear.xml @@ -0,0 +1,29 @@ +<sqale> + <chc> + <key>USABILITY</key> + <name>Usability</name> + <desc>Estimate usability</desc> + </chc> + <chc> + <key>EFFICIENCY</key> + <name>Efficiency</name> + <chc> + <key>MEMORY_EFFICIENCY</key> + <name>Memory use</name> + <chc> + <rule-repo>checkstyle</rule-repo> + <rule-key>Regexp</rule-key> + <prop> + <key>remediationFactor</key> + <val>3.0</val> + <txt>h</txt> + </prop> + <prop> + <key>remediationFunction</key> + <txt>linear</txt> + </prop> + </chc> + </chc> + </chc> + +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_rules.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_rules.xml new file mode 100644 index 00000000000..d035d7bde00 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/import_rules.xml @@ -0,0 +1,58 @@ +<sqale> + <chc> + <key>USABILITY</key> + <name>Usability</name> + <desc>Estimate usability</desc> + </chc> + <chc> + <key>EFFICIENCY</key> + <name>Efficiency</name> + <chc> + <key>MEMORY_EFFICIENCY</key> + <name>Memory use</name> + <chc> + <rule-repo>checkstyle</rule-repo> + <rule-key>Regexp</rule-key> + <prop> + <key>remediationFactor</key> + <val>3.0</val> + <txt>h</txt> + </prop> + <prop> + <key>remediationFunction</key> + <txt>linear</txt> + </prop> + </chc> + </chc> + </chc> + <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> + <rule-repo>checkstyle</rule-repo> + <rule-key>Regexp2</rule-key> + <prop> + <key>remediationFactor</key> + <val>3.0</val> + <txt>h</txt> + </prop> + <prop> + <key>remediationFunction</key> + <txt>linear</txt> + </prop> + <prop> + <key>offset</key> + <val>1.0</val> + <txt>h</txt> + </prop> + </chc> + </chc> + </chc> +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/replace_mn_by_min.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/replace_mn_by_min.xml new file mode 100644 index 00000000000..3254fef60ce --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/replace_mn_by_min.xml @@ -0,0 +1,49 @@ +<!-- + ~ 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>USABILITY</key> + <name>Usability</name> + <desc>Estimate usability</desc> + </chc> + <chc> + <key>EFFICIENCY</key> + <name>Efficiency</name> + <chc> + <key>MEMORY_EFFICIENCY</key> + <name>Memory use</name> + <chc> + <rule-repo>checkstyle</rule-repo> + <rule-key>Regexp</rule-key> + <prop> + <key>remediationFactor</key> + <val>3.0</val> + <txt>mn</txt> + </prop> + <prop> + <key>remediationFunction</key> + <txt>linear</txt> + </prop> + </chc> + </chc> + </chc> + +</sqale> diff --git a/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/use_default_unit_when_no_unit.xml b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/use_default_unit_when_no_unit.xml new file mode 100644 index 00000000000..f05e51228b3 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/technicaldebt/RuleDebtXMLImporterTest/use_default_unit_when_no_unit.xml @@ -0,0 +1,32 @@ +<sqale> + <chc> + <key>USABILITY</key> + <name>Usability</name> + <desc>Estimate usability</desc> + </chc> + <chc> + <key>EFFICIENCY</key> + <name>Efficiency</name> + <chc> + <key>MEMORY_EFFICIENCY</key> + <name>Memory use</name> + <chc> + <rule-repo>checkstyle</rule-repo> + <rule-key>Regexp</rule-key> + <prop> + <key>remediationFactor</key> + <val>3.0</val> + </prop> + <prop> + <key>remediationFunction</key> + <txt>linear</txt> + </prop> + <prop> + <key>offset</key> + <val>1.0</val> + </prop> + </chc> + </chc> + </chc> + +</sqale> diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index 4a4f73cb465..d08ff9f9a66 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -63,10 +63,7 @@ import org.sonar.core.qualitygate.db.QualityGateConditionDao; import org.sonar.core.qualitygate.db.QualityGateDao; import org.sonar.core.resource.DefaultResourcePermissions; import org.sonar.core.rule.DefaultRuleFinder; -import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager; -import org.sonar.core.technicaldebt.TechnicalDebtModelRepository; -import org.sonar.core.technicaldebt.TechnicalDebtModelSynchronizer; -import org.sonar.core.technicaldebt.TechnicalDebtXMLImporter; +import org.sonar.core.technicaldebt.*; import org.sonar.core.test.TestPlanPerspectiveLoader; import org.sonar.core.test.TestablePerspectiveLoader; import org.sonar.core.timemachine.Periods; @@ -387,8 +384,11 @@ public final class Platform { // technical debt servicesContainer.addSingleton(DebtService.class); servicesContainer.addSingleton(TechnicalDebtModelSynchronizer.class); + servicesContainer.addSingleton(DebtCharacteristicsSynchronizer.class); servicesContainer.addSingleton(TechnicalDebtModelRepository.class); servicesContainer.addSingleton(TechnicalDebtXMLImporter.class); + servicesContainer.addSingleton(RuleDebtXMLImporter.class); + servicesContainer.addSingleton(CharacteristicsXMLImporter.class); servicesContainer.addSingleton(DefaultTechnicalDebtManager.class); // source @@ -437,6 +437,7 @@ public final class Platform { startupContainer.addSingleton(RuleRegistration.class); startupContainer.addSingleton(RegisterNewProfiles.class); startupContainer.addSingleton(JdbcDriverDeployer.class); + startupContainer.addSingleton(RegisterDebtCharacteristicModel.class); startupContainer.addSingleton(RegisterTechnicalDebtModel.class); startupContainer.addSingleton(DeleteDeprecatedMeasures.class); startupContainer.addSingleton(GeneratePluginIndex.class); diff --git a/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRuleDefinitions.java b/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRuleDefinitions.java index 650aae01ff0..f96f117a034 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRuleDefinitions.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/DeprecatedRuleDefinitions.java @@ -19,37 +19,58 @@ */ package org.sonar.server.rule; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; -import org.sonar.api.server.rule.RuleDefinitions; -import org.sonar.api.server.rule.RuleParamType; import org.sonar.api.rules.RuleParam; import org.sonar.api.rules.RuleRepository; +import org.sonar.api.server.rule.RuleDefinitions; +import org.sonar.api.server.rule.RuleParamType; import org.sonar.check.Cardinality; import org.sonar.core.i18n.RuleI18nManager; +import org.sonar.core.technicaldebt.RuleDebtXMLImporter; +import org.sonar.core.technicaldebt.TechnicalDebtModelRepository; import javax.annotation.CheckForNull; +import java.io.Reader; +import java.util.Collection; +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; + /** * Inject deprecated RuleRepository into RuleDefinitions for backward-compatibility. * * @since 4.2 */ public class DeprecatedRuleDefinitions implements RuleDefinitions { + private final RuleI18nManager i18n; private final RuleRepository[] repositories; - public DeprecatedRuleDefinitions(RuleI18nManager i18n, RuleRepository[] repositories) { + private final TechnicalDebtModelRepository languageModelFinder; + private final RuleDebtXMLImporter importer; + + public DeprecatedRuleDefinitions(RuleI18nManager i18n, RuleRepository[] repositories, TechnicalDebtModelRepository languageModelFinder, RuleDebtXMLImporter importer) { this.i18n = i18n; this.repositories = repositories; + this.languageModelFinder = languageModelFinder; + this.importer = importer; } - public DeprecatedRuleDefinitions(RuleI18nManager i18n) { - this(i18n, new RuleRepository[0]); + public DeprecatedRuleDefinitions(RuleI18nManager i18n, TechnicalDebtModelRepository languageModelFinder, RuleDebtXMLImporter importer) { + this(i18n, new RuleRepository[0], languageModelFinder, importer); } @Override public void define(Context context) { + // Load rule debt definitions from xml files provided by plugin + List<RuleDebtXMLImporter.RuleDebt> ruleDebts = loadRuleDebtList(); + for (RuleRepository repository : repositories) { // RuleRepository API does not handle difference between new and extended repositories, NewRepository newRepository; @@ -74,11 +95,22 @@ public class DeprecatedRuleDefinitions implements RuleDefinitions { newParam.setDescription(paramDescription(repository.getKey(), rule.getKey(), param)); newParam.setType(RuleParamType.parse(param.getType())); } + updateRuleDebtDefinitions(newRule, repository.getKey(), rule.getKey(), ruleDebts); } newRepository.done(); } } + private void updateRuleDebtDefinitions(NewRule newRule, String repoKey, String ruleKey, List<RuleDebtXMLImporter.RuleDebt> ruleDebts){ + RuleDebtXMLImporter.RuleDebt ruleDebt = findRequirement(ruleDebts, repoKey, ruleKey); + if (ruleDebt != null) { + newRule.setCharacteristicKey(ruleDebt.characteristicKey()); + newRule.setRemediationFunction(ruleDebt.function()); + newRule.setRemediationFactor(ruleDebt.factor()); + newRule.setRemediationOffset(ruleDebt.offset()); + } + } + @CheckForNull private String ruleName(String repositoryKey, org.sonar.api.rules.Rule rule) { String name = i18n.getName(repositoryKey, rule.getKey()); @@ -105,4 +137,39 @@ public class DeprecatedRuleDefinitions implements RuleDefinitions { ); return StringUtils.defaultIfBlank(desc, null); } + + public List<RuleDebtXMLImporter.RuleDebt> loadRuleDebtList() { + List<RuleDebtXMLImporter.RuleDebt> ruleDebtList = newArrayList(); + for (String pluginKey : getContributingPluginListWithoutSqale()) { + ruleDebtList.addAll(loadRuleDebtsFromXml(pluginKey)); + } + return ruleDebtList; + } + + public List<RuleDebtXMLImporter.RuleDebt> loadRuleDebtsFromXml(String pluginKey) { + Reader xmlFileReader = null; + try { + xmlFileReader = languageModelFinder.createReaderForXMLFile(pluginKey); + return importer.importXML(xmlFileReader); + } finally { + IOUtils.closeQuietly(xmlFileReader); + } + } + + private Collection<String> getContributingPluginListWithoutSqale() { + Collection<String> pluginList = newArrayList(languageModelFinder.getContributingPluginList()); + pluginList.remove(TechnicalDebtModelRepository.DEFAULT_MODEL); + return pluginList; + } + + @CheckForNull + private RuleDebtXMLImporter.RuleDebt findRequirement(List<RuleDebtXMLImporter.RuleDebt> requirements, final String repoKey, final String ruleKey) { + return Iterables.find(requirements, new Predicate<RuleDebtXMLImporter.RuleDebt>() { + @Override + public boolean apply(RuleDebtXMLImporter.RuleDebt input) { + return input.ruleKey().equals(RuleKey.of(repoKey, ruleKey)); + } + }, null); + } + } diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistration.java b/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistration.java index 2ab48037f8e..852273d4009 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistration.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/RuleRegistration.java @@ -20,6 +20,7 @@ package org.sonar.server.rule; import com.google.common.base.Function; +import com.google.common.base.Predicate; import com.google.common.collect.*; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; @@ -37,7 +38,10 @@ import org.sonar.check.Cardinality; import org.sonar.core.persistence.MyBatis; import org.sonar.core.qualityprofile.db.ActiveRuleDao; import org.sonar.core.rule.*; +import org.sonar.core.technicaldebt.db.CharacteristicDao; +import org.sonar.core.technicaldebt.db.CharacteristicDto; import org.sonar.server.qualityprofile.ProfilesManager; +import org.sonar.server.startup.RegisterDebtCharacteristicModel; import javax.annotation.CheckForNull; @@ -51,6 +55,7 @@ import static com.google.common.collect.Lists.newArrayList; * @since 4.2 */ public class RuleRegistration implements Startable { + private static final Logger LOG = LoggerFactory.getLogger(RuleRegistration.class); private final RuleDefinitionsLoader defLoader; @@ -62,11 +67,16 @@ public class RuleRegistration implements Startable { private final RuleTagDao ruleTagDao; private final RuleTagOperations ruleTagOperations; private final ActiveRuleDao activeRuleDao; + private final CharacteristicDao characteristicDao; private final System2 system = System2.INSTANCE; + /** + * @param registerTechnicalDebtModel used only to be started after init of the technical debt model + */ public RuleRegistration(RuleDefinitionsLoader defLoader, ProfilesManager profilesManager, RuleRegistry ruleRegistry, ESRuleTags esRuleTags, RuleTagOperations ruleTagOperations, - MyBatis myBatis, RuleDao ruleDao, RuleTagDao ruleTagDao, ActiveRuleDao activeRuleDao) { + MyBatis myBatis, RuleDao ruleDao, RuleTagDao ruleTagDao, ActiveRuleDao activeRuleDao, CharacteristicDao characteristicDao, + RegisterDebtCharacteristicModel registerTechnicalDebtModel) { this.defLoader = defLoader; this.profilesManager = profilesManager; this.ruleRegistry = ruleRegistry; @@ -76,6 +86,7 @@ public class RuleRegistration implements Startable { this.ruleDao = ruleDao; this.ruleTagDao = ruleTagDao; this.activeRuleDao = activeRuleDao; + this.characteristicDao = characteristicDao; } @Override @@ -85,8 +96,9 @@ public class RuleRegistration implements Startable { try { RuleDefinitions.Context context = defLoader.load(); Buffer buffer = new Buffer(system.now()); + List<CharacteristicDto> characteristicDtos = characteristicDao.selectEnabledCharacteristics(); selectRulesFromDb(buffer, sqlSession); - enableRuleDefinitions(context, buffer, sqlSession); + enableRuleDefinitions(context, buffer, characteristicDtos, sqlSession); List<RuleDto> removedRules = processRemainingDbRules(buffer, sqlSession); removeActiveRulesOnStillExistingRepositories(removedRules, context); index(buffer); @@ -120,27 +132,27 @@ public class RuleRegistration implements Startable { } } - private void enableRuleDefinitions(RuleDefinitions.Context context, Buffer buffer, SqlSession sqlSession) { + private void enableRuleDefinitions(RuleDefinitions.Context context, Buffer buffer, List<CharacteristicDto> characteristicDtos, SqlSession sqlSession) { for (RuleDefinitions.Repository repoDef : context.repositories()) { - enableRepository(buffer, sqlSession, repoDef); + enableRepository(buffer, sqlSession, repoDef, characteristicDtos); } for (RuleDefinitions.ExtendedRepository extendedRepoDef : context.extendedRepositories()) { if (context.repository(extendedRepoDef.key()) == null) { LOG.warn(String.format("Extension is ignored, repository %s does not exist", extendedRepoDef.key())); } else { - enableRepository(buffer, sqlSession, extendedRepoDef); + enableRepository(buffer, sqlSession, extendedRepoDef, characteristicDtos); } } } - private void enableRepository(Buffer buffer, SqlSession sqlSession, RuleDefinitions.ExtendedRepository repoDef) { + private void enableRepository(Buffer buffer, SqlSession sqlSession, RuleDefinitions.ExtendedRepository repoDef, List<CharacteristicDto> characteristicDtos) { int count = 0; for (RuleDefinitions.Rule ruleDef : repoDef.rules()) { RuleDto dto = buffer.rule(RuleKey.of(ruleDef.repository().key(), ruleDef.key())); if (dto == null) { - dto = enableAndInsert(buffer, sqlSession, ruleDef); + dto = enableAndInsert(buffer, sqlSession, ruleDef, characteristicDtos); } else { - enableAndUpdate(buffer, sqlSession, ruleDef, dto); + enableAndUpdate(buffer, sqlSession, ruleDef, dto, characteristicDtos); } buffer.markProcessed(dto); count++; @@ -151,8 +163,9 @@ public class RuleRegistration implements Startable { sqlSession.commit(); } - private RuleDto enableAndInsert(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef) { + private RuleDto enableAndInsert(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef, List<CharacteristicDto> characteristicDtos) { RemediationFunction remediationFunction = ruleDef.remediationFunction(); + RuleDto ruleDto = new RuleDto() .setCardinality(ruleDef.template() ? Cardinality.MULTIPLE : Cardinality.SINGLE) .setConfigKey(ruleDef.internalKey()) @@ -164,12 +177,17 @@ public class RuleRegistration implements Startable { .setSeverity(ruleDef.severity()) .setCreatedAt(buffer.now()) .setUpdatedAt(buffer.now()) - .setStatus(ruleDef.status().name()) - // TODO set default characteristic id - .setDefaultRemediationFunction(remediationFunction != null ? remediationFunction.name() : null) - .setDefaultRemediationFactor(ruleDef.remediationFactor()) - .setDefaultRemediationOffset(ruleDef.remediationOffset()) - .setEffortToFixL10nKey(ruleDef.effortToFixL10nKey()); + .setStatus(ruleDef.status().name()); + + CharacteristicDto characteristic = findCharacteristic(characteristicDtos, ruleDef); + if (characteristic != null) { + ruleDto.setDefaultCharacteristicId(characteristic.getId()) + .setDefaultRemediationFunction(remediationFunction != null ? remediationFunction.name() : null) + .setDefaultRemediationFactor(ruleDef.remediationFactor()) + .setDefaultRemediationOffset(ruleDef.remediationOffset()) + .setEffortToFixL10nKey(ruleDef.effortToFixL10nKey()); + } + ruleDao.insert(ruleDto, sqlSession); buffer.add(ruleDto); @@ -187,8 +205,8 @@ public class RuleRegistration implements Startable { return ruleDto; } - private void enableAndUpdate(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef, RuleDto dto) { - if (mergeRule(buffer, ruleDef, dto)) { + private void enableAndUpdate(Buffer buffer, SqlSession sqlSession, RuleDefinitions.Rule ruleDef, RuleDto dto, List<CharacteristicDto> characteristicDtos) { + if (mergeRule(buffer, ruleDef, dto, characteristicDtos)) { ruleDao.update(dto); } mergeParams(buffer, sqlSession, ruleDef, dto); @@ -196,7 +214,7 @@ public class RuleRegistration implements Startable { buffer.markProcessed(dto); } - private boolean mergeRule(Buffer buffer, RuleDefinitions.Rule def, RuleDto dto) { + private boolean mergeRule(Buffer buffer, RuleDefinitions.Rule def, RuleDto dto, List<CharacteristicDto> characteristicDtos) { boolean changed = false; if (!StringUtils.equals(dto.getName(), def.name())) { dto.setName(def.name()); @@ -229,34 +247,42 @@ public class RuleRegistration implements Startable { dto.setLanguage(def.repository().language()); changed = true; } - changed = mergeDebtRule(def, dto) || changed; + changed = mergeDebtDefinitions(def, dto, characteristicDtos) || changed; if (changed) { dto.setUpdatedAt(buffer.now()); } return changed; } - private boolean mergeDebtRule(RuleDefinitions.Rule def, RuleDto dto){ + private boolean mergeDebtDefinitions(RuleDefinitions.Rule def, RuleDto dto, List<CharacteristicDto> characteristicDtos) { boolean changed = false; - // TODO add characteristic id change verification + CharacteristicDto characteristic = findCharacteristic(characteristicDtos, def); + Integer characteristicId = characteristic != null ? characteristic.getId() : null; + RemediationFunction remediationFunction = characteristic != null ? def.remediationFunction() : null; + String remediationFactor = characteristic != null ? def.remediationFactor() : null; + String remediationOffset = characteristic != null ? def.remediationOffset() : null; + String effortToFixL10nKey = characteristic != null ? def.effortToFixL10nKey() : null; - RemediationFunction remediationFunction = def.remediationFunction(); + if (!ObjectUtils.equals(dto.getDefaultCharacteristicId(), characteristicId)) { + dto.setDefaultCharacteristicId(characteristicId); + changed = true; + } String remediationFunctionString = remediationFunction != null ? remediationFunction.name() : null; if (!StringUtils.equals(dto.getDefaultRemediationFunction(), remediationFunctionString)) { dto.setDefaultRemediationFunction(remediationFunctionString); changed = true; } - if (!StringUtils.equals(dto.getDefaultRemediationFactor(), def.remediationFactor())) { - dto.setDefaultRemediationFactor(def.remediationFactor()); + if (!StringUtils.equals(dto.getDefaultRemediationFactor(), remediationFactor)) { + dto.setDefaultRemediationFactor(remediationFactor); changed = true; } - if (!StringUtils.equals(dto.getDefaultRemediationOffset(), def.remediationOffset())) { - dto.setDefaultRemediationOffset(def.remediationOffset()); + if (!StringUtils.equals(dto.getDefaultRemediationOffset(), remediationOffset)) { + dto.setDefaultRemediationOffset(remediationOffset); changed = true; } - if (!StringUtils.equals(dto.getEffortToFixL10nKey(), def.effortToFixL10nKey())) { - dto.setEffortToFixL10nKey(def.effortToFixL10nKey()); + if (!StringUtils.equals(dto.getEffortToFixL10nKey(), effortToFixL10nKey)) { + dto.setEffortToFixL10nKey(effortToFixL10nKey); changed = true; } return changed; @@ -508,4 +534,21 @@ public class RuleRegistration implements Startable { unprocessedRuleIds.remove(ruleDto.getId()); } } + + @CheckForNull + private CharacteristicDto findCharacteristic(List<CharacteristicDto> characteristicDtos, RuleDefinitions.Rule ruleDef) { + final String key = ruleDef.characteristicKey(); + CharacteristicDto characteristicDto = Iterables.find(characteristicDtos, new Predicate<CharacteristicDto>() { + @Override + public boolean apply(CharacteristicDto input) { + // TODO remove check on null rule id when only characteristics without requirements will be returned + return input.getRuleId() == null && input.getKey().equals(key); + } + }, null); + // TODO check not root characteristic + if (characteristicDto == null) { + LOG.warn(String.format("Characteristic : '%s' has not been found, Technical debt definitions on rule '%s:%s' will be ignored", key, ruleDef.repository(), ruleDef.key())); + } + return characteristicDto; + } } diff --git a/sonar-server/src/main/java/org/sonar/server/startup/RegisterDebtCharacteristicModel.java b/sonar-server/src/main/java/org/sonar/server/startup/RegisterDebtCharacteristicModel.java new file mode 100644 index 00000000000..5395740c127 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/startup/RegisterDebtCharacteristicModel.java @@ -0,0 +1,45 @@ +/* + * 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.startup; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.utils.TimeProfiler; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.core.technicaldebt.DebtCharacteristicsSynchronizer; + +public class RegisterDebtCharacteristicModel { + + private static final Logger LOGGER = LoggerFactory.getLogger(RegisterDebtCharacteristicModel.class); + + private final DebtCharacteristicsSynchronizer manager; + + public RegisterDebtCharacteristicModel(DebtCharacteristicsSynchronizer manager) { + this.manager = manager; + } + + public void start() { + TimeProfiler profiler = new TimeProfiler(LOGGER).start("Register Debt Characteristics Model"); + manager.synchronize(ValidationMessages.create()); + profiler.stop(); + } + +} diff --git a/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRuleDefinitionsTest.java b/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRuleDefinitionsTest.java index 5937637d48d..00fd9e91ece 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRuleDefinitionsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/DeprecatedRuleDefinitionsTest.java @@ -20,23 +20,43 @@ package org.sonar.server.rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.rule.RemediationFunction; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; -import org.sonar.api.server.rule.RuleDefinitions; import org.sonar.api.rule.Severity; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RulePriority; import org.sonar.api.rules.RuleRepository; +import org.sonar.api.server.rule.RuleDefinitions; import org.sonar.core.i18n.RuleI18nManager; +import org.sonar.core.technicaldebt.RuleDebtXMLImporter; +import org.sonar.core.technicaldebt.TechnicalDebtModelRepository; +import java.io.Reader; import java.util.Arrays; import java.util.List; +import static com.google.common.collect.Lists.newArrayList; import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +@RunWith(MockitoJUnitRunner.class) public class DeprecatedRuleDefinitionsTest { + @Mock + RuleI18nManager i18n; + + @Mock + TechnicalDebtModelRepository debtModelRepository; + + @Mock + RuleDebtXMLImporter importer; + static class CheckstyleRules extends RuleRepository { public CheckstyleRules() { super("checkstyle", "java"); @@ -56,12 +76,24 @@ public class DeprecatedRuleDefinitionsTest { } } + static class UseBundles extends RuleRepository { + public UseBundles() { + super("checkstyle", "java"); + setName("Checkstyle"); + } + + @Override + public List<Rule> createRules() { + Rule rule = Rule.create("checkstyle", "ConstantName"); + rule.createParameter("format"); + return Arrays.asList(rule); + } + } + @Test public void wrap_deprecated_rule_repositories() throws Exception { RuleDefinitions.Context context = new RuleDefinitions.Context(); - RuleI18nManager i18n = mock(RuleI18nManager.class); - - new DeprecatedRuleDefinitions(i18n, new RuleRepository[]{new CheckstyleRules()}).define(context); + new DeprecatedRuleDefinitions(i18n, new RuleRepository[]{new CheckstyleRules()}, debtModelRepository, importer).define(context); assertThat(context.repositories()).hasSize(1); RuleDefinitions.Repository checkstyle = context.repository("checkstyle"); @@ -91,38 +123,21 @@ public class DeprecatedRuleDefinitionsTest { @Test public void emulate_the_day_deprecated_api_can_be_dropped() throws Exception { RuleDefinitions.Context context = new RuleDefinitions.Context(); - RuleI18nManager i18n = mock(RuleI18nManager.class); // no more RuleRepository ! - new DeprecatedRuleDefinitions(i18n); + new DeprecatedRuleDefinitions(i18n, debtModelRepository, importer); assertThat(context.repositories()).isEmpty(); } - - static class UseBundles extends RuleRepository { - public UseBundles() { - super("checkstyle", "java"); - setName("Checkstyle"); - } - - @Override - public List<Rule> createRules() { - Rule rule = Rule.create("checkstyle", "ConstantName"); - rule.createParameter("format"); - return Arrays.asList(rule); - } - } - @Test public void use_l10n_bundles() throws Exception { RuleDefinitions.Context context = new RuleDefinitions.Context(); - RuleI18nManager i18n = mock(RuleI18nManager.class); when(i18n.getName("checkstyle", "ConstantName")).thenReturn("Constant Name"); when(i18n.getDescription("checkstyle", "ConstantName")).thenReturn("Checks that constant names conform to the specified format"); when(i18n.getParamDescription("checkstyle", "ConstantName", "format")).thenReturn("Regular expression"); - new DeprecatedRuleDefinitions(i18n, new RuleRepository[]{new UseBundles()}).define(context); + new DeprecatedRuleDefinitions(i18n, new RuleRepository[]{new UseBundles()}, debtModelRepository, importer).define(context); RuleDefinitions.Repository checkstyle = context.repository("checkstyle"); RuleDefinitions.Rule rule = checkstyle.rule("ConstantName"); @@ -134,4 +149,38 @@ public class DeprecatedRuleDefinitionsTest { assertThat(param.name()).isEqualTo("format"); assertThat(param.description()).isEqualTo("Regular expression"); } + + @Test + public void define_rule_debt() throws Exception { + RuleDefinitions.Context context = new RuleDefinitions.Context(); + + List<RuleDebtXMLImporter.RuleDebt> ruleDebts = newArrayList( + new RuleDebtXMLImporter.RuleDebt() + .setCharacteristicKey("MEMORY_EFFICIENCY") + .setRuleKey(RuleKey.of("checkstyle", "ConstantName")) + .setFunction(RemediationFunction.LINEAR_OFFSET) + .setFactor("1d") + .setOffset("10min") + ); + + Reader javaModelReader = mock(Reader.class); + when(debtModelRepository.createReaderForXMLFile("java")).thenReturn(javaModelReader); + when(debtModelRepository.getContributingPluginList()).thenReturn(newArrayList("java")); + when(importer.importXML(eq(javaModelReader))).thenReturn(ruleDebts); + + new DeprecatedRuleDefinitions(i18n, new RuleRepository[]{new CheckstyleRules()}, debtModelRepository, importer).define(context); + + assertThat(context.repositories()).hasSize(1); + RuleDefinitions.Repository checkstyle = context.repository("checkstyle"); + assertThat(checkstyle.rules()).hasSize(1); + + RuleDefinitions.Rule rule = checkstyle.rule("ConstantName"); + assertThat(rule).isNotNull(); + assertThat(rule.key()).isEqualTo("ConstantName"); + assertThat(rule.characteristicKey()).isEqualTo("MEMORY_EFFICIENCY"); + assertThat(rule.remediationFunction()).isEqualTo(RemediationFunction.LINEAR_OFFSET); + assertThat(rule.remediationFactor()).isEqualTo("1d"); + assertThat(rule.remediationOffset()).isEqualTo("10min"); + } + } diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistrationTest.java b/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistrationTest.java index 3853bff4914..7b75b8b8401 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistrationTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/RuleRegistrationTest.java @@ -31,7 +31,9 @@ import org.sonar.core.persistence.MyBatis; import org.sonar.core.qualityprofile.db.ActiveRuleDao; import org.sonar.core.rule.RuleDao; import org.sonar.core.rule.RuleTagDao; +import org.sonar.core.technicaldebt.db.CharacteristicDao; import org.sonar.server.qualityprofile.ProfilesManager; +import org.sonar.server.startup.RegisterDebtCharacteristicModel; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Mockito.*; @@ -53,6 +55,7 @@ public class RuleRegistrationTest extends AbstractDaoTestCase { RuleDao ruleDao; RuleTagDao ruleTagDao; ActiveRuleDao activeRuleDao; + CharacteristicDao characteristicDao; @Before public void before() { @@ -61,8 +64,9 @@ public class RuleRegistrationTest extends AbstractDaoTestCase { ruleTagDao = new RuleTagDao(myBatis); activeRuleDao = new ActiveRuleDao(myBatis); ruleTagOperations = new RuleTagOperations(ruleTagDao, esRuleTags); + characteristicDao = new CharacteristicDao(myBatis); task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RuleDefinitions[]{new FakeRepository()}), - profilesManager, ruleRegistry, esRuleTags, ruleTagOperations, myBatis, ruleDao, ruleTagDao, activeRuleDao); + profilesManager, ruleRegistry, esRuleTags, ruleTagOperations, myBatis, ruleDao, ruleTagDao, activeRuleDao, characteristicDao, mock(RegisterDebtCharacteristicModel.class)); } @Test @@ -162,6 +166,14 @@ public class RuleRegistrationTest extends AbstractDaoTestCase { } @Test + public void remove_rule_debt_definitions_if_characteristic_not_found() { + setupData("remove_rule_debt_definitions_if_characteristic_not_found"); + task.start(); + + checkTables("remove_rule_debt_definitions_if_characteristic_not_found", EXCLUDED_COLUMN_NAMES, "rules"); + } + + @Test public void not_disable_template_rules_if_parent_is_enabled() { setupData("not_disable_template_rules_if_parent_is_enabled"); task.start(); @@ -189,7 +201,7 @@ public class RuleRegistrationTest extends AbstractDaoTestCase { @Test public void test_high_number_of_rules() { task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RuleDefinitions[]{new BigRepository()}), - profilesManager, ruleRegistry, esRuleTags, ruleTagOperations, myBatis, ruleDao, ruleTagDao, activeRuleDao); + profilesManager, ruleRegistry, esRuleTags, ruleTagOperations, myBatis, ruleDao, ruleTagDao, activeRuleDao, characteristicDao, mock(RegisterDebtCharacteristicModel.class)); setupData("shared"); task.start(); @@ -204,7 +216,7 @@ public class RuleRegistrationTest extends AbstractDaoTestCase { public void insert_extended_repositories() { task = new RuleRegistration(new RuleDefinitionsLoader(mock(RuleRepositories.class), new RuleDefinitions[]{ new FindbugsRepository(), new FbContribRepository()}), - profilesManager, ruleRegistry, esRuleTags, ruleTagOperations, myBatis, ruleDao, ruleTagDao, activeRuleDao); + profilesManager, ruleRegistry, esRuleTags, ruleTagOperations, myBatis, ruleDao, ruleTagDao, activeRuleDao, characteristicDao, mock(RegisterDebtCharacteristicModel.class)); setupData("empty"); task.start(); @@ -221,7 +233,7 @@ public class RuleRegistrationTest extends AbstractDaoTestCase { .setName("One") .setHtmlDescription("Description of One") .setSeverity(Severity.BLOCKER) - .setCharacteristicKey("COMPILER") + .setCharacteristicKey("MEMORY_EFFICIENCY") .setRemediationFunction(RemediationFunction.LINEAR_OFFSET) .setRemediationFactor("5d") .setRemediationOffset("10h") diff --git a/sonar-server/src/test/java/org/sonar/server/startup/RegisterDebtCharacteristicModelTest.java b/sonar-server/src/test/java/org/sonar/server/startup/RegisterDebtCharacteristicModelTest.java new file mode 100644 index 00000000000..0dcd74a24c6 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/startup/RegisterDebtCharacteristicModelTest.java @@ -0,0 +1,41 @@ +/* + * 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.startup; + +import org.junit.Test; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.core.technicaldebt.DebtCharacteristicsSynchronizer; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; + +public class RegisterDebtCharacteristicModelTest { + + @Test + public void create_model() throws Exception { + DebtCharacteristicsSynchronizer synchronizer = mock(DebtCharacteristicsSynchronizer.class); + RegisterDebtCharacteristicModel sqaleDefinition = new RegisterDebtCharacteristicModel(synchronizer); + + sqaleDefinition.start(); + + verify(synchronizer, times(1)).synchronize(any(ValidationMessages.class)); + } +} diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/insert_new_rules-result.xml b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/insert_new_rules-result.xml index 050c8b4377c..489e2108e85 100644 --- a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/insert_new_rules-result.xml +++ b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/insert_new_rules-result.xml @@ -1,23 +1,3 @@ -<!-- - ~ 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. - --> - <dataset> <rules id="1" plugin_rule_key="deprecated-key" plugin_name="deprecated-repo" plugin_config_key="[null]" name="Deprecated" description="[null]" @@ -30,7 +10,7 @@ <rules id="2" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="config1" name="One" description="Description of One" status="READY" priority="4" cardinality="SINGLE" parent_id="[null]" language="java" - characteristic_id="[null]" default_characteristic_id="[null]" + characteristic_id="[null]" default_characteristic_id="2" remediation_function="[null]" default_remediation_function="LINEAR_OFFSET" remediation_factor="[null]" default_remediation_factor="5d" remediation_offset="[null]" default_remediation_offset="10h" diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/remove_rule_debt_definitions_if_characteristic_not_found-result.xml b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/remove_rule_debt_definitions_if_characteristic_not_found-result.xml new file mode 100644 index 00000000000..52e53e0cf39 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/remove_rule_debt_definitions_if_characteristic_not_found-result.xml @@ -0,0 +1,20 @@ +<dataset> + + <!-- All debt parameters are reset as the characteristic MEMORY_EFFICIENCY do not exists --> + <rules id="1" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="config1" name="One" description="Description of One" + status="READY" priority="4" cardinality="SINGLE" parent_id="[null]" language="java" + characteristic_id="[null]" default_characteristic_id="[null]" + remediation_function="[null]" default_remediation_function="[null]" + remediation_factor="[null]" default_remediation_factor="[null]" + remediation_offset="[null]" default_remediation_offset="[null]" + effort_to_fix_l10n_key="[null]"/> + + <rules id="2" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="[null]" name="Two" description="Description of Two" + status="DEPRECATED" priority="0" cardinality="SINGLE" parent_id="[null]" language="java" + characteristic_id="[null]" default_characteristic_id="[null]" + remediation_function="[null]" default_remediation_function="[null]" + remediation_factor="[null]" default_remediation_factor="[null]" + remediation_offset="[null]" default_remediation_offset="[null]" + effort_to_fix_l10n_key="[null]"/> + +</dataset> diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/remove_rule_debt_definitions_if_characteristic_not_found.xml b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/remove_rule_debt_definitions_if_characteristic_not_found.xml new file mode 100644 index 00000000000..f926443c2de --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/remove_rule_debt_definitions_if_characteristic_not_found.xml @@ -0,0 +1,17 @@ +<dataset> + + <characteristics id="999" kee="NEW" name="New" root_id="1" characteristic_order="1" enabled="[true]"/> + + <rules id="1" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="old_config_key" name="old name" description="old description" + status="READY" priority="2" cardinality="SINGLE" parent_id="[null]" + characteristic_id="[null]" default_characteristic_id="200" + remediation_function="[null]" default_remediation_function="LINEAR" + remediation_factor="[null]" default_remediation_factor="14min" + remediation_offset="[null]" default_remediation_offset="[null]" + effort_to_fix_l10n_key="[null]"/> + + <rules id="2" plugin_rule_key="rule2" plugin_name="fake" plugin_config_key="old_config_key2" name="old name2" description="old description2" + status="READY" priority="1" cardinality="SINGLE" parent_id="[null]"/> + + +</dataset> diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/shared.xml b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/shared.xml index 84192cc4810..17f652e7255 100644 --- a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/shared.xml +++ b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/shared.xml @@ -1,5 +1,7 @@ <dataset> + <characteristics id="2" kee="MEMORY_EFFICIENCY" name="Memory Efficiency" root_id="1" characteristic_order="1" enabled="[true]"/> + <rules id="1" plugin_rule_key="deprecated-key" plugin_name="deprecated-repo" plugin_config_key="[null]" name="Deprecated" description="[null]" status="READY" priority="4" cardinality="SINGLE" parent_id="[null]"/> diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/update_rule_fields.xml b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/update_rule_fields.xml index 416104e8287..cfa18f014e5 100644 --- a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/update_rule_fields.xml +++ b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/update_rule_fields.xml @@ -1,5 +1,7 @@ <dataset> + <characteristics id="200" kee="MEMORY_EFFICIENCY" name="Memory Efficiency" root_id="1" characteristic_order="1" enabled="[true]"/> + <rules id="1" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="old_config_key" name="old name" description="old description" status="READY" priority="2" cardinality="SINGLE" parent_id="[null]" characteristic_id="[null]" default_characteristic_id="200" diff --git a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/update_template_rule_language.xml b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/update_template_rule_language.xml index b5a6352ede7..0ea5e06aa17 100644 --- a/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/update_template_rule_language.xml +++ b/sonar-server/src/test/resources/org/sonar/server/rule/RuleRegistrationTest/update_template_rule_language.xml @@ -1,25 +1,7 @@ -<!-- - ~ 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. - --> - <dataset> + <characteristics id="100" kee="MEMORY_EFFICIENCY" name="Memory Efficiency" root_id="1" characteristic_order="1" enabled="[true]"/> + <rules id="1" plugin_rule_key="rule1" plugin_name="fake" plugin_config_key="[null]" name="Rule one" description="[null]" status="READY" priority="4" cardinality="MULTIPLE" parent_id="[null]" language="[null]" characteristic_id="[null]" default_characteristic_id="100" |