]> source.dussan.org Git - sonarqube.git/blob
6b40a6a72f401e2508b6566cff139de7abd1bc83
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.measure.live;
21
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Optional;
30 import java.util.Set;
31 import javax.annotation.Nullable;
32 import org.junit.Test;
33 import org.sonar.api.issue.Issue;
34 import org.sonar.api.measures.CoreMetrics;
35 import org.sonar.api.measures.Metric;
36 import org.sonar.api.rule.Severity;
37 import org.sonar.api.rules.RuleType;
38 import org.sonar.db.component.ComponentDto;
39 import org.sonar.db.issue.IssueGroupDto;
40 import org.sonar.server.measure.DebtRatingGrid;
41 import org.sonar.server.measure.Rating;
42
43 import static java.util.Arrays.asList;
44 import static org.assertj.core.api.Assertions.assertThat;
45 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED;
46 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS;
47 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS;
48 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING;
49 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED;
50 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS;
51 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS;
52 import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING;
53
54 public class MeasureUpdateFormulaFactoryImplTest {
55
56   private final MeasureUpdateFormulaFactoryImpl underTest = new MeasureUpdateFormulaFactoryImpl();
57
58   @Test
59   public void getFormulaMetrics_include_the_dependent_metrics() {
60     for (MeasureUpdateFormula formula : underTest.getFormulas()) {
61       assertThat(underTest.getFormulaMetrics()).contains(formula.getMetric());
62       for (Metric<?> dependentMetric : formula.getDependentMetrics()) {
63         assertThat(underTest.getFormulaMetrics()).contains(dependentMetric);
64       }
65     }
66   }
67
68   @Test
69   public void hierarchy_adding_numbers() {
70     new HierarchyTester(CoreMetrics.VIOLATIONS)
71       .withValue(1d)
72       .withChildrenValues(2d, 3d)
73       .expectedResult(6d);
74
75     new HierarchyTester(CoreMetrics.BUGS)
76       .withValue(0d)
77       .withChildrenValues(2d, 3d)
78       .expectedResult(5d);
79
80     new HierarchyTester(CoreMetrics.NEW_BUGS)
81       .withValue(1d)
82       .expectedResult(1d);
83   }
84
85   @Test
86   public void hierarchy_highest_rating() {
87     new HierarchyTester(CoreMetrics.RELIABILITY_RATING)
88       .withValue(1d)
89       .withChildrenValues(2d, 3d)
90       .expectedRating(Rating.C);
91
92     // if no children, no need to set a value
93     new HierarchyTester(CoreMetrics.SECURITY_RATING)
94       .withValue(1d)
95       .expectedResult(null);
96
97     new HierarchyTester(CoreMetrics.NEW_RELIABILITY_RATING)
98       .withValue(5d)
99       .withChildrenValues(2d, 3d)
100       .expectedRating(Rating.E);
101   }
102
103   @Test
104   public void hierarchy_combining_other_metrics() {
105     new HierarchyTester(SECURITY_HOTSPOTS_TO_REVIEW_STATUS)
106       .withValue(SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1d)
107       .withChildrenHotspotsCounts(10, 10, 2, 10)
108       .expectedResult(3d);
109
110     new HierarchyTester(SECURITY_HOTSPOTS_REVIEWED_STATUS)
111       .withValue(SECURITY_HOTSPOTS_REVIEWED_STATUS, 1d)
112       .withChildrenHotspotsCounts(2, 10, 10, 10)
113       .expectedResult(3d);
114
115     new HierarchyTester(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS)
116       .withValue(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1d)
117       .withChildrenHotspotsCounts(10, 10, 10, 2)
118       .expectedResult(3d);
119
120     new HierarchyTester(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS)
121       .withValue(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 1d)
122       .withChildrenHotspotsCounts(10, 2, 10, 10)
123       .expectedResult(3d);
124
125     new HierarchyTester(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED)
126       .withValue(SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1d)
127       .withValue(SECURITY_HOTSPOTS_REVIEWED_STATUS, 1d)
128       .expectedResult(50d);
129     new HierarchyTester(CoreMetrics.SECURITY_REVIEW_RATING)
130       .withValue(SECURITY_HOTSPOTS_REVIEWED, 100d)
131       .expectedRating(Rating.A);
132
133     new HierarchyTester(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED)
134       .withValue(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1d)
135       .withValue(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 1d)
136       .expectedResult(50d);
137     new HierarchyTester(CoreMetrics.NEW_SECURITY_REVIEW_RATING)
138       .withValue(NEW_SECURITY_HOTSPOTS_REVIEWED, 0d)
139       .expectedRating(Rating.E);
140   }
141
142   @Test
143   public void test_violations() {
144     withNoIssues().assertThatValueIs(CoreMetrics.VIOLATIONS, 0);
145     with(newGroup(), newGroup().setCount(4)).assertThatValueIs(CoreMetrics.VIOLATIONS, 5);
146
147     // exclude resolved
148     IssueGroupDto resolved = newResolvedGroup(Issue.RESOLUTION_FIXED, Issue.STATUS_RESOLVED);
149     with(newGroup(), newGroup(), resolved).assertThatValueIs(CoreMetrics.VIOLATIONS, 2);
150
151     // include issues on leak
152     IssueGroupDto onLeak = newGroup().setCount(11).setInLeak(true);
153     with(newGroup(), newGroup(), onLeak).assertThatValueIs(CoreMetrics.VIOLATIONS, 1 + 1 + 11);
154   }
155
156   @Test
157   public void test_bugs() {
158     withNoIssues().assertThatValueIs(CoreMetrics.BUGS, 0);
159     with(
160       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(3),
161       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(5),
162       // exclude resolved
163       newResolvedGroup(RuleType.BUG).setCount(7),
164       // not bugs
165       newGroup(RuleType.CODE_SMELL).setCount(11))
166       .assertThatValueIs(CoreMetrics.BUGS, 3 + 5);
167   }
168
169   @Test
170   public void test_code_smells() {
171     withNoIssues().assertThatValueIs(CoreMetrics.CODE_SMELLS, 0);
172     with(
173       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
174       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setCount(5),
175       // exclude resolved
176       newResolvedGroup(RuleType.CODE_SMELL).setCount(7),
177       // not code smells
178       newGroup(RuleType.BUG).setCount(11))
179       .assertThatValueIs(CoreMetrics.CODE_SMELLS, 3 + 5);
180   }
181
182   @Test
183   public void test_vulnerabilities() {
184     withNoIssues().assertThatValueIs(CoreMetrics.VULNERABILITIES, 0);
185     with(
186       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setCount(3),
187       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(5),
188       // exclude resolved
189       newResolvedGroup(RuleType.VULNERABILITY).setCount(7),
190       // not vulnerabilities
191       newGroup(RuleType.BUG).setCount(11))
192       .assertThatValueIs(CoreMetrics.VULNERABILITIES, 3 + 5);
193   }
194
195   @Test
196   public void test_security_hotspots() {
197     withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 0);
198     with(
199       newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.MAJOR).setCount(3),
200       newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.CRITICAL).setCount(5),
201       // exclude resolved
202       newResolvedGroup(RuleType.SECURITY_HOTSPOT).setCount(7),
203       // not hotspots
204       newGroup(RuleType.BUG).setCount(11))
205       .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 3 + 5);
206   }
207
208   @Test
209   public void test_security_review_rating() {
210     with(
211       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
212       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
213       .assertThatValueIs(SECURITY_REVIEW_RATING, Rating.B);
214
215     withNoIssues()
216       .assertThatValueIs(SECURITY_REVIEW_RATING, Rating.A);
217   }
218
219   @Test
220   public void test_security_hotspots_reviewed() {
221     with(
222       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
223       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
224       .assertThatValueIs(SECURITY_HOTSPOTS_REVIEWED, 75.0);
225
226     withNoIssues()
227       .assertNoValue(SECURITY_HOTSPOTS_REVIEWED);
228   }
229
230   @Test
231   public void test_security_hotspots_reviewed_status() {
232     with(
233       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
234       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
235       .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS, 3.0);
236
237     withNoIssues()
238       .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS, 0.0);
239   }
240
241   @Test
242   public void test_security_hotspots_to_review_status() {
243     with(
244       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
245       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
246       .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1.0);
247
248     withNoIssues()
249       .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 0.0);
250   }
251
252   @Test
253   public void count_unresolved_by_severity() {
254     withNoIssues()
255       .assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 0)
256       .assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 0)
257       .assertThatValueIs(CoreMetrics.MAJOR_VIOLATIONS, 0)
258       .assertThatValueIs(CoreMetrics.MINOR_VIOLATIONS, 0)
259       .assertThatValueIs(CoreMetrics.INFO_VIOLATIONS, 0);
260
261     with(
262       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setCount(3),
263       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(5),
264       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(7),
265       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(11),
266       // exclude security hotspot
267       newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.CRITICAL).setCount(15),
268       // include leak
269       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(13),
270       // exclude resolved
271       newResolvedGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setCount(17),
272       newResolvedGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(19),
273       newResolvedGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.INFO).setCount(21))
274       .assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 11 + 13)
275       .assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 7)
276       .assertThatValueIs(CoreMetrics.MAJOR_VIOLATIONS, 3 + 5)
277       .assertThatValueIs(CoreMetrics.MINOR_VIOLATIONS, 0)
278       .assertThatValueIs(CoreMetrics.INFO_VIOLATIONS, 0);
279   }
280
281   @Test
282   public void count_resolved() {
283     withNoIssues()
284       .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 0)
285       .assertThatValueIs(CoreMetrics.WONT_FIX_ISSUES, 0);
286
287     with(
288       newResolvedGroup(Issue.RESOLUTION_FIXED, Issue.STATUS_RESOLVED).setCount(3),
289       newResolvedGroup(Issue.RESOLUTION_FALSE_POSITIVE, Issue.STATUS_CLOSED).setCount(5),
290       newResolvedGroup(Issue.RESOLUTION_WONT_FIX, Issue.STATUS_CLOSED).setSeverity(Severity.MAJOR).setCount(7),
291       newResolvedGroup(Issue.RESOLUTION_WONT_FIX, Issue.STATUS_CLOSED).setSeverity(Severity.BLOCKER).setCount(11),
292       newResolvedGroup(Issue.RESOLUTION_REMOVED, Issue.STATUS_CLOSED).setCount(13),
293       // exclude security hotspot
294       newResolvedGroup(Issue.RESOLUTION_WONT_FIX, Issue.STATUS_RESOLVED).setCount(15).setRuleType(RuleType.SECURITY_HOTSPOT.getDbConstant()),
295       // exclude unresolved
296       newGroup(RuleType.VULNERABILITY).setCount(17),
297       newGroup(RuleType.BUG).setCount(19))
298       .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 5)
299       .assertThatValueIs(CoreMetrics.WONT_FIX_ISSUES, 7 + 11);
300   }
301
302   @Test
303   public void count_by_status() {
304     withNoIssues()
305       .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 0)
306       .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 0)
307       .assertThatValueIs(CoreMetrics.REOPENED_ISSUES, 0);
308
309     with(
310       newGroup().setStatus(Issue.STATUS_CONFIRMED).setSeverity(Severity.BLOCKER).setCount(3),
311       newGroup().setStatus(Issue.STATUS_CONFIRMED).setSeverity(Severity.INFO).setCount(5),
312       newGroup().setStatus(Issue.STATUS_REOPENED).setCount(7),
313       newGroup(RuleType.CODE_SMELL).setStatus(Issue.STATUS_OPEN).setCount(9),
314       newGroup(RuleType.BUG).setStatus(Issue.STATUS_OPEN).setCount(11),
315       // exclude security hotspot
316       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN).setCount(12),
317       newResolvedGroup(Issue.RESOLUTION_FALSE_POSITIVE, Issue.STATUS_CLOSED).setCount(13))
318       .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 3 + 5)
319       .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 9 + 11)
320       .assertThatValueIs(CoreMetrics.REOPENED_ISSUES, 7);
321   }
322
323   @Test
324   public void test_technical_debt() {
325     withNoIssues().assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 0);
326
327     with(
328       newGroup(RuleType.CODE_SMELL).setEffort(3.0).setInLeak(false),
329       newGroup(RuleType.CODE_SMELL).setEffort(5.0).setInLeak(true),
330       // exclude security hotspot
331       newGroup(RuleType.SECURITY_HOTSPOT).setEffort(9).setInLeak(true),
332       newGroup(RuleType.SECURITY_HOTSPOT).setEffort(11).setInLeak(false),
333       // not code smells
334       newGroup(RuleType.BUG).setEffort(7.0),
335       // exclude resolved
336       newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0))
337       .assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 3.0 + 5.0);
338   }
339
340   @Test
341   public void test_reliability_remediation_effort() {
342     withNoIssues().assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 0);
343
344     with(
345       newGroup(RuleType.BUG).setEffort(3.0),
346       newGroup(RuleType.BUG).setEffort(5.0).setSeverity(Severity.BLOCKER),
347       // not bugs
348       newGroup(RuleType.CODE_SMELL).setEffort(7.0),
349       // exclude resolved
350       newResolvedGroup(RuleType.BUG).setEffort(17.0))
351       .assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 3.0 + 5.0);
352   }
353
354   @Test
355   public void test_security_remediation_effort() {
356     withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 0);
357
358     with(
359       newGroup(RuleType.VULNERABILITY).setEffort(3.0),
360       newGroup(RuleType.VULNERABILITY).setEffort(5.0).setSeverity(Severity.BLOCKER),
361       // not vulnerability
362       newGroup(RuleType.CODE_SMELL).setEffort(7.0),
363       // exclude resolved
364       newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0))
365       .assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 3.0 + 5.0);
366   }
367
368   @Test
369   public void test_sqale_debt_ratio_and_sqale_rating() {
370     withNoIssues()
371       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
372       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
373
374     // technical_debt not computed
375     with(CoreMetrics.DEVELOPMENT_COST, "0")
376       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
377       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
378     with(CoreMetrics.DEVELOPMENT_COST, "20")
379       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
380       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
381
382     // development_cost not computed
383     with(CoreMetrics.TECHNICAL_DEBT, 0)
384       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
385       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
386     with(CoreMetrics.TECHNICAL_DEBT, 20)
387       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
388       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
389
390     // input measures are available
391     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
392       .andText(CoreMetrics.DEVELOPMENT_COST, "0")
393       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
394       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
395
396     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
397       .andText(CoreMetrics.DEVELOPMENT_COST, "160")
398       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 12.5)
399       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.C);
400
401     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
402       .andText(CoreMetrics.DEVELOPMENT_COST, "10")
403       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 200.0)
404       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.E);
405
406     // A is 5% --> min debt is exactly 200*0.05=10
407     with(CoreMetrics.DEVELOPMENT_COST, "200")
408       .and(CoreMetrics.TECHNICAL_DEBT, 10.0)
409       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 5.0)
410       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
411
412     with(CoreMetrics.TECHNICAL_DEBT, 0.0)
413       .andText(CoreMetrics.DEVELOPMENT_COST, "0")
414       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
415       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
416
417     with(CoreMetrics.TECHNICAL_DEBT, 0.0)
418       .andText(CoreMetrics.DEVELOPMENT_COST, "80")
419       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0);
420
421     with(CoreMetrics.TECHNICAL_DEBT, -20.0)
422       .andText(CoreMetrics.DEVELOPMENT_COST, "0")
423       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
424       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
425
426     // bug, debt can't be negative
427     with(CoreMetrics.TECHNICAL_DEBT, -20.0)
428       .andText(CoreMetrics.DEVELOPMENT_COST, "80")
429       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
430       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
431
432     // bug, cost can't be negative
433     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
434       .andText(CoreMetrics.DEVELOPMENT_COST, "-80")
435       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
436       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
437   }
438
439   @Test
440   public void test_effort_to_reach_maintainability_rating_A() {
441     withNoIssues()
442       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
443
444     // technical_debt not computed
445     with(CoreMetrics.DEVELOPMENT_COST, 0.0)
446       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
447     with(CoreMetrics.DEVELOPMENT_COST, 20.0)
448       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
449
450     // development_cost not computed
451     with(CoreMetrics.TECHNICAL_DEBT, 0.0)
452       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
453     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
454       // development cost is considered as zero, so the effort is to reach... zero
455       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 20.0);
456
457     // B to A
458     with(CoreMetrics.DEVELOPMENT_COST, "200")
459       .and(CoreMetrics.TECHNICAL_DEBT, 40.0)
460       // B is 5% --> goal is to reach 200*0.05=10 --> effort is 40-10=30
461       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 40.0 - (200.0 * 0.05));
462
463     // E to A
464     with(CoreMetrics.DEVELOPMENT_COST, "200")
465       .and(CoreMetrics.TECHNICAL_DEBT, 180.0)
466       // B is 5% --> goal is to reach 200*0.05=10 --> effort is 180-10=170
467       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 180.0 - (200.0 * 0.05));
468
469     // already A
470     with(CoreMetrics.DEVELOPMENT_COST, "200")
471       .and(CoreMetrics.TECHNICAL_DEBT, 8.0)
472       // B is 5% --> goal is to reach 200*0.05=10 --> debt is already at 8 --> effort to reach A is zero
473       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
474
475     // exactly lower range of B
476     with(CoreMetrics.DEVELOPMENT_COST, "200")
477       .and(CoreMetrics.TECHNICAL_DEBT, 10.0)
478       // B is 5% --> goal is to reach 200*0.05=10 --> debt is 10 --> effort to reach A is zero
479       // FIXME need zero to reach A but effective rating is B !
480       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
481   }
482
483   @Test
484   public void test_reliability_rating() {
485     withNoIssues()
486       .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
487
488     with(
489       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(1),
490       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setCount(5),
491       // excluded, not a bug
492       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(3))
493       // highest severity of bugs is CRITICAL --> D
494       .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.D);
495
496     with(
497       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
498       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(5))
499       // no bugs --> A
500       .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
501   }
502
503   @Test
504   public void test_security_rating() {
505     withNoIssues()
506       .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
507
508     with(
509       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(1),
510       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setCount(5),
511       // excluded, not a vulnerability
512       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(3))
513       // highest severity of vulnerabilities is CRITICAL --> D
514       .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.D);
515
516     with(
517       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
518       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(5))
519       // no vulnerabilities --> A
520       .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
521   }
522
523   @Test
524   public void test_new_bugs() {
525     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 0.0);
526
527     with(
528       newGroup(RuleType.BUG).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
529       newGroup(RuleType.BUG).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
530       newGroup(RuleType.BUG).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
531       // not bugs
532       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(9),
533       newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
534       .assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 5 + 7);
535
536   }
537
538   @Test
539   public void test_new_code_smells() {
540     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 0.0);
541
542     with(
543       newGroup(RuleType.CODE_SMELL).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
544       newGroup(RuleType.CODE_SMELL).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
545       newGroup(RuleType.CODE_SMELL).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
546       // not code smells
547       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
548       newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
549       .assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 5 + 7);
550   }
551
552   @Test
553   public void test_new_vulnerabilities() {
554     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 0.0);
555
556     with(
557       newGroup(RuleType.VULNERABILITY).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
558       newGroup(RuleType.VULNERABILITY).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
559       newGroup(RuleType.VULNERABILITY).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
560       // not vulnerabilities
561       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
562       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(11))
563       .assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 5 + 7);
564   }
565
566   @Test
567   public void test_new_security_hotspots() {
568     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 0);
569
570     with(
571       newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
572       newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
573       newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
574       // not hotspots
575       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
576       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(11))
577       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 5 + 7);
578   }
579
580   @Test
581   public void test_new_violations() {
582     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 0.0);
583
584     with(
585       newGroup(RuleType.BUG).setInLeak(true).setCount(5),
586       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(7),
587       newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(9),
588       // not in leak
589       newGroup(RuleType.BUG).setInLeak(false).setCount(11),
590       newGroup(RuleType.CODE_SMELL).setInLeak(false).setCount(13),
591       newGroup(RuleType.VULNERABILITY).setInLeak(false).setCount(17))
592       .assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 5 + 7 + 9);
593   }
594
595   @Test
596   public void test_new_blocker_violations() {
597     withNoIssues()
598       .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 0.0);
599
600     with(
601       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(3),
602       newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(5),
603       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(7),
604       // not blocker
605       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
606       // not in leak
607       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(false).setCount(11),
608       newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(false).setCount(13))
609       .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 3 + 5 + 7);
610   }
611
612   @Test
613   public void test_new_critical_violations() {
614     withNoIssues()
615       .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 0.0);
616
617     with(
618       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(3),
619       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(5),
620       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(7),
621       // not CRITICAL
622       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(true).setCount(9),
623       // not in leak
624       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(false).setCount(11),
625       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setInLeak(false).setCount(13))
626       .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 3 + 5 + 7);
627   }
628
629   @Test
630   public void test_new_major_violations() {
631     withNoIssues()
632       .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 0.0);
633
634     with(
635       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(true).setCount(3),
636       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setInLeak(true).setCount(5),
637       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setInLeak(true).setCount(7),
638       // not MAJOR
639       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
640       // not in leak
641       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(false).setCount(11),
642       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setInLeak(false).setCount(13))
643       .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 3 + 5 + 7);
644   }
645
646   @Test
647   public void test_new_minor_violations() {
648     withNoIssues()
649       .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 0.0);
650
651     with(
652       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MINOR).setInLeak(true).setCount(3),
653       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setInLeak(true).setCount(5),
654       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setInLeak(true).setCount(7),
655       // not MINOR
656       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
657       // not in leak
658       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MINOR).setInLeak(false).setCount(11),
659       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setInLeak(false).setCount(13))
660       .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 3 + 5 + 7);
661   }
662
663   @Test
664   public void test_new_info_violations() {
665     withNoIssues()
666       .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 0.0);
667
668     with(
669       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.INFO).setInLeak(true).setCount(3),
670       newGroup(RuleType.BUG).setSeverity(Severity.INFO).setInLeak(true).setCount(5),
671       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setInLeak(true).setCount(7),
672       // not INFO
673       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
674       // not in leak
675       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.INFO).setInLeak(false).setCount(11),
676       newGroup(RuleType.BUG).setSeverity(Severity.INFO).setInLeak(false).setCount(13))
677       .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 3 + 5 + 7);
678   }
679
680   @Test
681   public void test_new_technical_debt() {
682     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0);
683
684     with(
685       newGroup(RuleType.CODE_SMELL).setEffort(3.0).setInLeak(true),
686       // not in leak
687       newGroup(RuleType.CODE_SMELL).setEffort(5.0).setInLeak(false),
688       // not code smells
689       newGroup(RuleType.SECURITY_HOTSPOT).setEffort(9.0).setInLeak(true),
690       newGroup(RuleType.BUG).setEffort(7.0).setInLeak(true),
691       // exclude resolved
692       newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0).setInLeak(true))
693       .assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 3.0);
694   }
695
696   @Test
697   public void test_new_reliability_remediation_effort() {
698     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 0.0);
699
700     with(
701       newGroup(RuleType.BUG).setEffort(3.0).setInLeak(true),
702       // not in leak
703       newGroup(RuleType.BUG).setEffort(5.0).setInLeak(false),
704       // not bugs
705       newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
706       // exclude resolved
707       newResolvedGroup(RuleType.BUG).setEffort(17.0).setInLeak(true))
708       .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 3.0);
709   }
710
711   @Test
712   public void test_new_security_remediation_effort() {
713     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 0.0);
714
715     with(
716       newGroup(RuleType.VULNERABILITY).setEffort(3.0).setInLeak(true),
717       // not in leak
718       newGroup(RuleType.VULNERABILITY).setEffort(5.0).setInLeak(false),
719       // not vulnerability
720       newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
721       // exclude resolved
722       newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0).setInLeak(true))
723       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 3.0);
724   }
725
726   @Test
727   public void test_new_reliability_rating() {
728     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.A);
729
730     with(
731       newGroup(RuleType.BUG).setSeverity(Severity.INFO).setCount(3).setInLeak(true),
732       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setCount(1).setInLeak(true),
733       // not in leak
734       newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(false),
735       // not bug
736       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
737       // exclude resolved
738       newResolvedGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(true))
739       // highest severity of bugs on leak period is minor -> B
740       .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.B);
741   }
742
743   @Test
744   public void test_new_security_rating() {
745     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.A);
746
747     with(
748       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setCount(3).setInLeak(true),
749       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setCount(1).setInLeak(true),
750       // not in leak
751       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(false),
752       // not vulnerability
753       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
754       // exclude resolved
755       newResolvedGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(true))
756       // highest severity of bugs on leak period is minor -> B
757       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.B);
758   }
759
760   @Test
761   public void test_new_security_review_rating() {
762     with(
763       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
764       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
765       // not in leak
766       newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Issue.STATUS_TO_REVIEW).setInLeak(false))
767       .assertThatLeakValueIs(NEW_SECURITY_REVIEW_RATING, Rating.B);
768
769     withNoIssues()
770       .assertThatLeakValueIs(NEW_SECURITY_REVIEW_RATING, Rating.A);
771   }
772
773   @Test
774   public void test_new_security_hotspots_reviewed() {
775     with(
776       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
777       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
778       // not in leak
779       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
780       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED, 75.0);
781
782     withNoIssues()
783       .assertNoLeakValue(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED);
784   }
785
786   @Test
787   public void test_new_security_hotspots_reviewed_status() {
788     with(
789       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
790       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
791       // not in leak
792       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
793       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 3.0);
794
795     withNoIssues()
796       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 0.0);
797   }
798
799   @Test
800   public void test_new_security_hotspots_to_review_status() {
801     with(
802       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
803       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
804       // not in leak
805       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
806       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1.0);
807
808     withNoIssues()
809       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 0.0);
810   }
811
812   @Test
813   public void test_new_sqale_debt_ratio_and_new_maintainability_rating() {
814     withNoIssues()
815       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
816       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
817
818     // technical_debt not computed
819     with(CoreMetrics.NEW_DEVELOPMENT_COST, 0)
820       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
821       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
822     with(CoreMetrics.NEW_DEVELOPMENT_COST, 20)
823       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
824       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
825
826     // development_cost not computed
827     with(CoreMetrics.NEW_TECHNICAL_DEBT, 0)
828       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
829       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
830     with(CoreMetrics.NEW_TECHNICAL_DEBT, 20)
831       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
832       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
833
834     // input measures are available
835     with(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
836       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
837       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
838       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
839
840     with(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
841       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 160.0)
842       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 12.5)
843       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.C);
844
845     with(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
846       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 10.0D)
847       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 200.0)
848       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.E);
849
850     // A is 5% --> min debt is exactly 200*0.05=10
851     with(CoreMetrics.NEW_DEVELOPMENT_COST, 200.0)
852       .and(CoreMetrics.NEW_TECHNICAL_DEBT, 10.0)
853       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 5.0)
854       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
855
856     with(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0)
857       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
858       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
859       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
860
861     with(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0)
862       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 80.0)
863       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0);
864
865     with(CoreMetrics.NEW_TECHNICAL_DEBT, -20.0)
866       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
867       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
868       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
869
870     // bug, debt can't be negative
871     with(CoreMetrics.NEW_TECHNICAL_DEBT, -20.0)
872       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 80.0)
873       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
874       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
875
876     // bug, cost can't be negative
877     with(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
878       .and(CoreMetrics.NEW_DEVELOPMENT_COST, -80.0)
879       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
880       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
881   }
882
883   private Verifier with(IssueGroupDto... groups) {
884     return new Verifier(groups);
885   }
886
887   private Verifier withNoIssues() {
888     return new Verifier(new IssueGroupDto[0]);
889   }
890
891   private Verifier with(Metric metric, double value) {
892     return new Verifier(new IssueGroupDto[0]).and(metric, value);
893   }
894
895   private Verifier with(Metric metric, String value) {
896     return new Verifier(new IssueGroupDto[0]).andText(metric, value);
897   }
898
899   private class Verifier {
900     private final IssueGroupDto[] groups;
901     private final InitialValues initialValues = new InitialValues();
902
903     private Verifier(IssueGroupDto[] groups) {
904       this.groups = groups;
905     }
906
907     Verifier and(Metric metric, double value) {
908       this.initialValues.values.put(metric, value);
909       return this;
910     }
911
912     Verifier andText(Metric metric, String value) {
913       this.initialValues.text.put(metric, value);
914       return this;
915     }
916
917     Verifier assertThatValueIs(Metric metric, double expectedValue) {
918       TestContext context = run(metric, false);
919       assertThat(context.doubleValue).isNotNull().isEqualTo(expectedValue);
920       return this;
921     }
922
923     Verifier assertThatLeakValueIs(Metric metric, double expectedValue) {
924       TestContext context = run(metric, true);
925       assertThat(context.doubleValue).isNotNull().isEqualTo(expectedValue);
926       return this;
927     }
928
929     Verifier assertThatLeakValueIs(Metric metric, Rating expectedRating) {
930       TestContext context = run(metric, true);
931       assertThat(context.ratingValue).isNotNull().isEqualTo(expectedRating);
932       return this;
933     }
934
935     Verifier assertNoLeakValue(Metric metric) {
936       TestContext context = run(metric, true);
937       assertThat(context.ratingValue).isNull();
938       return this;
939     }
940
941     Verifier assertThatValueIs(Metric metric, Rating expectedValue) {
942       TestContext context = run(metric, false);
943       assertThat(context.ratingValue).isNotNull().isEqualTo(expectedValue);
944       return this;
945     }
946
947     Verifier assertNoValue(Metric metric) {
948       TestContext context = run(metric, false);
949       assertThat(context.ratingValue).isNull();
950       return this;
951     }
952
953     private TestContext run(Metric metric, boolean expectLeakFormula) {
954       MeasureUpdateFormula formula = underTest.getFormulas().stream()
955         .filter(f -> f.getMetric().getKey().equals(metric.getKey()))
956         .findFirst()
957         .get();
958       assertThat(formula.isOnLeak()).isEqualTo(expectLeakFormula);
959       TestContext context = new TestContext(formula.getDependentMetrics(), initialValues);
960       formula.compute(context, newIssueCounter(groups));
961       return context;
962     }
963   }
964
965   private static IssueCounter newIssueCounter(IssueGroupDto... issues) {
966     return new IssueCounter(asList(issues));
967   }
968
969   private static IssueGroupDto newGroup() {
970     return newGroup(RuleType.CODE_SMELL);
971   }
972
973   private static IssueGroupDto newGroup(RuleType ruleType) {
974     IssueGroupDto dto = new IssueGroupDto();
975     // set non-null fields
976     dto.setRuleType(ruleType.getDbConstant());
977     dto.setCount(1);
978     dto.setEffort(0.0);
979     dto.setSeverity(Severity.INFO);
980     dto.setStatus(Issue.STATUS_OPEN);
981     dto.setInLeak(false);
982     return dto;
983   }
984
985   private static IssueGroupDto newResolvedGroup(RuleType ruleType) {
986     return newGroup(ruleType).setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setStatus(Issue.STATUS_CLOSED);
987   }
988
989   private static IssueGroupDto newResolvedGroup(String resolution, String status) {
990     return newGroup().setResolution(resolution).setStatus(status);
991   }
992
993   private static class TestContext implements MeasureUpdateFormula.Context {
994     private final Set<Metric> dependentMetrics;
995     private final InitialValues initialValues;
996     private Double doubleValue;
997     private Rating ratingValue;
998
999     private TestContext(Collection<Metric> dependentMetrics, InitialValues initialValues) {
1000       this.dependentMetrics = new HashSet<>(dependentMetrics);
1001       this.initialValues = initialValues;
1002     }
1003
1004     @Override
1005     public List<Double> getChildrenValues() {
1006       return initialValues.childrenValues;
1007     }
1008
1009     @Override
1010     public long getChildrenHotspotsReviewed() {
1011       return initialValues.childrenHotspotsReviewed;
1012     }
1013
1014     @Override
1015     public long getChildrenHotspotsToReview() {
1016       return initialValues.childrenHotspotsToReview;
1017     }
1018
1019     @Override
1020     public long getChildrenNewHotspotsReviewed() {
1021       return initialValues.childrenNewHotspotsReviewed;
1022     }
1023
1024     @Override
1025     public long getChildrenNewHotspotsToReview() {
1026       return initialValues.childrenNewHotspotsToReview;
1027     }
1028
1029     @Override
1030     public ComponentDto getComponent() {
1031       throw new UnsupportedOperationException();
1032     }
1033
1034     @Override
1035     public DebtRatingGrid getDebtRatingGrid() {
1036       return new DebtRatingGrid(new double[] {0.05, 0.1, 0.2, 0.5});
1037     }
1038
1039     @Override
1040     public Optional<Double> getValue(Metric metric) {
1041       if (!dependentMetrics.contains(metric)) {
1042         throw new IllegalStateException("Metric " + metric.getKey() + " is not declared as a dependency");
1043       }
1044       if (initialValues.values.containsKey(metric)) {
1045         return Optional.of(initialValues.values.get(metric));
1046       }
1047       return Optional.empty();
1048     }
1049
1050     @Override
1051     public Optional<String> getText(Metric metric) {
1052       if (initialValues.text.containsKey(metric)) {
1053         return Optional.of(initialValues.text.get(metric));
1054       }
1055       return Optional.empty();
1056     }
1057
1058     @Override
1059     public void setValue(double value) {
1060       this.doubleValue = value;
1061     }
1062
1063     @Override
1064     public void setValue(Rating value) {
1065       this.ratingValue = value;
1066     }
1067   }
1068
1069   private class InitialValues {
1070     private final Map<Metric, Double> values = new HashMap<>();
1071     private final List<Double> childrenValues = new ArrayList<>();
1072     private final Map<Metric, String> text = new HashMap<>();
1073     private long childrenHotspotsReviewed = 0;
1074     private long childrenNewHotspotsReviewed = 0;
1075     private long childrenHotspotsToReview = 0;
1076     private long childrenNewHotspotsToReview = 0;
1077
1078   }
1079
1080   private class HierarchyTester {
1081     private final Metric metric;
1082     private final InitialValues initialValues;
1083     private final MeasureUpdateFormula formula;
1084
1085     public HierarchyTester(Metric metric) {
1086       this.metric = metric;
1087       this.initialValues = new InitialValues();
1088       this.formula = underTest.getFormulas().stream().filter(f -> f.getMetric().equals(metric)).findAny().get();
1089     }
1090
1091     public HierarchyTester withValue(Metric metric, Double value) {
1092       this.initialValues.values.put(metric, value);
1093       return this;
1094     }
1095
1096     public HierarchyTester withChildrenHotspotsCounts(long childrenHotspotsReviewed, long childrenNewHotspotsReviewed, long childrenHotspotsToReview,
1097       long childrenNewHotspotsToReview) {
1098       this.initialValues.childrenHotspotsReviewed = childrenHotspotsReviewed;
1099       this.initialValues.childrenNewHotspotsReviewed = childrenNewHotspotsReviewed;
1100       this.initialValues.childrenHotspotsToReview = childrenHotspotsToReview;
1101       this.initialValues.childrenNewHotspotsToReview = childrenNewHotspotsToReview;
1102       return this;
1103     }
1104
1105     public HierarchyTester withValue(Double value) {
1106       return withValue(metric, value);
1107     }
1108
1109     public HierarchyTester withChildrenValues(Double... values) {
1110       this.initialValues.childrenValues.addAll(asList(values));
1111       return this;
1112     }
1113
1114     public HierarchyTester expectedResult(@Nullable Double expected) {
1115       TestContext ctx = run();
1116       assertThat(ctx.doubleValue).isEqualTo(expected);
1117       return this;
1118     }
1119
1120     public HierarchyTester expectedRating(@Nullable Rating rating) {
1121       TestContext ctx = run();
1122       assertThat(ctx.ratingValue).isEqualTo(rating);
1123       return this;
1124     }
1125
1126     private TestContext run() {
1127       List<Metric> deps = new LinkedList<>(formula.getDependentMetrics());
1128       deps.add(formula.getMetric());
1129       deps.addAll(initialValues.values.keySet());
1130       deps.addAll(initialValues.text.keySet());
1131       TestContext context = new TestContext(deps, initialValues);
1132       formula.computeHierarchy(context);
1133       return context;
1134     }
1135   }
1136 }