--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.core.technicaldebt;
+
+import org.apache.commons.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;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.core.technicaldebt;
+
+import org.apache.commons.io.IOUtils;
+import org.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);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.core.technicaldebt;
+
+import com.google.common.base.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;
+ }
+ }
+
+}
* @return the list of plugin keys
*/
public Collection<String> getContributingPluginList() {
- Collection<String> contributingPlugins = newArrayList(contributingPluginKeyToClassLoader.keySet());
- return contributingPlugins;
+ return newArrayList(contributingPluginKeyToClassLoader.keySet());
}
/**
import org.sonar.core.persistence.MyBatis;
import javax.annotation.CheckForNull;
+
import java.util.List;
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
*/
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.core.technicaldebt;
+
+import com.google.common.base.Charsets;
+import com.google.common.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);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.core.technicaldebt;
+
+import com.google.common.collect.Lists;
+import org.apache.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));
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.core.technicaldebt;
+
+import com.google.common.base.Charsets;
+import com.google.common.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);
+ }
+ }
+
+}
--- /dev/null
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<sqale>
+ <chc>
+ <key>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>
--- /dev/null
+<sqale>
+ <chc>
+ <key>PORTABILITY</key>
+ <name>Portability</name>
+ <chc>
+ <key>COMPILER_RELATED_PORTABILITY</key>
+ <name>Compiler related portability</name>
+ </chc>
+ <chc>
+ <key>HARDWARE_RELATED_PORTABILITY</key>
+ <name>Hardware related portability</name>
+ </chc>
+ </chc>
+ <chc>
+ <key>MAINTAINABILITY</key>
+ <name>Maintainability</name>
+ <chc>
+ <key>READABILITY</key>
+ <name>Readability</name>
+ </chc>
+ </chc>
+</sqale>
--- /dev/null
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<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>
--- /dev/null
+<sqale>
+ <chc>
+ <key>USABILITY</key>
+ <name>Usability</name>
+ <desc>Estimate usability</desc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+ <chc>
+ <key>MEMORY_EFFICIENCY</key>
+ <name>Memory use</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>Regexp</rule-key>
+ <prop>
+ <key>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>
--- /dev/null
+<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>
--- /dev/null
+<sqale>
+ <chc>
+ <key>USABILITY</key>
+ <name>Usability</name>
+ <desc>Estimate usability</desc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+ <chc>
+ <key>MEMORY_EFFICIENCY</key>
+ <name>Memory use</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>Regexp</rule-key>
+ <prop>
+ <key>factor</key>
+ <val>abc</val>
+ </prop>
+ <prop>
+ <key>function</key>
+ <txt>linear</txt>
+ </prop>
+ </chc>
+ </chc>
+ </chc>
+
+</sqale>
--- /dev/null
+<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>
--- /dev/null
+<sqale>
+ <chc>
+ <key>USABILITY
+ </key>
+ <name>Usability
+ </name>
+ <desc>Estimate usability
+ </desc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY
+ </key>
+ <name>Efficiency
+ </name>
+ <chc>
+ <key>MEMORY_EFFICIENCY
+ </key>
+ <name>Memory use
+ </name>
+ <chc>
+ <rule-repo>checkstyle
+ </rule-repo>
+ <rule-key>Regexp
+ </rule-key>
+ <prop>
+ <key>remediationFactor
+ </key>
+ <val>3.0
+ </val>
+ <txt>h
+ </txt>
+ </prop>
+ <prop>
+ <key>remediationFunction
+ </key>
+ <txt>linear
+ </txt>
+ </prop>
+ </chc>
+ </chc>
+ </chc>
+
+</sqale>
--- /dev/null
+<sqale>
+ <chc>
+ <key>USABILITY</key>
+ <name>Usability</name>
+ <desc>Estimate usability</desc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+ <chc>
+ <key>MEMORY_EFFICIENCY</key>
+ <name>Memory use</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>Regexp</rule-key>
+ <prop>
+ <key>offset</key>
+ <val>3.0</val>
+ <txt>d</txt>
+ </prop>
+ <prop>
+ <key>remediationFunction</key>
+ <txt>constant_issue</txt>
+ </prop>
+ </chc>
+ </chc>
+ </chc>
+
+</sqale>
--- /dev/null
+<sqale>
+ <chc>
+ <key>USABILITY</key>
+ <name>Usability</name>
+ <desc>Estimate usability</desc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+ <chc>
+ <key>MEMORY_EFFICIENCY</key>
+ <name>Memory use</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>Regexp</rule-key>
+ <prop>
+ <key>remediationFactor</key>
+ <val>3.0</val>
+ <txt>h</txt>
+ </prop>
+ <prop>
+ <key>remediationFunction</key>
+ <txt>linear</txt>
+ </prop>
+ </chc>
+ </chc>
+ </chc>
+
+</sqale>
--- /dev/null
+<sqale>
+ <chc>
+ <key>USABILITY</key>
+ <name>Usability</name>
+ <desc>Estimate usability</desc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+ <chc>
+ <key>MEMORY_EFFICIENCY</key>
+ <name>Memory use</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>Regexp</rule-key>
+ <prop>
+ <key>remediationFactor</key>
+ <val>3.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>
--- /dev/null
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<sqale>
+ <chc>
+ <key>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>
--- /dev/null
+<sqale>
+ <chc>
+ <key>USABILITY</key>
+ <name>Usability</name>
+ <desc>Estimate usability</desc>
+ </chc>
+ <chc>
+ <key>EFFICIENCY</key>
+ <name>Efficiency</name>
+ <chc>
+ <key>MEMORY_EFFICIENCY</key>
+ <name>Memory use</name>
+ <chc>
+ <rule-repo>checkstyle</rule-repo>
+ <rule-key>Regexp</rule-key>
+ <prop>
+ <key>remediationFactor</key>
+ <val>3.0</val>
+ </prop>
+ <prop>
+ <key>remediationFunction</key>
+ <txt>linear</txt>
+ </prop>
+ <prop>
+ <key>offset</key>
+ <val>1.0</val>
+ </prop>
+ </chc>
+ </chc>
+ </chc>
+
+</sqale>
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;
// 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
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);
*/
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;
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());
);
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);
+ }
+
}
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;
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;
* @since 4.2
*/
public class RuleRegistration implements Startable {
+
private static final Logger LOG = LoggerFactory.getLogger(RuleRegistration.class);
private final RuleDefinitionsLoader defLoader;
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;
this.ruleDao = ruleDao;
this.ruleTagDao = ruleTagDao;
this.activeRuleDao = activeRuleDao;
+ this.characteristicDao = characteristicDao;
}
@Override
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);
}
}
- 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++;
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())
.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);
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);
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());
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;
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;
+ }
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.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();
+ }
+
+}
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");
}
}
+ 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");
@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");
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");
+ }
+
}
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.*;
RuleDao ruleDao;
RuleTagDao ruleTagDao;
ActiveRuleDao activeRuleDao;
+ CharacteristicDao characteristicDao;
@Before
public void before() {
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
checkTables("update_rule_parameters", EXCLUDED_COLUMN_NAMES_INCLUDING_DEBT, "rules", "rules_parameters");
}
+ @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");
@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();
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();
.setName("One")
.setHtmlDescription("Description of One")
.setSeverity(Severity.BLOCKER)
- .setCharacteristicKey("COMPILER")
+ .setCharacteristicKey("MEMORY_EFFICIENCY")
.setRemediationFunction(RemediationFunction.LINEAR_OFFSET)
.setRemediationFactor("5d")
.setRemediationOffset("10h")
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.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));
+ }
+}
-<!--
- ~ 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]"
<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"
--- /dev/null
+<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>
--- /dev/null
+<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>
<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]"/>
<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"
-<!--
- ~ 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"