diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-03-11 08:09:01 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2014-03-11 08:09:01 +0100 |
commit | dce23fcf9ac907f6621ab0c11c397febff280ebc (patch) | |
tree | f819d4a7216d35389a3063b0f0a96cde49cb62db /plugins | |
parent | 1ffa320fb3e44cdaf635678a04c473ca4f348514 (diff) | |
download | sonarqube-dce23fcf9ac907f6621ab0c11c397febff280ebc.tar.gz sonarqube-dce23fcf9ac907f6621ab0c11c397febff280ebc.zip |
SONAR-5056 Read debt from rule during analysis
Diffstat (limited to 'plugins')
2 files changed, 128 insertions, 114 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java index 8d4c893f436..359f348cc7c 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecorator.java @@ -24,10 +24,12 @@ import com.google.common.annotations.VisibleForTesting; 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; @@ -37,10 +39,13 @@ import org.sonar.api.measures.*; 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; @@ -57,10 +62,12 @@ public final class TechnicalDebtDecorator implements Decorator { 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) { @@ -81,25 +88,29 @@ public final class TechnicalDebtDecorator implements Decorator { } 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) { @@ -108,8 +119,8 @@ public final class TechnicalDebtDecorator implements Decorator { } } - 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())); } } @@ -126,17 +137,17 @@ public final class TechnicalDebtDecorator implements Decorator { } @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); @@ -145,22 +156,17 @@ public final class TechnicalDebtDecorator implements Decorator { } @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) { @@ -171,16 +177,18 @@ public final class TechnicalDebtDecorator implements Decorator { } } - 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) { @@ -207,4 +215,13 @@ public final class TechnicalDebtDecorator implements Decorator { .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; + } + } diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java index 248e2921192..cc98836f049 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/TechnicalDebtDecoratorTest.java @@ -32,23 +32,21 @@ import org.mockito.ArgumentMatcher; 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; @@ -78,14 +76,24 @@ public class TechnicalDebtDecoratorTest { @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 @@ -109,24 +117,17 @@ public class TechnicalDebtDecoratorTest { @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 @@ -134,14 +135,10 @@ public class TechnicalDebtDecoratorTest { 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 @@ -149,10 +146,6 @@ public class TechnicalDebtDecoratorTest { 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); @@ -163,20 +156,16 @@ public class TechnicalDebtDecoratorTest { 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 @@ -190,22 +179,20 @@ public class TechnicalDebtDecoratorTest { 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 @@ -214,21 +201,20 @@ public class TechnicalDebtDecoratorTest { 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 @@ -265,8 +251,8 @@ public class TechnicalDebtDecoratorTest { 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)); @@ -293,34 +279,17 @@ public class TechnicalDebtDecoratorTest { 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; } @@ -332,7 +301,35 @@ public class TechnicalDebtDecoratorTest { 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()); } |