aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-batch
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2014-03-11 08:09:01 +0100
committerJulien Lancelot <julien.lancelot@sonarsource.com>2014-03-11 08:09:01 +0100
commitdce23fcf9ac907f6621ab0c11c397febff280ebc (patch)
treef819d4a7216d35389a3063b0f0a96cde49cb62db /sonar-batch
parent1ffa320fb3e44cdaf635678a04c473ca4f348514 (diff)
downloadsonarqube-dce23fcf9ac907f6621ab0c11c397febff280ebc.tar.gz
sonarqube-dce23fcf9ac907f6621ab0c11c397febff280ebc.zip
SONAR-5056 Read debt from rule during analysis
Diffstat (limited to 'sonar-batch')
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java7
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelLoader.java103
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java43
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/debt/RuleDebtCalculator.java95
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java64
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java120
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java17
-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.java145
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/debt/TechnicalDebtModelProviderTest.java60
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java200
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/rule/RulesProviderTest.java170
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_default_and_user_debt_definitions.xml29
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_default_debt_definitions.xml29
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_user_debt_definitions.xml29
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/fail_if_characteristic_not_found.xml29
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/fail_if_no_function.xml29
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/shared.xml31
18 files changed, 699 insertions, 530 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java
index 88ce12563d1..6e6f000d511 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java
@@ -32,7 +32,6 @@ import org.sonar.api.measures.MetricFinder;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Resource;
import org.sonar.api.technicaldebt.batch.Characteristic;
-import org.sonar.api.technicaldebt.batch.Requirement;
import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
import org.sonar.batch.index.DefaultIndex;
@@ -65,8 +64,7 @@ public class DefaultTimeMachine implements TimeMachine {
MeasureModel model = (MeasureModel) object[0];
Integer characteristicId = model.getCharacteristicId();
Characteristic characteristic = techDebtModel.characteristicById(characteristicId);
- Requirement requirement = techDebtModel.requirementsById(characteristicId);
- Measure measure = toMeasure(model, metricById.get(model.getMetricId()), characteristic != null ? characteristic : null, requirement != null ? requirement : null);
+ Measure measure = toMeasure(model, metricById.get(model.getMetricId()), characteristic);
measure.setDate((Date) object[1]);
result.add(measure);
}
@@ -154,7 +152,7 @@ public class DefaultTimeMachine implements TimeMachine {
return result;
}
- static Measure toMeasure(MeasureModel model, Metric metric, @Nullable Characteristic characteristic, @Nullable Requirement requirement) {
+ static Measure toMeasure(MeasureModel model, Metric metric, @Nullable Characteristic characteristic) {
// NOTE: measures on rule are not supported
Measure measure = new Measure(metric);
measure.setId(model.getId());
@@ -171,7 +169,6 @@ public class DefaultTimeMachine implements TimeMachine {
measure.setVariation5(model.getVariationValue5());
measure.setUrl(model.getUrl());
measure.setCharacteristic(characteristic);
- measure.setRequirement(requirement);
measure.setPersonId(model.getPersonId());
return measure;
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelLoader.java b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelLoader.java
deleted file mode 100644
index 1cbf38d6835..00000000000
--- a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelLoader.java
+++ /dev/null
@@ -1,103 +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.sonar.api.BatchComponent;
-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.TechnicalDebtModel;
-import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
-import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel;
-import org.sonar.core.technicaldebt.db.CharacteristicDao;
-import org.sonar.core.technicaldebt.db.CharacteristicDto;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import static com.google.common.collect.Maps.newHashMap;
-
-public class DebtModelLoader implements BatchComponent {
-
- private final CharacteristicDao dao;
- private final RuleFinder ruleFinder;
-
- public DebtModelLoader(CharacteristicDao dao, RuleFinder ruleFinder) {
- this.dao = dao;
- this.ruleFinder = ruleFinder;
- }
-
- public TechnicalDebtModel load() {
- DefaultTechnicalDebtModel model = new DefaultTechnicalDebtModel();
- List<CharacteristicDto> dtos = dao.selectEnabledCharacteristics();
- Map<Integer, DefaultCharacteristic> characteristicsById = newHashMap();
-
- addRootCharacteristics(model, dtos, characteristicsById);
- addCharacteristics(dtos, characteristicsById);
- addRequirements(dtos, characteristicsById);
- return model;
- }
-
- private void addRootCharacteristics(DefaultTechnicalDebtModel model, List<CharacteristicDto> dtos, Map<Integer, DefaultCharacteristic> characteristicsById) {
- for (CharacteristicDto dto : dtos) {
- if (dto.getParentId() == null) {
- DefaultCharacteristic rootCharacteristic = dto.toCharacteristic(null);
- model.addRootCharacteristic(rootCharacteristic);
- characteristicsById.put(dto.getId(), rootCharacteristic);
- }
- }
- }
-
- private void addCharacteristics(List<CharacteristicDto> dtos, Map<Integer, DefaultCharacteristic> characteristicsById) {
- for (CharacteristicDto dto : dtos) {
- if (dto.getParentId() != null && dto.getRuleId() == null) {
- DefaultCharacteristic parent = characteristicsById.get(dto.getParentId());
- DefaultCharacteristic characteristic = dto.toCharacteristic(parent);
- characteristicsById.put(dto.getId(), characteristic);
- }
- }
- }
-
- private void addRequirements(List<CharacteristicDto> dtos, Map<Integer, DefaultCharacteristic> characteristicsById) {
- Map<Integer, Rule> rulesById = rulesById(ruleFinder.findAll(RuleQuery.create()));
- for (CharacteristicDto dto : dtos) {
- Integer ruleId = dto.getRuleId();
- if (ruleId != null) {
- DefaultCharacteristic characteristic = characteristicsById.get(dto.getParentId());
- DefaultCharacteristic rootCharacteristic = characteristicsById.get(dto.getRootId());
- Rule rule = rulesById.get(ruleId);
- RuleKey ruleKey = RuleKey.of(rule.getRepositoryKey(), rule.getKey());
- dto.toRequirement(ruleKey, characteristic, rootCharacteristic);
- }
- }
- }
-
- private Map<Integer, Rule> rulesById(Collection<Rule> rules) {
- Map<Integer, Rule> rulesById = newHashMap();
- for (Rule rule : rules) {
- rulesById.put(rule.getId(), rule);
- }
- return rulesById;
- }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java
index fafeade5489..237bfa3d93b 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtModelProvider.java
@@ -24,7 +24,16 @@ import org.picocontainer.injectors.ProviderAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
+import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
import org.sonar.api.utils.TimeProfiler;
+import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel;
+import org.sonar.core.technicaldebt.db.CharacteristicDao;
+import org.sonar.core.technicaldebt.db.CharacteristicDto;
+
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.collect.Maps.newHashMap;
public class DebtModelProvider extends ProviderAdapter {
@@ -32,12 +41,42 @@ public class DebtModelProvider extends ProviderAdapter {
private TechnicalDebtModel model;
- public TechnicalDebtModel provide(DebtModelLoader loader) {
+ public TechnicalDebtModel provide(CharacteristicDao dao) {
if (model == null) {
TimeProfiler profiler = new TimeProfiler(LOG).start("Loading technical debt model");
- model = loader.load();
+ model = load(dao);
profiler.stop();
}
return model;
}
+
+ private TechnicalDebtModel load(CharacteristicDao dao) {
+ DefaultTechnicalDebtModel model = new DefaultTechnicalDebtModel();
+ List<CharacteristicDto> dtos = dao.selectCharacteristics();
+ Map<Integer, DefaultCharacteristic> characteristicsById = newHashMap();
+
+ addRootCharacteristics(model, dtos, characteristicsById);
+ addCharacteristics(dtos, characteristicsById);
+ return model;
+ }
+
+ private void addRootCharacteristics(DefaultTechnicalDebtModel model, List<CharacteristicDto> dtos, Map<Integer, DefaultCharacteristic> characteristicsById) {
+ for (CharacteristicDto dto : dtos) {
+ if (dto.getParentId() == null) {
+ DefaultCharacteristic rootCharacteristic = dto.toCharacteristic(null);
+ model.addRootCharacteristic(rootCharacteristic);
+ characteristicsById.put(dto.getId(), rootCharacteristic);
+ }
+ }
+ }
+
+ private void addCharacteristics(List<CharacteristicDto> dtos, Map<Integer, DefaultCharacteristic> characteristicsById) {
+ for (CharacteristicDto dto : dtos) {
+ if (dto.getParentId() != null && dto.getRuleId() == null) {
+ DefaultCharacteristic parent = characteristicsById.get(dto.getParentId());
+ DefaultCharacteristic characteristic = dto.toCharacteristic(parent);
+ characteristicsById.put(dto.getId(), characteristic);
+ }
+ }
+ }
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/RuleDebtCalculator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/RuleDebtCalculator.java
deleted file mode 100644
index 54798761191..00000000000
--- a/sonar-batch/src/main/java/org/sonar/batch/debt/RuleDebtCalculator.java
+++ /dev/null
@@ -1,95 +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 com.google.common.base.Objects;
-import org.sonar.api.BatchExtension;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.technicaldebt.batch.Requirement;
-import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
-import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement;
-import org.sonar.api.utils.internal.WorkDuration;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-
-/**
- * Computes the remediation cost based on the quality and analysis models.
- */
-public class RuleDebtCalculator implements BatchExtension {
-
- private final TechnicalDebtModel model;
- private final Settings settings;
-
- public RuleDebtCalculator(TechnicalDebtModel model, Settings settings) {
- this.model = model;
- this.settings = settings;
- }
-
- /**
- * Calculate the technical debt from a requirement
- */
- @CheckForNull
- public Long calculateTechnicalDebt(RuleKey ruleKey, @Nullable Double effortToFix) {
- Requirement requirement = model.requirementsByRule(ruleKey);
- if (requirement != null) {
- if (requirement.function().equals(DefaultRequirement.CONSTANT_ISSUE) && effortToFix != null) {
- throw new IllegalArgumentException("Requirement for '" + ruleKey + "' can not use 'Constant/issue' remediation function " +
- "because this rule does not have a fixed remediation cost.");
- }
- return calculateTechnicalDebt(requirement, effortToFix);
- }
- return null;
- }
-
- private long calculateTechnicalDebt(Requirement requirement, @Nullable Double effortToFix) {
- long result = 0L;
-
- int factorValue = requirement.factorValue();
- if (factorValue > 0) {
- int effortToFixValue = Objects.firstNonNull(effortToFix, 1).intValue();
- result = convertValueAndUnitToMinutes(factorValue, requirement.factorUnit()) * effortToFixValue;
- }
-
- int offsetValue = requirement.offsetValue();
- if (offsetValue > 0) {
- result += convertValueAndUnitToMinutes(offsetValue, requirement.offsetUnit());
- }
- return result;
- }
-
- private long convertValueAndUnitToMinutes(int value, WorkDuration.UNIT unit){
- if (WorkDuration.UNIT.DAYS.equals(unit)) {
- return 60L * value * hoursInDay();
- } else if (WorkDuration.UNIT.HOURS.equals(unit)) {
- return 60L * value;
- } else if (WorkDuration.UNIT.MINUTES.equals(unit)) {
- return value;
- }
- throw new IllegalStateException("Invalid unit : " + unit);
- }
-
- private int hoursInDay(){
- return settings.getInt(CoreProperties.HOURS_IN_DAY);
- }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
index d19ad5de7ad..b204cd6605c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
@@ -19,18 +19,19 @@
*/
package org.sonar.batch.issue;
+import com.google.common.base.Objects;
import com.google.common.base.Strings;
+import org.sonar.api.batch.rule.ActiveRule;
+import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.rule.Rule;
+import org.sonar.api.batch.rule.Rules;
import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.profiles.RulesProfile;
import org.sonar.api.resources.Project;
+import org.sonar.api.rule.RemediationFunction;
import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.ActiveRule;
-import org.sonar.api.rules.Rule;
-import org.sonar.api.rules.RuleFinder;
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 org.sonar.core.issue.DefaultIssueBuilder;
import javax.annotation.Nullable;
@@ -40,20 +41,18 @@ import javax.annotation.Nullable;
*/
public class ModuleIssues {
- private final RulesProfile qProfile;
+ private final ActiveRules activeRules;
+ private final Rules rules;
private final IssueCache cache;
private final Project project;
private final IssueFilters filters;
- private final RuleDebtCalculator technicalDebtCalculator;
- private final RuleFinder ruleFinder;
- public ModuleIssues(RulesProfile qProfile, IssueCache cache, Project project, IssueFilters filters, RuleDebtCalculator technicalDebtCalculator, RuleFinder ruleFinder) {
- this.qProfile = qProfile;
+ public ModuleIssues(ActiveRules activeRules, Rules rules, IssueCache cache, Project project, IssueFilters filters) {
+ this.activeRules = activeRules;
+ this.rules = rules;
this.cache = cache;
this.project = project;
this.filters = filters;
- this.technicalDebtCalculator = technicalDebtCalculator;
- this.ruleFinder = ruleFinder;
}
public boolean initAndAddIssue(DefaultIssue issue) {
@@ -78,10 +77,10 @@ public class ModuleIssues {
private boolean initAndAddIssue(DefaultIssue issue, @Nullable Violation violation) {
RuleKey ruleKey = issue.ruleKey();
- Rule rule = ruleFinder.findByKey(ruleKey);
+ Rule rule = rules.find(ruleKey);
validateRule(issue, rule);
- ActiveRule activeRule = qProfile.getActiveRule(ruleKey.repository(), ruleKey.rule());
- if (activeRule == null || activeRule.getRule() == null) {
+ ActiveRule activeRule = activeRules.find(ruleKey);
+ if (activeRule == null) {
// rule does not exist or is not enabled -> ignore the issue
return false;
}
@@ -93,27 +92,48 @@ public class ModuleIssues {
return false;
}
- private void validateRule(DefaultIssue issue, Rule rule){
+ private void validateRule(DefaultIssue issue, Rule rule) {
RuleKey ruleKey = issue.ruleKey();
if (rule == null) {
throw MessageException.of(String.format("The rule '%s' does not exist.", ruleKey));
}
- if (Strings.isNullOrEmpty(rule.getName()) && Strings.isNullOrEmpty(issue.message())) {
+ if (Strings.isNullOrEmpty(rule.name()) && Strings.isNullOrEmpty(issue.message())) {
throw MessageException.of(String.format("The rule '%s' has no name and the related issue has no message.", ruleKey));
}
}
- private void updateIssue(DefaultIssue issue, Rule rule, ActiveRule activeRule ){
+ private void updateIssue(DefaultIssue issue, Rule rule, ActiveRule activeRule) {
if (Strings.isNullOrEmpty(issue.message())) {
- issue.setMessage(rule.getName());
+ issue.setMessage(rule.name());
}
issue.setCreationDate(project.getAnalysisDate());
issue.setUpdateDate(project.getAnalysisDate());
if (issue.severity() == null) {
- issue.setSeverity(activeRule.getSeverity().name());
+ issue.setSeverity(activeRule.severity());
}
- Long debt = technicalDebtCalculator.calculateTechnicalDebt(issue.ruleKey(), issue.effortToFix());
- issue.setDebt(debt != null ? Duration.create(debt) : null);
+ if (rule.characteristic() != null) {
+ issue.setDebt(calculateDebt(rule, issue.effortToFix()));
+ }
+ }
+
+ private Duration calculateDebt(Rule rule, @Nullable Double effortToFix) {
+ if (RemediationFunction.CONSTANT_ISSUE.equals(rule.function()) && effortToFix != null) {
+ throw new IllegalArgumentException("Rule '" + rule.key() + "' can not use 'Constant/issue' remediation function " +
+ "because this rule does not have a fixed remediation cost.");
+ }
+ Duration result = Duration.create(0);
+ Duration factor = rule.factor();
+ Duration offset = rule.offset();
+
+ if (factor != null) {
+ int effortToFixValue = Objects.firstNonNull(effortToFix, 1).intValue();
+ result = rule.factor().multiply(effortToFixValue);
+ }
+
+ if (offset != null) {
+ result = result.add(offset);
+ }
+ return result;
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java
new file mode 100644
index 00000000000..c36b70c0e3b
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProvider.java
@@ -0,0 +1,120 @@
+/*
+ * 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 com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import org.picocontainer.injectors.ProviderAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.batch.rule.internal.NewRule;
+import org.sonar.api.batch.rule.internal.RulesBuilder;
+import org.sonar.api.rule.RemediationFunction;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.technicaldebt.batch.Characteristic;
+import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
+import org.sonar.api.utils.Durations;
+import org.sonar.api.utils.TimeProfiler;
+import org.sonar.core.rule.RuleDao;
+import org.sonar.core.rule.RuleDto;
+import org.sonar.core.rule.RuleParamDto;
+
+import javax.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Loads all enabled and non manual rules
+ */
+public class RulesProvider extends ProviderAdapter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RulesProvider.class);
+
+ private Rules singleton = null;
+
+ public Rules provide(RuleDao ruleDao, TechnicalDebtModel debtModel, Durations durations) {
+ if (singleton == null) {
+ TimeProfiler profiler = new TimeProfiler(LOG).start("Loading rules");
+ singleton = load(ruleDao, debtModel, durations);
+ profiler.stop();
+ }
+ return singleton;
+ }
+
+ private Rules load(RuleDao ruleDao, TechnicalDebtModel debtModel, Durations durations) {
+ RulesBuilder rulesBuilder = new RulesBuilder();
+
+ List<RuleParamDto> ruleParamDtos = ruleDao.selectParameters();
+ ListMultimap<Integer, RuleParamDto> paramDtosByRuleId = ArrayListMultimap.create();
+ for (RuleParamDto dto : ruleParamDtos) {
+ paramDtosByRuleId.put(dto.getRuleId(), dto);
+ }
+ for (RuleDto ruleDto : ruleDao.selectEnablesAndNonManual()) {
+ RuleKey ruleKey = RuleKey.of(ruleDto.getRepositoryKey(), ruleDto.getRuleKey());
+ NewRule newRule = rulesBuilder.add(ruleKey)
+ .setId(ruleDto.getId())
+ .setName(ruleDto.getName())
+ .setSeverity(ruleDto.getSeverityString())
+ .setDescription(ruleDto.getDescription())
+ .setStatus(RuleStatus.valueOf(ruleDto.getStatus()));
+ // TODO should we set metadata ?
+ if (ruleDto.getCharacteristicId() != null) {
+ Characteristic characteristic = characteristic(ruleDto.getCharacteristicId(), ruleKey, debtModel);
+ updateRuleDebtDefinitions(newRule, ruleKey, characteristic, ruleDto.getRemediationFunction(), ruleDto.getRemediationFactor(), ruleDto.getRemediationOffset(), durations);
+ } else if (ruleDto.getDefaultCharacteristicId() != null) {
+ Characteristic characteristic = characteristic(ruleDto.getDefaultCharacteristicId(), ruleKey, debtModel);
+ updateRuleDebtDefinitions(newRule, ruleKey, characteristic, ruleDto.getDefaultRemediationFunction(), ruleDto.getDefaultRemediationFactor(),
+ ruleDto.getDefaultRemediationOffset(), durations);
+ }
+ for (RuleParamDto ruleParamDto : paramDtosByRuleId.get(ruleDto.getId())) {
+ newRule.addParam(ruleParamDto.getName())
+ .setDescription(ruleParamDto.getDescription());
+ }
+ }
+ return rulesBuilder.build();
+ }
+
+ private void updateRuleDebtDefinitions(NewRule newRule, RuleKey ruleKey, Characteristic characteristic, @Nullable String function,
+ @Nullable String factor, @Nullable String offset,
+ Durations durations) {
+ newRule.setCharacteristic(characteristic.key());
+ newRule.setFunction(function(function, ruleKey));
+ newRule.setFactor(factor != null ? durations.decode(factor) : null);
+ newRule.setOffset(offset != null ? durations.decode(offset) : null);
+ }
+
+ private Characteristic characteristic(Integer characteristicId, RuleKey ruleKey, TechnicalDebtModel debtModel) {
+ Characteristic characteristic = debtModel.characteristicById(characteristicId);
+ if (characteristic == null) {
+ throw new IllegalStateException(String.format("Characteristic id '%s' on rule '%s' has not been found", characteristicId, ruleKey));
+ }
+ return characteristic;
+ }
+
+ private RemediationFunction function(@Nullable String function, RuleKey ruleKey) {
+ if (function == null) {
+ throw new IllegalStateException(String.format("Remediation function should not be null on rule '%s'", ruleKey));
+ }
+ return RemediationFunction.valueOf(function);
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
index 4d3f6876bdd..5e0446c8ae7 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
@@ -37,15 +37,14 @@ import org.sonar.batch.ProjectConfigurator;
import org.sonar.batch.ProjectTree;
import org.sonar.batch.bootstrap.*;
import org.sonar.batch.components.PeriodsDefinition;
-import org.sonar.batch.debt.DebtModelLoader;
import org.sonar.batch.debt.DebtModelProvider;
import org.sonar.batch.debt.IssueChangelogDebtCalculator;
-import org.sonar.batch.debt.RuleDebtCalculator;
import org.sonar.batch.index.*;
import org.sonar.batch.issue.*;
import org.sonar.batch.phases.GraphPersister;
import org.sonar.batch.profiling.PhasesSumUpTimeProfiler;
import org.sonar.batch.qualitygate.ProjectAlerts;
+import org.sonar.batch.rule.RulesProvider;
import org.sonar.batch.scan.filesystem.InputFileCache;
import org.sonar.batch.scan.maven.FakeMavenPluginExecutor;
import org.sonar.batch.scan.maven.MavenPluginExecutor;
@@ -156,17 +155,19 @@ public class ProjectScanContainer extends ComponentContainer {
SymbolizableBuilder.class,
// technical debt
- DebtModelLoader.class,
- RuleDebtCalculator.class,
new DebtModelProvider(),
+ // quality gates
+ ProjectAlerts.class,
+
+ // rules
+ new RulesProvider(),
+
// Differential periods
PeriodsDefinition.class,
- ProjectSettingsReady.class,
-
- // quality gates
- ProjectAlerts.class);
+ ProjectSettingsReady.class
+ );
}
private void fixMavenExecutor() {
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'");
+ }
+ }
+}
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_default_and_user_debt_definitions.xml b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_default_and_user_debt_definitions.xml
new file mode 100644
index 00000000000..5090b80b139
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_default_and_user_debt_definitions.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<dataset>
+
+ <rules id="1" plugin_rule_key="AvoidNull" plugin_name="checkstyle" name="Avoid Null" description="Should avoid NULL" status="READY" priority="1"
+ characteristic_id="103" default_characteristic_id="101"
+ remediation_function="LINEAR" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="2h" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="10h"/>
+
+</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_default_debt_definitions.xml b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_default_debt_definitions.xml
new file mode 100644
index 00000000000..024cc36f848
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_default_debt_definitions.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<dataset>
+
+ <rules id="1" plugin_rule_key="AvoidNull" plugin_name="checkstyle" name="Avoid Null" description="Should avoid NULL" status="READY" priority="1"
+ characteristic_id="[null]" default_characteristic_id="101"
+ remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="[null]" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="10h"/>
+
+</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_user_debt_definitions.xml b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_user_debt_definitions.xml
new file mode 100644
index 00000000000..97b8965b1a8
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/build_rules_with_user_debt_definitions.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<dataset>
+
+ <rules id="1" plugin_rule_key="AvoidNull" plugin_name="checkstyle" name="Avoid Null" description="Should avoid NULL" status="READY" priority="1"
+ characteristic_id="103" default_characteristic_id="[null]"
+ remediation_function="LINEAR" default_remediation_function="[null]"
+ remediation_factor="2h" default_remediation_factor="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"/>
+
+</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/fail_if_characteristic_not_found.xml b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/fail_if_characteristic_not_found.xml
new file mode 100644
index 00000000000..005d68415d8
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/fail_if_characteristic_not_found.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<dataset>
+
+ <rules id="1" plugin_rule_key="AvoidNull" plugin_name="checkstyle" name="Avoid Null" description="Should avoid NULL" status="READY" priority="1"
+ characteristic_id="[null]" default_characteristic_id="999"
+ remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="[null]" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="10h"/>
+
+</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/fail_if_no_function.xml b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/fail_if_no_function.xml
new file mode 100644
index 00000000000..7c069728ff9
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/fail_if_no_function.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<dataset>
+
+ <rules id="1" plugin_rule_key="AvoidNull" plugin_name="checkstyle" name="Avoid Null" description="Should avoid NULL" status="READY" priority="1"
+ characteristic_id="[null]" default_characteristic_id="101"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_factor="[null]" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="10h"/>
+
+</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/shared.xml b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/shared.xml
new file mode 100644
index 00000000000..916cabb500c
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/rule/RulesProviderTest/shared.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<dataset>
+
+ <rules id="1" plugin_rule_key="AvoidNull" plugin_name="checkstyle" name="Avoid Null" description="Should avoid NULL" status="READY" priority="1"
+ characteristic_id="[null]" default_characteristic_id="101"
+ remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="[null]" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="10h"/>
+
+ <rules_parameters id="1" rule_id="1" name="myParameter" param_type="plop" default_value="plouf" description="My Parameter"/>
+
+</dataset>