import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;
-import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
import org.sonar.api.PropertyType;
import org.sonar.api.batch.*;
+import org.sonar.api.batch.rule.Rule;
+import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.batch.rule.internal.DefaultRule;
import org.sonar.api.component.ResourcePerspectives;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.issue.Issuable;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RulePriority;
import org.sonar.api.technicaldebt.batch.Characteristic;
-import org.sonar.api.technicaldebt.batch.Requirement;
import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
+import javax.annotation.Nullable;
+
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
private final ResourcePerspectives perspectives;
private final TechnicalDebtModel model;
+ private final Rules rules;
- public TechnicalDebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model) {
+ public TechnicalDebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model, Rules rules) {
this.perspectives = perspectives;
this.model = model;
+ this.rules = rules;
}
public boolean shouldExecuteOnProject(Project project) {
}
private void saveMeasures(DecoratorContext context, List<Issue> issues) {
- // group issues by requirement
- ListMultimap<Requirement, Issue> issuesByRequirement = issuesByRequirement(issues);
+ // group issues by rules
+ ListMultimap<Rule, Issue> issuesByRule = issuesByRule(issues);
double total = 0.0;
Map<Characteristic, Double> characteristicCosts = newHashMap();
- Map<Requirement, Double> requirementCosts = newHashMap();
-
- for (Requirement requirement : model.requirements()) {
- List<Issue> requirementIssues = issuesByRequirement.get(requirement);
- double value = computeTechnicalDebt(CoreMetrics.TECHNICAL_DEBT, context, requirement, requirementIssues);
-
- requirementCosts.put(requirement, value);
- total += value;
- propagateTechnicalDebtInParents(requirement.characteristic(), value, characteristicCosts);
+ Map<Rule, Double> ruleDebtCosts = newHashMap();
+
+ for (Rule rule : rules.findAll()) {
+ String characteristicKey = rule.characteristic();
+ if (characteristicKey != null) {
+ List<Issue> requirementIssues = issuesByRule.get(rule);
+ double value = computeTechnicalDebt(CoreMetrics.TECHNICAL_DEBT, context, rule, requirementIssues);
+
+ ruleDebtCosts.put(rule, value);
+ total += value;
+ Characteristic characteristic = model.characteristicByKey(characteristicKey);
+ propagateTechnicalDebtInParents(characteristic, value, characteristicCosts);
+ }
}
context.saveMeasure(CoreMetrics.TECHNICAL_DEBT, total);
saveOnCharacteristic(context, characteristicCosts);
- saveOnRequirement(context, requirementCosts);
+ saveOnRequirement(context, ruleDebtCosts);
}
private void saveOnCharacteristic(DecoratorContext context, Map<Characteristic, Double> characteristicCosts) {
}
}
- private void saveOnRequirement(DecoratorContext context, Map<Requirement, Double> requirementCosts) {
- for (Map.Entry<Requirement, Double> entry : requirementCosts.entrySet()) {
+ private void saveOnRequirement(DecoratorContext context, Map<Rule, Double> requirementCosts) {
+ for (Map.Entry<Rule, Double> entry : requirementCosts.entrySet()) {
saveTechnicalDebt(context, entry.getKey(), entry.getValue(), ResourceUtils.isEntity(context.getResource()));
}
}
}
@VisibleForTesting
- void saveTechnicalDebt(DecoratorContext context, Requirement requirement, Double value, boolean inMemory) {
+ void saveTechnicalDebt(DecoratorContext context, Rule rule, Double value, boolean inMemory) {
// we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122)
// BUT we don't want to save zero-values for non top-characteristics (see SQALE-147)
if (value > 0.0) {
- Measure measure = new Measure(CoreMetrics.TECHNICAL_DEBT);
- measure.setRequirement(requirement);
+ org.sonar.api.rules.Rule oldRule = toOldRule(rule);
+ RuleMeasure measure = new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, oldRule, oldRule.getSeverity(), null);
saveMeasure(context, measure, value, inMemory);
}
}
- private void saveMeasure(DecoratorContext context, Measure measure, Double value, boolean inMemory){
+ private void saveMeasure(DecoratorContext context, Measure measure, Double value, boolean inMemory) {
measure.setValue(value);
if (inMemory) {
measure.setPersistenceMode(PersistenceMode.MEMORY);
}
@VisibleForTesting
- ListMultimap<Requirement, Issue> issuesByRequirement(List<Issue> issues) {
- ListMultimap<Requirement, Issue> issuesByRequirement = ArrayListMultimap.create();
+ ListMultimap<Rule, Issue> issuesByRule(List<Issue> issues) {
+ ListMultimap<Rule, Issue> result = ArrayListMultimap.create();
for (Issue issue : issues) {
- String repositoryKey = issue.ruleKey().repository();
- String key = issue.ruleKey().rule();
- Requirement requirement = model.requirementsByRule(issue.ruleKey());
- if (requirement == null) {
- LoggerFactory.getLogger(getClass()).debug("No technical debt requirement for: " + repositoryKey + "/" + key);
- } else {
- issuesByRequirement.put(requirement, issue);
- }
+ RuleKey key = issue.ruleKey();
+ Rule rule = rules.find(key);
+ result.put(rule, issue);
}
- return issuesByRequirement;
+ return result;
}
- private double computeTechnicalDebt(Metric metric, DecoratorContext context, Requirement requirement, Collection<Issue> issues) {
+ private double computeTechnicalDebt(Metric metric, DecoratorContext context, Rule rule, Collection<Issue> issues) {
long debt = 0L;
if (issues != null) {
for (Issue issue : issues) {
}
}
- for (Measure measure : context.getChildrenMeasures(MeasuresFilters.requirement(metric, requirement))) {
- Requirement measureRequirement = measure.getRequirement();
- if (measureRequirement != null && measureRequirement.equals(requirement) && measure.getValue() != null) {
+ org.sonar.api.rules.Rule oldRule = toOldRule(rule);
+ for (Measure measure : context.getChildrenMeasures(MeasuresFilters.rule(metric, oldRule))) {
+ // Comparison on rule is only used for unit test, otherwise no need to do this check
+ RuleMeasure ruleMeasure = (RuleMeasure) measure;
+ if (measure != null && ruleMeasure.getRule().equals(oldRule) && measure.getValue() != null) {
debt += measure.getValue();
}
}
return debt;
}
- private void propagateTechnicalDebtInParents(Characteristic characteristic, double value, Map<Characteristic, Double> characteristicCosts) {
+ private void propagateTechnicalDebtInParents(@Nullable Characteristic characteristic, double value, Map<Characteristic, Double> characteristicCosts) {
if (characteristic != null) {
Double parentCost = characteristicCosts.get(characteristic);
if (parentCost == null) {
.build()
);
}
+
+ private org.sonar.api.rules.Rule toOldRule(Rule rule) {
+ DefaultRule defaultRule = (DefaultRule) rule;
+ org.sonar.api.rules.Rule oldRule = org.sonar.api.rules.Rule.create(rule.key().repository(), rule.key().rule());
+ oldRule.setSeverity(RulePriority.valueOf(rule.severity()));
+ oldRule.setId(defaultRule.id());
+ return oldRule;
+ }
+
}
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.rule.Rule;
+import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.batch.rule.internal.RulesBuilder;
import org.sonar.api.component.ResourcePerspectives;
import org.sonar.api.issue.Issuable;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.MeasuresFilter;
-import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.*;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.technicaldebt.batch.Characteristic;
-import org.sonar.api.technicaldebt.batch.Requirement;
import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
-import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement;
import org.sonar.api.test.IsMeasure;
import org.sonar.api.utils.Duration;
@Mock
Issuable issuable;
+ @Mock
+ ResourcePerspectives perspectives;
+
+ RuleKey ruleKey1 = RuleKey.of("repo1", "rule1");
+ RuleKey ruleKey2 = RuleKey.of("repo2", "rule2");
+ Rules rules;
+
TechnicalDebtDecorator decorator;
@Before
public void before() throws Exception {
- ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
+ RulesBuilder rulesBuilder = new RulesBuilder();
+ rulesBuilder.add(ruleKey1).setName("rule1").setCharacteristic("MEMORY_EFFICIENCY");
+ rulesBuilder.add(ruleKey2).setName("rule2").setCharacteristic("MODULARITY");
+ rules = rulesBuilder.build();
- decorator = new TechnicalDebtDecorator(perspectives, defaultTechnicalDebtModel);
+ decorator = new TechnicalDebtDecorator(perspectives, defaultTechnicalDebtModel, rules);
}
@Test
@Test
public void group_issues_by_requirement() throws Exception {
- Requirement requirement1 = mock(Requirement.class);
- Requirement requirement2 = mock(Requirement.class);
-
Issue issue1 = createIssue("rule1", "repo1");
Issue issue2 = createIssue("rule1", "repo1");
Issue issue3 = createIssue("rule2", "repo2");
- Issue issue4 = createIssue("unmatchable", "repo2");
-
- List<Issue> issues = newArrayList(issue1, issue2, issue3, issue4);
- when(defaultTechnicalDebtModel.requirementsByRule(RuleKey.of("repo1", "rule1"))).thenReturn(requirement1);
- when(defaultTechnicalDebtModel.requirementsByRule(RuleKey.of("repo2", "rule2"))).thenReturn(requirement2);
+ List<Issue> issues = newArrayList(issue1, issue2, issue3);
- ListMultimap<Requirement, Issue> result = decorator.issuesByRequirement(issues);
+ ListMultimap<Rule, Issue> result = decorator.issuesByRule(issues);
assertThat(result.keySet().size()).isEqualTo(2);
- assertThat(result.get(requirement1)).containsExactly(issue1, issue2);
- assertThat(result.get(requirement2)).containsExactly(issue3);
+ assertThat(result.get(rules.find(ruleKey1))).containsExactly(issue1, issue2);
+ assertThat(result.get(rules.find(ruleKey2))).containsExactly(issue3);
}
@Test
Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
when(issuable.issues()).thenReturn(newArrayList(issue));
- Requirement requirement = mock(Requirement.class);
- when(defaultTechnicalDebtModel.requirementsByRule(RuleKey.of("repo1", "rule1"))).thenReturn(requirement);
- doReturn(newArrayList(requirement)).when(defaultTechnicalDebtModel).requirements();
-
decorator.decorate(resource, context);
verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue());
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, null, requirement, ONE_DAY_IN_MINUTES.doubleValue())));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue())));
}
@Test
Issue issue = createIssue("rule1", "repo1").setDebt(null);
when(issuable.issues()).thenReturn(newArrayList(issue));
- Requirement requirement = mock(Requirement.class);
- when(defaultTechnicalDebtModel.requirementsByRule(RuleKey.of("repo1", "rule1"))).thenReturn(requirement);
- doReturn(newArrayList(requirement)).when(defaultTechnicalDebtModel).requirements();
-
decorator.decorate(resource, context);
verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 0.0);
Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
when(issuable.issues()).thenReturn(newArrayList(issue));
- DefaultCharacteristic parentCharacteristic = new DefaultCharacteristic().setKey("parentCharacteristic");
- DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("characteristic").setParent(parentCharacteristic);
- RuleKey ruleKey = RuleKey.of("repo1", "rule1");
- DefaultRequirement requirement = new DefaultRequirement().setCharacteristic(characteristic).setRuleKey(ruleKey);
-
- when(defaultTechnicalDebtModel.requirementsByRule(ruleKey)).thenReturn(requirement);
- doReturn(newArrayList(requirement)).when(defaultTechnicalDebtModel).requirements();
+ DefaultCharacteristic parentCharacteristic = new DefaultCharacteristic().setKey("EFFICIENCY");
+ DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(parentCharacteristic);
+ when(defaultTechnicalDebtModel.characteristicByKey("MEMORY_EFFICIENCY")).thenReturn(characteristic);
decorator.decorate(resource, context);
verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue());
verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, parentCharacteristic, ONE_DAY_IN_MINUTES.doubleValue())));
verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, characteristic, ONE_DAY_IN_MINUTES.doubleValue())));
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, requirement, ONE_DAY_IN_MINUTES.doubleValue())));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue())));
}
@Test
Issue issue4 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2));
when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
- DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("rootCharacteristic");
- DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("characteristic").setParent(rootCharacteristic);
- RuleKey ruleKey1 = RuleKey.of("repo1", "rule1");
- DefaultRequirement requirement1 = new DefaultRequirement().setRuleKey(ruleKey1).setCharacteristic(characteristic);
- RuleKey ruleKey2 = RuleKey.of("repo2", "rule2");
- DefaultRequirement requirement2 = new DefaultRequirement().setRuleKey(ruleKey2).setCharacteristic(characteristic);
-
- when(defaultTechnicalDebtModel.requirementsByRule(ruleKey1)).thenReturn(requirement1);
- when(defaultTechnicalDebtModel.requirementsByRule(ruleKey2)).thenReturn(requirement2);
- doReturn(newArrayList(requirement1, requirement2)).when(defaultTechnicalDebtModel).requirements();
+ when(defaultTechnicalDebtModel.characteristicByKey("MEMORY_EFFICIENCY")).thenReturn(
+ new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(
+ new DefaultCharacteristic().setKey("EFFICIENCY")
+ ),
+ new DefaultCharacteristic().setKey("MODULARITY").setParent(
+ new DefaultCharacteristic().setKey("REUSABILITY")
+ )
+ );
decorator.decorate(resource, context);
verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 6d * ONE_DAY_IN_MINUTES);
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, requirement1, 2d * ONE_DAY_IN_MINUTES)));
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, requirement2, 4d * ONE_DAY_IN_MINUTES)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 2d * ONE_DAY_IN_MINUTES)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 4d * ONE_DAY_IN_MINUTES)));
}
@Test
Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
- DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("rootCharacteristic");
- DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("characteristic").setParent(rootCharacteristic);
- RuleKey ruleKey1 = RuleKey.of("repo1", "rule1");
- DefaultRequirement requirement = new DefaultRequirement().setRuleKey(ruleKey1).setCharacteristic(characteristic);
+ when(defaultTechnicalDebtModel.characteristicByKey("MEMORY_EFFICIENCY")).thenReturn(
+ new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(
+ new DefaultCharacteristic().setKey("EFFICIENCY")
+ )
+ );
- when(defaultTechnicalDebtModel.requirementsByRule(ruleKey1)).thenReturn(requirement);
- doReturn(newArrayList(requirement)).when(defaultTechnicalDebtModel).requirements();
-
- Measure measure = new Measure().setRequirement(requirement).setValue(5d * ONE_DAY_IN_MINUTES);
+ org.sonar.api.rules.Rule oldRule = org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule());
+ Measure measure = new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, oldRule, null, null).setValue(5d * ONE_DAY_IN_MINUTES);
when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(newArrayList(measure));
decorator.decorate(resource, context);
verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 7d * ONE_DAY_IN_MINUTES);
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, requirement, 7d * ONE_DAY_IN_MINUTES)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 7d * ONE_DAY_IN_MINUTES)));
}
@Test
DecoratorContext context = mock(DecoratorContext.class);
when(context.getResource()).thenReturn(new Project("foo"));
- DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("rootCharacteristic");
- DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("characteristic").setParent(rootCharacteristic);
+ DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("EFFICIENCY");
+ DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(rootCharacteristic);
decorator.saveTechnicalDebt(context, characteristic, 0.0, true);
verify(context, never()).saveMeasure(any(Measure.class));
class IsCharacteristicMeasure extends ArgumentMatcher<Measure> {
Metric metric = null;
Characteristic characteristic = null;
- Requirement requirement = null;
Double value = null;
- public IsCharacteristicMeasure(Metric metric, Characteristic characteristic, Requirement requirement, Double value) {
- this.metric = metric;
- this.characteristic = characteristic;
- this.requirement = requirement;
- this.value = value;
- }
-
public IsCharacteristicMeasure(Metric metric, Characteristic characteristic, Double value) {
this.metric = metric;
this.characteristic = characteristic;
- this.requirement = null;
- this.value = value;
- }
-
- public IsCharacteristicMeasure(Metric metric, Requirement requirement, Double value) {
- this.metric = metric;
- this.characteristic = null;
- this.requirement = requirement;
this.value = value;
}
public IsCharacteristicMeasure(Metric metric, Double value) {
this.metric = metric;
this.characteristic = null;
- this.requirement = null;
this.value = value;
}
Measure m = (Measure) o;
return ObjectUtils.equals(metric, m.getMetric()) &&
ObjectUtils.equals(characteristic, m.getCharacteristic()) &&
- ObjectUtils.equals(requirement, m.getRequirement()) &&
+ ObjectUtils.equals(value, m.getValue());
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE));
+ }
+ }
+
+ class IsRuleMeasure extends ArgumentMatcher<RuleMeasure> {
+ Metric metric = null;
+ RuleKey ruleKey = null;
+ Double value = null;
+
+ public IsRuleMeasure(Metric metric, RuleKey ruleKey, Double value) {
+ this.metric = metric;
+ this.ruleKey = ruleKey;
+ this.value = value;
+ }
+
+ @Override
+ public boolean matches(Object o) {
+ if (!(o instanceof RuleMeasure)) {
+ return false;
+ }
+ RuleMeasure m = (RuleMeasure) o;
+ return ObjectUtils.equals(metric, m.getMetric()) &&
+ ObjectUtils.equals(ruleKey.repository(), m.getRule().getRepositoryKey()) &&
+ ObjectUtils.equals(ruleKey.rule(), m.getRule().getKey()) &&
ObjectUtils.equals(value, m.getValue());
}
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;
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);
}
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());
measure.setVariation5(model.getVariationValue5());
measure.setUrl(model.getUrl());
measure.setCharacteristic(characteristic);
- measure.setRequirement(requirement);
measure.setPersonId(model.getPersonId());
return measure;
}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.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;
- }
-
-}
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 {
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);
+ }
+ }
+ }
}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.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);
- }
-
-}
*/
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;
*/
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) {
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;
}
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;
}
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.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);
+ }
+}
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;
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() {
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.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.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 {
-
- @Mock
- CharacteristicDao dao;
-
- @Mock
- RuleFinder ruleFinder;
-
- DebtModelLoader loader;
-
- @Before
- public void before() {
- loader = new DebtModelLoader(dao, ruleFinder);
- }
-
- @Test
- public void find_all() throws Exception {
- CharacteristicDto rootCharacteristicDto = new CharacteristicDto()
- .setId(1)
- .setKey("MEMORY_EFFICIENCY")
- .setName("Memory use");
-
- CharacteristicDto characteristicDto = new CharacteristicDto()
- .setId(2)
- .setKey("EFFICIENCY")
- .setName("Efficiency")
- .setParentId(1);
-
- CharacteristicDto requirementDto = new CharacteristicDto()
- .setId(3)
- .setParentId(2)
- .setRuleId(100)
- .setFunction("linear")
- .setFactorValue(2d)
- .setFactorUnit(CharacteristicDto.DAYS)
- .setOffsetValue(0d)
- .setOffsetUnit(CharacteristicDto.MINUTES);
-
- 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));
-
- DefaultTechnicalDebtModel result = (DefaultTechnicalDebtModel) loader.load();
- 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");
-
- DefaultCharacteristic characteristic = result.characteristicByKey("EFFICIENCY");
- assertThat(characteristic.key()).isEqualTo("EFFICIENCY");
- 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);
- }
-
-}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.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.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+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 static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DebtModelProviderTest {
+
+ @Mock
+ CharacteristicDao dao;
+
+ DebtModelProvider provider;
+
+
+ @Before
+ public void before() {
+ provider = new DebtModelProvider();
+ }
+
+ @Test
+ public void find_all() throws Exception {
+ CharacteristicDto rootCharacteristicDto = new CharacteristicDto()
+ .setId(1)
+ .setKey("MEMORY_EFFICIENCY")
+ .setName("Memory use");
+
+ CharacteristicDto characteristicDto = new CharacteristicDto()
+ .setId(2)
+ .setKey("EFFICIENCY")
+ .setName("Efficiency")
+ .setParentId(1);
+
+ CharacteristicDto requirementDto = new CharacteristicDto()
+ .setId(3)
+ .setParentId(2)
+ .setRuleId(100)
+ .setFunction("linear")
+ .setFactorValue(2d)
+ .setFactorUnit(CharacteristicDto.DAYS)
+ .setOffsetValue(0d)
+ .setOffsetUnit(CharacteristicDto.MINUTES);
+
+ RuleKey ruleKey = RuleKey.of("checkstyle", "Regexp");
+ Rule rule = Rule.create(ruleKey.repository(), ruleKey.rule());
+ rule.setId(100);
+ when(dao.selectCharacteristics()).thenReturn(newArrayList(rootCharacteristicDto, characteristicDto, requirementDto));
+
+ 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.children()).hasSize(1);
+ assertThat(rootCharacteristic.children().get(0).key()).isEqualTo("EFFICIENCY");
+
+ DefaultCharacteristic characteristic = result.characteristicByKey("EFFICIENCY");
+ assertThat(characteristic.key()).isEqualTo("EFFICIENCY");
+ assertThat(characteristic.name()).isEqualTo("Efficiency");
+ assertThat(characteristic.parent().key()).isEqualTo("MEMORY_EFFICIENCY");
+ assertThat(characteristic.children()).isEmpty();
+ }
+
+}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.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.");
- }
- }
-
-}
-
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2013 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.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);
- }
-}
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;
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.*;
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;
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 {
@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 {
@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);
@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);
@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);
}
@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);
@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);
@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);
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")
}
@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);
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);
}
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.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'");
+ }
+ }
+}
--- /dev/null
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<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>
--- /dev/null
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<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>
--- /dev/null
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<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>
--- /dev/null
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<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>
--- /dev/null
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<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>
--- /dev/null
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<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>
import org.sonar.core.persistence.MyBatis;
import javax.annotation.CheckForNull;
+
import java.util.Collection;
import java.util.List;
}
}
+ public List<RuleDto> selectEnablesAndNonManual() {
+ SqlSession session = mybatis.openSession();
+ try {
+ return getMapper(session).selectEnablesAndNonManual();
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
public List<RuleDto> selectNonManual(SqlSession session) {
return getMapper(session).selectNonManual();
}
public interface RuleMapper {
List<RuleDto> selectAll();
+ List<RuleDto> selectEnablesAndNonManual();
+
List<RuleDto> selectNonManual();
RuleDto selectById(Integer id);
return null;
}
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
@CheckForNull
public Characteristic findRequirementByRuleId(int ruleId) {
CharacteristicDto requirementDto = dao.selectByRuleId(ruleId);
return null;
}
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
@CheckForNull
public Characteristic findRequirementByRule(Rule rule) {
CharacteristicDto requirementDto = dao.selectByRuleId(rule.getId());
select <include refid="selectColumns"/> from rules
</select>
+ <select id="selectEnablesAndNonManual" resultType="Rule">
+ select <include refid="selectColumns"/> from rules
+ <where>
+ and status != 'REMOVED'
+ and plugin_name != 'manual'
+ </where>
+ </select>
+
<select id="selectById" parameterType="Integer" resultType="Rule">
select <include refid="selectColumns"/> from rules WHERE id=#{id}
</select>
assertThat(ruleDto.getEffortToFixL10nKey()).isEqualTo("squid.S115.effortToFix");
}
+ @Test
+ public void select_enables_and_non_manual() throws Exception {
+ setupData("select_enables_and_non_manual");
+ List<RuleDto> ruleDtos = dao.selectEnablesAndNonManual();
+
+ assertThat(ruleDtos.size()).isEqualTo(1);
+ RuleDto ruleDto = ruleDtos.get(0);
+ assertThat(ruleDto.getId()).isEqualTo(1);
+ assertThat(ruleDto.getName()).isEqualTo("Avoid Null");
+ assertThat(ruleDto.getDescription()).isEqualTo("Should avoid NULL");
+ assertThat(ruleDto.getStatus()).isEqualTo(Rule.STATUS_READY);
+ assertThat(ruleDto.getRepositoryKey()).isEqualTo("checkstyle");
+ assertThat(ruleDto.getNoteData()).isEqualTo("Rule note with accents \u00e9\u00e8\u00e0");
+ assertThat(ruleDto.getCharacteristicId()).isEqualTo(100);
+ assertThat(ruleDto.getDefaultCharacteristicId()).isEqualTo(101);
+ assertThat(ruleDto.getRemediationFunction()).isEqualTo("LINEAR");
+ assertThat(ruleDto.getDefaultRemediationFunction()).isEqualTo("LINEAR_OFFSET");
+ assertThat(ruleDto.getRemediationFactor()).isEqualTo("1h");
+ assertThat(ruleDto.getDefaultRemediationFactor()).isEqualTo("5d");
+ assertThat(ruleDto.getRemediationOffset()).isEqualTo("5min");
+ assertThat(ruleDto.getDefaultRemediationOffset()).isEqualTo("10h");
+ assertThat(ruleDto.getEffortToFixL10nKey()).isEqualTo("squid.S115.effortToFix");
+ }
+
@Test
public void select_by_id() throws Exception {
setupData("selectById");
import org.junit.Before;
import org.junit.Test;
-import org.sonar.api.rule.RuleKey;
import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
-import org.sonar.api.technicaldebt.batch.internal.DefaultRequirement;
-import org.sonar.api.utils.internal.WorkDuration;
import static org.fest.assertions.Assertions.assertThat;
assertThat(sqaleModel.characteristicByKey("UNKNOWN")).isNull();
}
- @Test
- public void get_requirement_by_rule_key() throws Exception {
- DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic()
- .setKey("MEMORY_EFFICIENCY")
- .setName("Memory use");
-
- DefaultCharacteristic characteristic = new DefaultCharacteristic()
- .setKey("EFFICIENCY")
- .setName("Efficiency")
- .setParent(rootCharacteristic);
-
- RuleKey ruleKey = RuleKey.of("checkstyle", "Regexp");
- DefaultRequirement requirement = new DefaultRequirement()
- .setCharacteristic(characteristic)
- .setRuleKey(ruleKey)
- .setFunction("linear")
- .setFactorValue(2)
- .setFactorUnit(WorkDuration.UNIT.HOURS)
- .setOffsetValue(0)
- .setOffsetUnit(WorkDuration.UNIT.HOURS);
-
- sqaleModel.addRootCharacteristic(rootCharacteristic);
-
- assertThat(sqaleModel.requirementsByRule(ruleKey)).isEqualTo(requirement);
- assertThat(sqaleModel.requirementsByRule(RuleKey.of("not", "found"))).isNull();
- }
}
--- /dev/null
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2013 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<dataset>
+
+ <rules id="1" plugin_rule_key="AvoidNull" plugin_name="checkstyle" name="Avoid Null" description="Should avoid NULL" status="READY"
+ note_data="Rule note with accents éèà " note_user_login="polop.palap" note_created_at="2013-12-25"
+ characteristic_id="100" default_characteristic_id="101"
+ remediation_function="LINEAR" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="1h" default_remediation_factor="5d"
+ remediation_offset="5min" default_remediation_offset="10h"
+ effort_to_fix_l10n_key="squid.S115.effortToFix"/>
+
+ <rules id="2" plugin_rule_key="AvoidNull" plugin_name="squid" name="Avoid Null" description="Should avoid NULL" status="REMOVED"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_factor="[null]" default_remediation_factor="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_l10n_key="[null]"/>
+
+ <rules id="3" plugin_rule_key="AvoidNull" plugin_name="manual" name="Manual Rule" description="Should not appear" status="READY"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_factor="[null]" default_remediation_factor="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_l10n_key="[null]"/>
+
+</dataset>
package org.sonar.api.batch.rule;
import com.google.common.annotations.Beta;
+import org.sonar.api.rule.RemediationFunction;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.utils.Duration;
import javax.annotation.CheckForNull;
+
import java.util.Collection;
/**
- * Not used
* @since 4.2
*/
@Beta
RuleStatus status();
+ /**
+ * Characteristic key.
+ *
+ * @since 4.3
+ */
+ @CheckForNull
+ String characteristic();
+
+ /**
+ * Remediation function : one of LINEAR, LINEAR_OFFSET or CONSTANT_ISSUE.
+ *
+ * @since 4.3
+ */
+ @CheckForNull
+ RemediationFunction function();
+
+ /**
+ * Remediation factor duration (used for LINEAR function).
+ *
+ * @since 4.3
+ */
+ @CheckForNull
+ Duration factor();
+
+ /**
+ * Remediation offset duration (used for LINEAR_OFFSET or CONSTANT_ISSUE function).
+ *
+ * @since 4.3
+ */
+ @CheckForNull
+ Duration offset();
+
}
import com.google.common.annotations.Beta;
/**
- * Not used
* @since 4.2
*/
@Beta
import org.sonar.api.rule.RuleKey;
import javax.annotation.CheckForNull;
+
import java.util.Collection;
/**
- * Not used yet
* @since 4.2
*/
@Beta
import com.google.common.collect.ImmutableMap;
import org.sonar.api.batch.rule.Rule;
import org.sonar.api.batch.rule.RuleParam;
+import org.sonar.api.rule.RemediationFunction;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.utils.Duration;
import javax.annotation.CheckForNull;
import javax.annotation.concurrent.Immutable;
+
import java.util.Collection;
import java.util.Map;
private final RuleKey key;
private final Integer id;
- private final String name, severity, description, metadata;
+ private final String name, severity, description, metadata, characteristic;
private final RuleStatus status;
+ RemediationFunction function;
+ Duration factor, offset;
+
private final Map<String, RuleParam> params;
DefaultRule(NewRule newRule) {
this.description = newRule.description;
this.metadata = newRule.metadata;
this.status = newRule.status;
+ this.characteristic = newRule.characteristic;
+ this.function = newRule.function;
+ this.factor = newRule.factor;
+ this.offset = newRule.offset;
ImmutableMap.Builder<String, RuleParam> builder = ImmutableMap.builder();
for (NewRuleParam newRuleParam : newRule.params.values()) {
return status;
}
+ @Override
+ public String characteristic() {
+ return characteristic;
+ }
+
+ @Override
+ public RemediationFunction function() {
+ return function;
+ }
+
+ @Override
+ public Duration factor() {
+ return factor;
+ }
+
+ @Override
+ public Duration offset() {
+ return offset;
+ }
+
@Override
public RuleParam param(String paramKey) {
return params.get(paramKey);
import org.sonar.api.rule.RuleKey;
import javax.annotation.concurrent.Immutable;
+
import java.util.Collection;
import java.util.List;
rulesByRepository = builder.build();
}
-
@Override
public Rule find(RuleKey ruleKey) {
List<Rule> rules = rulesByRepository.get(ruleKey.repository());
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
+import org.sonar.api.rule.RemediationFunction;
import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rule.Severity;
import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.Duration;
import javax.annotation.Nullable;
+
import java.util.HashMap;
import java.util.Map;
final RuleKey key;
Integer id;
- String name, description, severity = DEFAULT_SEVERITY, metadata;
+ String name, description, severity = DEFAULT_SEVERITY, metadata, characteristic;
+ RemediationFunction function;
+ Duration factor, offset;
RuleStatus status = RuleStatus.defaultStatus();
Map<String, NewRuleParam> params = new HashMap<String, NewRuleParam>();
return this;
}
+ public NewRule setCharacteristic(@Nullable String c) {
+ this.characteristic = c;
+ return this;
+ }
+
+ public NewRule setFunction(@Nullable RemediationFunction f) {
+ this.function = f;
+ return this;
+ }
+
+ public NewRule setFactor(@Nullable Duration f) {
+ this.factor = f;
+ return this;
+ }
+
+ public NewRule setOffset(@Nullable Duration o) {
+ this.offset = o;
+ return this;
+ }
+
public NewRuleParam addParam(String paramKey) {
if (params.containsKey(paramKey)) {
throw new IllegalStateException(String.format("Parameter '%s' already exists on rule '%s'", paramKey, key));
package org.sonar.api.rule;
/**
+ * List of availables remediation function
+ *
* @since 4.3
*/
public enum RemediationFunction {
List<? extends Characteristic> children();
- List<? extends Requirement> requirements();
-
boolean isRoot();
Date createdAt();
/**
* @since 4.1
+ * @deprecated since 4.3
*/
+@Deprecated
public interface Requirement {
Integer id();
@CheckForNull
Characteristic characteristicByKey(String key);
+ /**
+ * @deprecated since 4.3
+ */
@CheckForNull
+ @Deprecated
Requirement requirementsByRule(RuleKey ruleKey);
+ /**
+ * @deprecated since 4.3
+ */
@CheckForNull
+ @Deprecated
Requirement requirementsById(Integer id);
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
List<? extends Requirement> requirements();
}
import java.util.Date;
+/**
+ * @deprecated since 4.3
+ */
+@Deprecated
public class DefaultRequirement implements Requirement {
public static final String FUNCTION_LINEAR = "linear";
boolean isRoot();
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
boolean isRequirement();
}
List<Characteristic> findRootCharacteristics();
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
Characteristic findRequirementByRule(Rule rule);
Characteristic findCharacteristicById(Integer id);
return this;
}
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
@CheckForNull
public Integer factorValue() {
return factorValue;
}
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
public DefaultCharacteristic setFactorValue(@Nullable Integer factorValue) {
this.factorValue = factorValue;
return this;
return factorUnit;
}
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
public DefaultCharacteristic setFactorUnit(@Nullable WorkDuration.UNIT factorUnit) {
this.factorUnit = factorUnit;
return this;
return this;
}
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
@CheckForNull
public Integer offsetValue() {
return offsetValue;
}
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
public DefaultCharacteristic setOffsetValue(@Nullable Integer offsetValue) {
this.offsetValue = offsetValue;
return this;
}
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
@CheckForNull
public WorkDuration.UNIT offsetUnit() {
return offsetUnit;
}
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
public DefaultCharacteristic setOffsetUnit(@Nullable WorkDuration.UNIT offsetUnit) {
this.offsetUnit = offsetUnit;
return this;
}
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
public static WorkDuration.UNIT toUnit(@Nullable String requirementUnit) {
if (requirementUnit != null) {
if (WorkUnit.DAYS.equals(requirementUnit)) {
return parentId == null;
}
+ /**
+ * @deprecated since 4.3
+ */
+ @Deprecated
public boolean isRequirement() {
return ruleKey != null;
}
return durationInMinutes;
}
- public boolean isGreaterThan(Duration other){
+ public boolean isGreaterThan(Duration other) {
return toMinutes() > other.toMinutes();
}
+ public Duration add(Duration with) {
+ return Duration.create(durationInMinutes + with.durationInMinutes);
+ }
+
+ public Duration subtract(Duration with) {
+ return Duration.create(durationInMinutes - with.durationInMinutes);
+ }
+
+ public Duration multiply(int factor) {
+ return Duration.create(durationInMinutes * factor);
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
assertThat(Duration.decode("1d", 10).isGreaterThan(Duration.decode("1d", 8))).isTrue();
}
+ @Test
+ public void add() throws Exception {
+ assertThat(Duration.decode("1h", HOURS_IN_DAY).add(Duration.decode("1min", HOURS_IN_DAY))).isEqualTo(Duration.decode("1h1min", HOURS_IN_DAY));
+ }
+
+ @Test
+ public void subtract() throws Exception {
+ assertThat(Duration.decode("1h", HOURS_IN_DAY).subtract(Duration.decode("1min", HOURS_IN_DAY))).isEqualTo(Duration.decode("59min", HOURS_IN_DAY));
+ }
+
+ @Test
+ public void multiply() throws Exception {
+ assertThat(Duration.decode("1h", HOURS_IN_DAY).multiply(2)).isEqualTo(Duration.decode("2h", HOURS_IN_DAY));
+ }
+
@Test
public void test_equals_and_hashcode() throws Exception {
Duration duration = Duration.create(ONE_DAY_IN_MINUTES + ONE_HOUR_IN_MINUTES + ONE_MINUTE);