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