diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-03-11 08:09:01 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-03-11 08:09:01 +0100 |
commit | dce23fcf9ac907f6621ab0c11c397febff280ebc (patch) | |
tree | f819d4a7216d35389a3063b0f0a96cde49cb62db /sonar-batch/src/test/java/org | |
parent | 1ffa320fb3e44cdaf635678a04c473ca4f348514 (diff) | |
download | sonarqube-dce23fcf9ac907f6621ab0c11c397febff280ebc.tar.gz sonarqube-dce23fcf9ac907f6621ab0c11c397febff280ebc.zip |
SONAR-5056 Read debt from rule during analysis
Diffstat (limited to 'sonar-batch/src/test/java/org')
-rw-r--r-- | sonar-batch/src/test/java/org/sonar/batch/debt/DebtModelProviderTest.java (renamed from sonar-batch/src/test/java/org/sonar/batch/debt/DebtModelLoaderTest.java) | 29 | ||||
-rw-r--r-- | sonar-batch/src/test/java/org/sonar/batch/debt/RuleDebtCalculatorTest.java | 145 | ||||
-rw-r--r-- | sonar-batch/src/test/java/org/sonar/batch/debt/TechnicalDebtModelProviderTest.java | 60 | ||||
-rw-r--r-- | sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java | 200 | ||||
-rw-r--r-- | sonar-batch/src/test/java/org/sonar/batch/rule/RulesProviderTest.java | 170 |
5 files changed, 309 insertions, 295 deletions
diff --git a/sonar-batch/src/test/java/org/sonar/batch/debt/DebtModelLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/debt/DebtModelProviderTest.java index 2de08c8f77a..50e08fc793c 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/debt/DebtModelLoaderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/debt/DebtModelProviderTest.java @@ -27,34 +27,27 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; 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 org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; -import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement; -import org.sonar.api.utils.internal.WorkDuration; import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel; import org.sonar.core.technicaldebt.db.CharacteristicDao; import org.sonar.core.technicaldebt.db.CharacteristicDto; import static com.google.common.collect.Lists.newArrayList; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Matchers.any; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) -public class DebtModelLoaderTest { +public class DebtModelProviderTest { @Mock CharacteristicDao dao; - @Mock - RuleFinder ruleFinder; + DebtModelProvider provider; - DebtModelLoader loader; @Before public void before() { - loader = new DebtModelLoader(dao, ruleFinder); + provider = new DebtModelProvider(); } @Test @@ -83,17 +76,15 @@ public class DebtModelLoaderTest { RuleKey ruleKey = RuleKey.of("checkstyle", "Regexp"); Rule rule = Rule.create(ruleKey.repository(), ruleKey.rule()); rule.setId(100); - when(ruleFinder.findAll(any(RuleQuery.class))).thenReturn(newArrayList(rule)); - when(dao.selectEnabledCharacteristics()).thenReturn(newArrayList(rootCharacteristicDto, characteristicDto, requirementDto)); + when(dao.selectCharacteristics()).thenReturn(newArrayList(rootCharacteristicDto, characteristicDto, requirementDto)); - DefaultTechnicalDebtModel result = (DefaultTechnicalDebtModel) loader.load(); + DefaultTechnicalDebtModel result = (DefaultTechnicalDebtModel) provider.provide(dao); assertThat(result.rootCharacteristics()).hasSize(1); DefaultCharacteristic rootCharacteristic = result.characteristicByKey("MEMORY_EFFICIENCY"); assertThat(rootCharacteristic.key()).isEqualTo("MEMORY_EFFICIENCY"); assertThat(rootCharacteristic.name()).isEqualTo("Memory use"); assertThat(rootCharacteristic.parent()).isNull(); - assertThat(rootCharacteristic.requirements()).isEmpty(); assertThat(rootCharacteristic.children()).hasSize(1); assertThat(rootCharacteristic.children().get(0).key()).isEqualTo("EFFICIENCY"); @@ -102,16 +93,6 @@ public class DebtModelLoaderTest { assertThat(characteristic.name()).isEqualTo("Efficiency"); assertThat(characteristic.parent().key()).isEqualTo("MEMORY_EFFICIENCY"); assertThat(characteristic.children()).isEmpty(); - assertThat(characteristic.requirements()).hasSize(1); - assertThat(characteristic.requirements().get(0).ruleKey()).isEqualTo(ruleKey); - - DefaultRequirement requirement = result.requirementsByRule(ruleKey); - assertThat(requirement.ruleKey()).isEqualTo(ruleKey); - assertThat(requirement.function()).isEqualTo("linear"); - assertThat(requirement.factorValue()).isEqualTo(2); - assertThat(requirement.factorUnit()).isEqualTo(WorkDuration.UNIT.DAYS); - assertThat(requirement.offsetValue()).isEqualTo(0); - assertThat(requirement.offsetUnit()).isEqualTo(WorkDuration.UNIT.MINUTES); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/debt/RuleDebtCalculatorTest.java b/sonar-batch/src/test/java/org/sonar/batch/debt/RuleDebtCalculatorTest.java deleted file mode 100644 index c53cdcdc2d5..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/debt/RuleDebtCalculatorTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.batch.debt; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import org.sonar.api.CoreProperties; -import org.sonar.api.config.Settings; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; -import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement; -import org.sonar.api.utils.internal.WorkDuration; - -import static org.fest.assertions.Assertions.assertThat; -import static org.fest.assertions.Fail.fail; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class RuleDebtCalculatorTest { - - private static final int HOURS_IN_DAY = 8; - - @Mock - TechnicalDebtModel model; - - RuleDebtCalculator calculator; - - @Before - public void before() { - calculator = new RuleDebtCalculator(model, new Settings().setProperty(CoreProperties.HOURS_IN_DAY, 8)); - } - - @Test - public void calculate_technical_debt() throws Exception { - RuleKey ruleKey = RuleKey.of("squid", "AvoidCycle"); - DefaultIssue issue = new DefaultIssue().setKey("ABCDE").setRuleKey(ruleKey); - - DefaultRequirement requirement = new DefaultRequirement() - .setFunction("constant_issue") - .setFactorValue(10) - .setFactorUnit(WorkDuration.UNIT.MINUTES) - .setOffsetValue(5) - .setOffsetUnit(WorkDuration.UNIT.MINUTES); - when(model.requirementsByRule(ruleKey)).thenReturn(requirement); - - assertThat(calculator.calculateTechnicalDebt(issue.ruleKey(), issue.effortToFix())).isEqualTo(15L); - } - - @Test - public void calculate_technical_debt_with_effort_to_fix() throws Exception { - RuleKey ruleKey = RuleKey.of("squid", "AvoidCycle"); - DefaultIssue issue = new DefaultIssue().setKey("ABCDE").setRuleKey(ruleKey).setEffortToFix(2d); - - DefaultRequirement requirement = new DefaultRequirement() - .setFunction("linear_offset") - .setFactorValue(10) - .setFactorUnit(WorkDuration.UNIT.MINUTES) - .setOffsetValue(5) - .setOffsetUnit(WorkDuration.UNIT.MINUTES); - when(model.requirementsByRule(ruleKey)).thenReturn(requirement); - - assertThat(calculator.calculateTechnicalDebt(issue.ruleKey(), issue.effortToFix())).isEqualTo(((10 * 2) + 5)); - } - - @Test - public void calculate_technical_debt_with_no_offset() throws Exception { - RuleKey ruleKey = RuleKey.of("squid", "AvoidCycle"); - DefaultIssue issue = new DefaultIssue().setKey("ABCDE").setRuleKey(ruleKey).setEffortToFix(2d); - - DefaultRequirement requirement = new DefaultRequirement() - .setFunction("linear") - .setFactorValue(10) - .setFactorUnit(WorkDuration.UNIT.HOURS); - when(model.requirementsByRule(ruleKey)).thenReturn(requirement); - - assertThat(calculator.calculateTechnicalDebt(issue.ruleKey(), issue.effortToFix())).isEqualTo((10 * 2) * 60); - } - - @Test - public void calculate_technical_debt_with_no_factor() throws Exception { - RuleKey ruleKey = RuleKey.of("squid", "AvoidCycle"); - DefaultIssue issue = new DefaultIssue().setKey("ABCDE").setRuleKey(ruleKey); - - DefaultRequirement requirement = new DefaultRequirement() - .setFunction("constant_issue") - .setOffsetValue(5) - .setOffsetUnit(WorkDuration.UNIT.DAYS); - - when(model.requirementsByRule(ruleKey)).thenReturn(requirement); - - assertThat(calculator.calculateTechnicalDebt(issue.ruleKey(), issue.effortToFix())).isEqualTo(5 * HOURS_IN_DAY * 60); - } - - @Test - public void no_technical_debt_if_requirement_not_found() throws Exception { - RuleKey ruleKey = RuleKey.of("squid", "AvoidCycle"); - DefaultIssue issue = new DefaultIssue().setKey("ABCDE").setRuleKey(ruleKey); - when(model.requirementsByRule(ruleKey)).thenReturn(null); - - assertThat(calculator.calculateTechnicalDebt(issue.ruleKey(), issue.effortToFix())).isNull(); - } - - @Test - public void fail_to_calculate_technical_debt_on_constant_issue_function_with_effort_to_fix() throws Exception { - RuleKey ruleKey = RuleKey.of("squid", "AvoidCycle"); - DefaultIssue issue = new DefaultIssue().setKey("ABCDE").setRuleKey(ruleKey).setEffortToFix(2d); - - DefaultRequirement requirement = new DefaultRequirement() - .setFunction("constant_issue") - .setOffsetValue(5) - .setOffsetUnit(WorkDuration.UNIT.MINUTES); - when(model.requirementsByRule(ruleKey)).thenReturn(requirement); - - try { - assertThat(calculator.calculateTechnicalDebt(issue.ruleKey(), issue.effortToFix())).isEqualTo(15); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(IllegalArgumentException.class) - .hasMessage("Requirement for 'squid:AvoidCycle' can not use 'Constant/issue' remediation function because this rule does not have a fixed remediation cost."); - } - } - -} - diff --git a/sonar-batch/src/test/java/org/sonar/batch/debt/TechnicalDebtModelProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/debt/TechnicalDebtModelProviderTest.java deleted file mode 100644 index 05747f800e5..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/debt/TechnicalDebtModelProviderTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.batch.debt; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; - -import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.*; - -@RunWith(MockitoJUnitRunner.class) -public class TechnicalDebtModelProviderTest { - - @Mock - DebtModelLoader loader; - - @Test - public void load_model() { - TechnicalDebtModel model = mock(TechnicalDebtModel.class); - when(loader.load()).thenReturn(model); - - DebtModelProvider provider = new DebtModelProvider(); - TechnicalDebtModel result = provider.provide(loader); - assertThat(result).isNotNull(); - } - - @Test - public void load_model_only_once() { - TechnicalDebtModel model = mock(TechnicalDebtModel.class); - when(loader.load()).thenReturn(model); - - DebtModelProvider provider = new DebtModelProvider(); - provider.provide(loader); - verify(loader).load(); - - provider.provide(loader); - verifyZeroInteractions(loader); - } -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java index 1150211081e..b8c4d1efbf1 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java @@ -26,17 +26,19 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.batch.rule.internal.RulesBuilder; import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.profiles.RulesProfile; import org.sonar.api.resources.JavaFile; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; +import org.sonar.api.rule.RemediationFunction; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; -import org.sonar.api.rules.*; +import org.sonar.api.rules.RulePriority; +import org.sonar.api.rules.Violation; import org.sonar.api.utils.Duration; import org.sonar.api.utils.MessageException; -import org.sonar.batch.debt.RuleDebtCalculator; import java.util.Calendar; import java.util.Date; @@ -44,7 +46,6 @@ import java.util.Date; import static org.fest.assertions.Assertions.assertThat; import static org.fest.assertions.Fail.fail; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; @@ -52,25 +53,19 @@ import static org.mockito.Mockito.*; public class ModuleIssuesTest { static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle"); - static final Rule SQUID_RULE = Rule.create("squid", "AvoidCycle").setName("Avoid Cycle"); + static final String SQUID_RULE_NAME = "Avoid Cycle"; @Mock IssueCache cache; @Mock - RulesProfile qProfile; - - @Mock Project project; @Mock IssueFilters filters; - @Mock - RuleDebtCalculator technicalDebtCalculator; - - @Mock - RuleFinder ruleFinder; + ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); + RulesBuilder ruleBuilder = new RulesBuilder(); ModuleIssues moduleIssues; @@ -78,13 +73,11 @@ public class ModuleIssuesTest { public void setUp() { when(project.getAnalysisDate()).thenReturn(new Date()); when(project.getEffectiveKey()).thenReturn("org.apache:struts-core"); - - moduleIssues = new ModuleIssues(qProfile, cache, project, filters, technicalDebtCalculator, ruleFinder); } @Test public void fail_on_unknown_rule() throws Exception { - when(ruleFinder.findByKey(SQUID_RULE_KEY)).thenReturn(null); + initModuleIssues(); DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY); try { @@ -99,7 +92,8 @@ public class ModuleIssuesTest { @Test public void fail_if_rule_has_no_name_and_issue_has_no_message() throws Exception { - when(ruleFinder.findByKey(SQUID_RULE_KEY)).thenReturn(Rule.create("squid", "AvoidCycle")); + ruleBuilder.add(RuleKey.of("squid", "AvoidCycle")); + initModuleIssues(); DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY).setMessage(""); try { @@ -114,8 +108,8 @@ public class ModuleIssuesTest { @Test public void ignore_null_active_rule() throws Exception { - when(qProfile.getActiveRule(anyString(), anyString())).thenReturn(null); - when(ruleFinder.findByKey(SQUID_RULE_KEY)).thenReturn(SQUID_RULE); + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + initModuleIssues(); DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY); boolean added = moduleIssues.initAndAddIssue(issue); @@ -126,10 +120,9 @@ public class ModuleIssuesTest { @Test public void ignore_null_rule_of_active_rule() throws Exception { - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(null); - when(qProfile.getActiveRule(anyString(), anyString())).thenReturn(activeRule); - when(ruleFinder.findByKey(SQUID_RULE_KEY)).thenReturn(SQUID_RULE); + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + activeRulesBuilder.activate(SQUID_RULE_KEY); + initModuleIssues(); DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY); boolean added = moduleIssues.initAndAddIssue(issue); @@ -140,12 +133,9 @@ public class ModuleIssuesTest { @Test public void add_issue_to_cache() throws Exception { - Rule rule = SQUID_RULE; - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(rule); - when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); - when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); - when(ruleFinder.findByKey(SQUID_RULE_KEY)).thenReturn(rule); + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); + initModuleIssues(); Date analysisDate = new Date(); when(project.getAnalysisDate()).thenReturn(analysisDate); @@ -166,13 +156,10 @@ public class ModuleIssuesTest { } @Test - public void use_severity_from_active_rule_if_no_severity() throws Exception { - Rule rule = SQUID_RULE; - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(rule); - when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); - when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); - when(ruleFinder.findByKey(SQUID_RULE_KEY)).thenReturn(rule); + public void use_severity_from_active_rule_if_no_severity_on_issue() throws Exception { + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); + initModuleIssues(); Date analysisDate = new Date(); when(project.getAnalysisDate()).thenReturn(analysisDate); @@ -189,12 +176,9 @@ public class ModuleIssuesTest { @Test public void use_rule_name_if_no_message() throws Exception { - Rule rule = SQUID_RULE; - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(rule); - when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); - when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); - when(ruleFinder.findByKey(SQUID_RULE_KEY)).thenReturn(rule); + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); + initModuleIssues(); Date analysisDate = new Date(); when(project.getAnalysisDate()).thenReturn(analysisDate); @@ -216,18 +200,17 @@ public class ModuleIssuesTest { @Test public void add_deprecated_violation() throws Exception { - Rule rule = SQUID_RULE; + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); + initModuleIssues(); + + org.sonar.api.rules.Rule rule = org.sonar.api.rules.Rule.create("squid", "AvoidCycle", "Avoid Cycle"); Resource resource = new JavaFile("org.struts.Action").setEffectiveKey("struts:org.struts.Action"); Violation violation = new Violation(rule, resource); violation.setLineId(42); violation.setSeverity(RulePriority.CRITICAL); violation.setMessage("the message"); - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(rule); - when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); - when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); - when(ruleFinder.findByKey(SQUID_RULE_KEY)).thenReturn(rule); when(filters.accept(any(DefaultIssue.class), eq(violation))).thenReturn(true); boolean added = moduleIssues.initAndAddViolation(violation); @@ -241,17 +224,14 @@ public class ModuleIssuesTest { assertThat(issue.message()).isEqualTo("the message"); assertThat(issue.key()).isNotEmpty(); assertThat(issue.ruleKey().toString()).isEqualTo("squid:AvoidCycle"); - assertThat(issue.componentKey().toString()).isEqualTo("struts:org.struts.Action"); + assertThat(issue.componentKey()).isEqualTo("struts:org.struts.Action"); } @Test public void filter_issue() throws Exception { - Rule rule = SQUID_RULE; - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(rule); - when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); - when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); - when(ruleFinder.findByKey(SQUID_RULE_KEY)).thenReturn(rule); + ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); + activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); + initModuleIssues(); DefaultIssue issue = new DefaultIssue() .setKey("ABCDE") @@ -267,13 +247,14 @@ public class ModuleIssuesTest { } @Test - public void set_remediation_cost() throws Exception { - Rule rule = SQUID_RULE; - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(rule); - when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); - when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); - when(ruleFinder.findByKey(SQUID_RULE_KEY)).thenReturn(rule); + public void set_debt_with_linear_function() throws Exception { + ruleBuilder.add(SQUID_RULE_KEY) + .setName(SQUID_RULE_NAME) + .setCharacteristic("COMPILER_RELATED_PORTABILITY") + .setFunction(RemediationFunction.LINEAR) + .setFactor(Duration.create(10L)); + activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); + initModuleIssues(); Date analysisDate = new Date(); when(project.getAnalysisDate()).thenReturn(analysisDate); @@ -281,17 +262,104 @@ public class ModuleIssuesTest { DefaultIssue issue = new DefaultIssue() .setKey("ABCDE") .setRuleKey(SQUID_RULE_KEY) - .setSeverity(Severity.CRITICAL); + .setSeverity(Severity.CRITICAL) + .setEffortToFix(2d); + + when(filters.accept(issue, null)).thenReturn(true); + moduleIssues.initAndAddIssue(issue); + + ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); + verify(cache).put(argument.capture()); + assertThat(argument.getValue().debt()).isEqualTo(Duration.create(20L)); + } + + @Test + public void set_debt_with_linear_with_offset_function() throws Exception { + ruleBuilder.add(SQUID_RULE_KEY) + .setName(SQUID_RULE_NAME) + .setCharacteristic("COMPILER_RELATED_PORTABILITY") + .setFunction(RemediationFunction.LINEAR_OFFSET) + .setFactor(Duration.create(10L)) + .setOffset(Duration.create(25L)); + activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); + initModuleIssues(); + + Date analysisDate = new Date(); + when(project.getAnalysisDate()).thenReturn(analysisDate); + + DefaultIssue issue = new DefaultIssue() + .setKey("ABCDE") + .setRuleKey(SQUID_RULE_KEY) + .setSeverity(Severity.CRITICAL) + .setEffortToFix(2d); - Long debt = 10L; - when(technicalDebtCalculator.calculateTechnicalDebt(issue.ruleKey(), issue.effortToFix())).thenReturn(debt); when(filters.accept(issue, null)).thenReturn(true); + moduleIssues.initAndAddIssue(issue); + + ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); + verify(cache).put(argument.capture()); + assertThat(argument.getValue().debt()).isEqualTo(Duration.create(45L)); + } + + @Test + public void set_debt_with_constant_issue_function() throws Exception { + ruleBuilder.add(SQUID_RULE_KEY) + .setName(SQUID_RULE_NAME) + .setCharacteristic("COMPILER_RELATED_PORTABILITY") + .setFunction(RemediationFunction.CONSTANT_ISSUE) + .setOffset(Duration.create(10L)); + activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); + initModuleIssues(); + + Date analysisDate = new Date(); + when(project.getAnalysisDate()).thenReturn(analysisDate); + DefaultIssue issue = new DefaultIssue() + .setKey("ABCDE") + .setRuleKey(SQUID_RULE_KEY) + .setSeverity(Severity.CRITICAL) + .setEffortToFix(null); + + when(filters.accept(issue, null)).thenReturn(true); moduleIssues.initAndAddIssue(issue); ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); verify(cache).put(argument.capture()); - assertThat(argument.getValue().debt()).isEqualTo(Duration.create(debt)); + assertThat(argument.getValue().debt()).isEqualTo(Duration.create(10L)); + } + + @Test + public void fail_to_set_debt_with_constant_issue_function_when_effort_to_fix_is_set() throws Exception { + ruleBuilder.add(SQUID_RULE_KEY) + .setName(SQUID_RULE_NAME) + .setCharacteristic("COMPILER_RELATED_PORTABILITY") + .setFunction(RemediationFunction.CONSTANT_ISSUE) + .setOffset(Duration.create(25L)); + activeRulesBuilder.activate(SQUID_RULE_KEY).setSeverity(Severity.INFO); + initModuleIssues(); + + DefaultIssue issue = new DefaultIssue() + .setKey("ABCDE") + .setRuleKey(SQUID_RULE_KEY) + .setSeverity(Severity.CRITICAL) + .setEffortToFix(2d); + + when(filters.accept(issue, null)).thenReturn(true); + + try { + moduleIssues.initAndAddIssue(issue); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Rule 'squid:AvoidCycle' can not use 'Constant/issue' remediation function because this rule does not have a fixed remediation cost."); + } + } + + /** + * Every rules and active rules has to be added in builders before creating ModuleIssues + */ + private void initModuleIssues() { + moduleIssues = new ModuleIssues(activeRulesBuilder.build(), ruleBuilder.build(), cache, project, filters); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProviderTest.java new file mode 100644 index 00000000000..dc5d9b9e6e9 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProviderTest.java @@ -0,0 +1,170 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.batch.rule; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.batch.rule.Rule; +import org.sonar.api.batch.rule.RuleParam; +import org.sonar.api.batch.rule.Rules; +import org.sonar.api.config.Settings; +import org.sonar.api.rule.RemediationFunction; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; +import org.sonar.api.utils.Duration; +import org.sonar.api.utils.Durations; +import org.sonar.core.persistence.AbstractDaoTestCase; +import org.sonar.core.rule.RuleDao; +import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel; + +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; + +@RunWith(MockitoJUnitRunner.class) +public class RulesProviderTest extends AbstractDaoTestCase { + + @Mock + Durations durations; + + RuleDao ruleDao; + + DefaultTechnicalDebtModel debtModel; + + RulesProvider provider; + + @Before + public void setUp() throws Exception { + debtModel = new DefaultTechnicalDebtModel(); + debtModel.addRootCharacteristic(new DefaultCharacteristic() + .setId(101) + .setKey("EFFICIENCY") + .setName("Efficiency") + .setParent(new DefaultCharacteristic() + .setId(100) + .setKey("MEMORY_EFFICIENCY") + .setName("Memory use"))); + debtModel.addRootCharacteristic(new DefaultCharacteristic() + .setId(103) + .setKey("PORTABILITY") + .setName("Portability") + .setParent(new DefaultCharacteristic() + .setId(102) + .setKey("COMPILER_RELATED_PORTABILITY") + .setName("Compiler"))); + + durations = new Durations(new Settings().setProperty("sonar.technicalDebt.hoursInDay", 8), null); + ruleDao = new RuleDao(getMyBatis()); + + provider = new RulesProvider(); + } + + @Test + public void build_rules() throws Exception { + setupData("shared"); + + Rules rules = provider.provide(ruleDao, debtModel, durations); + + assertThat(rules.findAll()).hasSize(1); + assertThat(rules.findByRepository("checkstyle")).hasSize(1); + assertThat(rules.findByRepository("unknown")).isEmpty(); + + Rule rule = rules.find(RuleKey.of("checkstyle", "AvoidNull")); + assertThat(rule).isNotNull(); + assertThat(rule.key()).isEqualTo(RuleKey.of("checkstyle", "AvoidNull")); + assertThat(rule.name()).isEqualTo("Avoid Null"); + assertThat(rule.description()).isEqualTo("Should avoid NULL"); + assertThat(rule.severity()).isEqualTo(Severity.MINOR); + assertThat(rule.metadata()).isNull(); + assertThat(rule.params()).hasSize(1); + + RuleParam param = rule.param("myParameter"); + assertThat(param).isNotNull(); + assertThat(param.description()).isEqualTo("My Parameter"); + } + + @Test + public void build_rules_with_default_debt_definitions() throws Exception { + setupData("build_rules_with_default_debt_definitions"); + + Rules rules = provider.provide(ruleDao, debtModel, durations); + + Rule rule = rules.find(RuleKey.of("checkstyle", "AvoidNull")); + assertThat(rule.characteristic()).isEqualTo("EFFICIENCY"); + assertThat(rule.function()).isEqualTo(RemediationFunction.LINEAR_OFFSET); + assertThat(rule.factor()).isEqualTo(Duration.decode("5d", 8)); + assertThat(rule.offset()).isEqualTo(Duration.decode("10h", 8)); + } + + @Test + public void build_rules_with_user_debt_definitions() throws Exception { + setupData("build_rules_with_user_debt_definitions"); + + Rules rules = provider.provide(ruleDao, debtModel, durations); + + Rule rule = rules.find(RuleKey.of("checkstyle", "AvoidNull")); + assertThat(rule.characteristic()).isEqualTo("PORTABILITY"); + assertThat(rule.function()).isEqualTo(RemediationFunction.LINEAR); + assertThat(rule.factor()).isEqualTo(Duration.decode("2h", 8)); + assertThat(rule.offset()).isNull(); + } + + @Test + public void build_rules_with_default_and_user_debt_definitions() throws Exception { + setupData("build_rules_with_default_and_user_debt_definitions"); + + Rules rules = provider.provide(ruleDao, debtModel, durations); + + // As both default columns and user columns on debt are set, user debt columns should be used + Rule rule = rules.find(RuleKey.of("checkstyle", "AvoidNull")); + assertThat(rule.characteristic()).isEqualTo("PORTABILITY"); + assertThat(rule.function()).isEqualTo(RemediationFunction.LINEAR); + assertThat(rule.factor()).isEqualTo(Duration.decode("2h", 8)); + assertThat(rule.offset()).isNull(); + } + + @Test + public void fail_if_characteristic_not_found() throws Exception { + setupData("fail_if_characteristic_not_found"); + + try { + provider.provide(ruleDao, debtModel, durations); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("Characteristic id '999' on rule 'checkstyle:AvoidNull' has not been found"); + } + } + + @Test + public void fail_if_no_function() throws Exception { + setupData("fail_if_no_function"); + + try { + provider.provide(ruleDao, debtModel, durations); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("Remediation function should not be null on rule 'checkstyle:AvoidNull'"); + } + } +} |