]> source.dussan.org Git - sonarqube.git/blob
9ca8ddc1774a02dbeaac4e6daa21daae132602e2
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2024 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.ACCEPTED_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.ACCEPTED_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_high_impact_accepted_issues() {
325     withNoIssues()
326       .assertThatValueIs(CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES, 0);
327
328     with(
329       newGroup(RuleType.CODE_SMELL).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE)
330         .setHasHighImpactSeverity(true).setCount(3),
331       newGroup(RuleType.CODE_SMELL).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)
332         .setHasHighImpactSeverity(true).setCount(4),
333       newGroup(RuleType.CODE_SMELL).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)
334         .setHasHighImpactSeverity(false).setCount(5),
335       newGroup(RuleType.BUG).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE)
336         .setHasHighImpactSeverity(true).setCount(30),
337       newGroup(RuleType.BUG).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)
338         .setHasHighImpactSeverity(true).setCount(40),
339       newGroup(RuleType.BUG).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)
340         .setHasHighImpactSeverity(false).setCount(50),
341       // exclude security hotspot
342       newGroup(RuleType.SECURITY_HOTSPOT).setResolution(Issue.RESOLUTION_WONT_FIX).setHasHighImpactSeverity(true).setCount(40))
343       .assertThatValueIs(CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES, 4 + 40);
344   }
345
346   @Test
347   public void test_technical_debt() {
348     withNoIssues().assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 0);
349
350     with(
351       newGroup(RuleType.CODE_SMELL).setEffort(3.0).setInLeak(false),
352       newGroup(RuleType.CODE_SMELL).setEffort(5.0).setInLeak(true),
353       // exclude security hotspot
354       newGroup(RuleType.SECURITY_HOTSPOT).setEffort(9).setInLeak(true),
355       newGroup(RuleType.SECURITY_HOTSPOT).setEffort(11).setInLeak(false),
356       // not code smells
357       newGroup(RuleType.BUG).setEffort(7.0),
358       // exclude resolved
359       newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0))
360       .assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 3.0 + 5.0);
361   }
362
363   @Test
364   public void test_reliability_remediation_effort() {
365     withNoIssues().assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 0);
366
367     with(
368       newGroup(RuleType.BUG).setEffort(3.0),
369       newGroup(RuleType.BUG).setEffort(5.0).setSeverity(Severity.BLOCKER),
370       // not bugs
371       newGroup(RuleType.CODE_SMELL).setEffort(7.0),
372       // exclude resolved
373       newResolvedGroup(RuleType.BUG).setEffort(17.0))
374       .assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 3.0 + 5.0);
375   }
376
377   @Test
378   public void test_security_remediation_effort() {
379     withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 0);
380
381     with(
382       newGroup(RuleType.VULNERABILITY).setEffort(3.0),
383       newGroup(RuleType.VULNERABILITY).setEffort(5.0).setSeverity(Severity.BLOCKER),
384       // not vulnerability
385       newGroup(RuleType.CODE_SMELL).setEffort(7.0),
386       // exclude resolved
387       newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0))
388       .assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 3.0 + 5.0);
389   }
390
391   @Test
392   public void test_sqale_debt_ratio_and_sqale_rating() {
393     withNoIssues()
394       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
395       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
396
397     // technical_debt not computed
398     with(CoreMetrics.DEVELOPMENT_COST, "0")
399       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
400       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
401     with(CoreMetrics.DEVELOPMENT_COST, "20")
402       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
403       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
404
405     // development_cost not computed
406     with(CoreMetrics.TECHNICAL_DEBT, 0)
407       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
408       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
409     with(CoreMetrics.TECHNICAL_DEBT, 20)
410       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
411       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
412
413     // input measures are available
414     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
415       .andText(CoreMetrics.DEVELOPMENT_COST, "0")
416       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
417       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
418
419     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
420       .andText(CoreMetrics.DEVELOPMENT_COST, "160")
421       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 12.5)
422       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.C);
423
424     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
425       .andText(CoreMetrics.DEVELOPMENT_COST, "10")
426       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 200.0)
427       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.E);
428
429     // A is 5% --> min debt is exactly 200*0.05=10
430     with(CoreMetrics.DEVELOPMENT_COST, "200")
431       .and(CoreMetrics.TECHNICAL_DEBT, 10.0)
432       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 5.0)
433       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
434
435     with(CoreMetrics.TECHNICAL_DEBT, 0.0)
436       .andText(CoreMetrics.DEVELOPMENT_COST, "0")
437       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
438       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
439
440     with(CoreMetrics.TECHNICAL_DEBT, 0.0)
441       .andText(CoreMetrics.DEVELOPMENT_COST, "80")
442       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0);
443
444     with(CoreMetrics.TECHNICAL_DEBT, -20.0)
445       .andText(CoreMetrics.DEVELOPMENT_COST, "0")
446       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
447       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
448
449     // bug, debt can't be negative
450     with(CoreMetrics.TECHNICAL_DEBT, -20.0)
451       .andText(CoreMetrics.DEVELOPMENT_COST, "80")
452       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
453       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
454
455     // bug, cost can't be negative
456     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
457       .andText(CoreMetrics.DEVELOPMENT_COST, "-80")
458       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
459       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
460   }
461
462   @Test
463   public void test_effort_to_reach_maintainability_rating_A() {
464     withNoIssues()
465       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
466
467     // technical_debt not computed
468     with(CoreMetrics.DEVELOPMENT_COST, 0.0)
469       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
470     with(CoreMetrics.DEVELOPMENT_COST, 20.0)
471       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
472
473     // development_cost not computed
474     with(CoreMetrics.TECHNICAL_DEBT, 0.0)
475       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
476     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
477       // development cost is considered as zero, so the effort is to reach... zero
478       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 20.0);
479
480     // B to A
481     with(CoreMetrics.DEVELOPMENT_COST, "200")
482       .and(CoreMetrics.TECHNICAL_DEBT, 40.0)
483       // B is 5% --> goal is to reach 200*0.05=10 --> effort is 40-10=30
484       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 40.0 - (200.0 * 0.05));
485
486     // E to A
487     with(CoreMetrics.DEVELOPMENT_COST, "200")
488       .and(CoreMetrics.TECHNICAL_DEBT, 180.0)
489       // B is 5% --> goal is to reach 200*0.05=10 --> effort is 180-10=170
490       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 180.0 - (200.0 * 0.05));
491
492     // already A
493     with(CoreMetrics.DEVELOPMENT_COST, "200")
494       .and(CoreMetrics.TECHNICAL_DEBT, 8.0)
495       // B is 5% --> goal is to reach 200*0.05=10 --> debt is already at 8 --> effort to reach A is zero
496       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
497
498     // exactly lower range of B
499     with(CoreMetrics.DEVELOPMENT_COST, "200")
500       .and(CoreMetrics.TECHNICAL_DEBT, 10.0)
501       // B is 5% --> goal is to reach 200*0.05=10 --> debt is 10 --> effort to reach A is zero
502       // FIXME need zero to reach A but effective rating is B !
503       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
504   }
505
506   @Test
507   public void test_reliability_rating() {
508     withNoIssues()
509       .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
510
511     with(
512       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(1),
513       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setCount(5),
514       // excluded, not a bug
515       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(3))
516       // highest severity of bugs is CRITICAL --> D
517       .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.D);
518
519     with(
520       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
521       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(5))
522       // no bugs --> A
523       .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
524   }
525
526   @Test
527   public void test_security_rating() {
528     withNoIssues()
529       .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
530
531     with(
532       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(1),
533       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setCount(5),
534       // excluded, not a vulnerability
535       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(3))
536       // highest severity of vulnerabilities is CRITICAL --> D
537       .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.D);
538
539     with(
540       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
541       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(5))
542       // no vulnerabilities --> A
543       .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
544   }
545
546   @Test
547   public void test_new_bugs() {
548     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 0.0);
549
550     with(
551       newGroup(RuleType.BUG).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
552       newGroup(RuleType.BUG).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
553       newGroup(RuleType.BUG).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
554       // not bugs
555       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(9),
556       newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
557       .assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 5 + 7);
558
559   }
560
561   @Test
562   public void test_new_code_smells() {
563     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 0.0);
564
565     with(
566       newGroup(RuleType.CODE_SMELL).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
567       newGroup(RuleType.CODE_SMELL).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
568       newGroup(RuleType.CODE_SMELL).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
569       // not code smells
570       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
571       newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
572       .assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 5 + 7);
573   }
574
575   @Test
576   public void test_new_vulnerabilities() {
577     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 0.0);
578
579     with(
580       newGroup(RuleType.VULNERABILITY).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
581       newGroup(RuleType.VULNERABILITY).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
582       newGroup(RuleType.VULNERABILITY).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
583       // not vulnerabilities
584       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
585       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(11))
586       .assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 5 + 7);
587   }
588
589   @Test
590   public void test_new_security_hotspots() {
591     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 0);
592
593     with(
594       newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
595       newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
596       newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
597       // not hotspots
598       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
599       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(11))
600       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 5 + 7);
601   }
602
603   @Test
604   public void test_new_violations() {
605     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 0.0);
606
607     with(
608       newGroup(RuleType.BUG).setInLeak(true).setCount(5),
609       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(7),
610       newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(9),
611       // not in leak
612       newGroup(RuleType.BUG).setInLeak(false).setCount(11),
613       newGroup(RuleType.CODE_SMELL).setInLeak(false).setCount(13),
614       newGroup(RuleType.VULNERABILITY).setInLeak(false).setCount(17))
615       .assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 5 + 7 + 9);
616   }
617
618   @Test
619   public void test_new_blocker_violations() {
620     withNoIssues()
621       .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 0.0);
622
623     with(
624       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(3),
625       newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(5),
626       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(7),
627       // not blocker
628       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
629       // not in leak
630       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(false).setCount(11),
631       newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(false).setCount(13))
632       .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 3 + 5 + 7);
633   }
634
635   @Test
636   public void test_new_critical_violations() {
637     withNoIssues()
638       .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 0.0);
639
640     with(
641       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(3),
642       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(5),
643       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(7),
644       // not CRITICAL
645       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(true).setCount(9),
646       // not in leak
647       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(false).setCount(11),
648       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setInLeak(false).setCount(13))
649       .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 3 + 5 + 7);
650   }
651
652   @Test
653   public void test_new_major_violations() {
654     withNoIssues()
655       .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 0.0);
656
657     with(
658       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(true).setCount(3),
659       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setInLeak(true).setCount(5),
660       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setInLeak(true).setCount(7),
661       // not MAJOR
662       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
663       // not in leak
664       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(false).setCount(11),
665       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setInLeak(false).setCount(13))
666       .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 3 + 5 + 7);
667   }
668
669   @Test
670   public void test_new_minor_violations() {
671     withNoIssues()
672       .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 0.0);
673
674     with(
675       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MINOR).setInLeak(true).setCount(3),
676       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setInLeak(true).setCount(5),
677       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setInLeak(true).setCount(7),
678       // not MINOR
679       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
680       // not in leak
681       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MINOR).setInLeak(false).setCount(11),
682       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setInLeak(false).setCount(13))
683       .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 3 + 5 + 7);
684   }
685
686   @Test
687   public void test_new_info_violations() {
688     withNoIssues()
689       .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 0.0);
690
691     with(
692       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.INFO).setInLeak(true).setCount(3),
693       newGroup(RuleType.BUG).setSeverity(Severity.INFO).setInLeak(true).setCount(5),
694       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setInLeak(true).setCount(7),
695       // not INFO
696       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
697       // not in leak
698       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.INFO).setInLeak(false).setCount(11),
699       newGroup(RuleType.BUG).setSeverity(Severity.INFO).setInLeak(false).setCount(13))
700       .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 3 + 5 + 7);
701   }
702
703   @Test
704   public void test_new_accepted_issues() {
705     withNoIssues()
706       .assertThatLeakValueIs(CoreMetrics.NEW_ACCEPTED_ISSUES, 0);
707
708     with(
709       newGroup(RuleType.CODE_SMELL).setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setInLeak(true).setCount(3),
710       newGroup(RuleType.CODE_SMELL).setResolution(Issue.RESOLUTION_WONT_FIX).setInLeak(true).setCount(4),
711       newGroup(RuleType.BUG).setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setInLeak(true).setCount(30),
712       newGroup(RuleType.BUG).setResolution(Issue.RESOLUTION_WONT_FIX).setInLeak(true).setCount(40),
713       // not in leak
714       newGroup(RuleType.CODE_SMELL).setResolution(Issue.RESOLUTION_WONT_FIX).setInLeak(false).setCount(5),
715       newGroup(RuleType.BUG).setResolution(Issue.RESOLUTION_WONT_FIX).setInLeak(false).setCount(50))
716       .assertThatLeakValueIs(CoreMetrics.NEW_ACCEPTED_ISSUES, 4 + 40);
717   }
718
719   @Test
720   public void test_new_technical_debt() {
721     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0);
722
723     with(
724       newGroup(RuleType.CODE_SMELL).setEffort(3.0).setInLeak(true),
725       // not in leak
726       newGroup(RuleType.CODE_SMELL).setEffort(5.0).setInLeak(false),
727       // not code smells
728       newGroup(RuleType.SECURITY_HOTSPOT).setEffort(9.0).setInLeak(true),
729       newGroup(RuleType.BUG).setEffort(7.0).setInLeak(true),
730       // exclude resolved
731       newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0).setInLeak(true))
732       .assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 3.0);
733   }
734
735   @Test
736   public void test_new_reliability_remediation_effort() {
737     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 0.0);
738
739     with(
740       newGroup(RuleType.BUG).setEffort(3.0).setInLeak(true),
741       // not in leak
742       newGroup(RuleType.BUG).setEffort(5.0).setInLeak(false),
743       // not bugs
744       newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
745       // exclude resolved
746       newResolvedGroup(RuleType.BUG).setEffort(17.0).setInLeak(true))
747       .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 3.0);
748   }
749
750   @Test
751   public void test_new_security_remediation_effort() {
752     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 0.0);
753
754     with(
755       newGroup(RuleType.VULNERABILITY).setEffort(3.0).setInLeak(true),
756       // not in leak
757       newGroup(RuleType.VULNERABILITY).setEffort(5.0).setInLeak(false),
758       // not vulnerability
759       newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
760       // exclude resolved
761       newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0).setInLeak(true))
762       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 3.0);
763   }
764
765   @Test
766   public void test_new_reliability_rating() {
767     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.A);
768
769     with(
770       newGroup(RuleType.BUG).setSeverity(Severity.INFO).setCount(3).setInLeak(true),
771       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setCount(1).setInLeak(true),
772       // not in leak
773       newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(false),
774       // not bug
775       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
776       // exclude resolved
777       newResolvedGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(true))
778       // highest severity of bugs on leak period is minor -> B
779       .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.B);
780   }
781
782   @Test
783   public void test_new_security_rating() {
784     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.A);
785
786     with(
787       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setCount(3).setInLeak(true),
788       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setCount(1).setInLeak(true),
789       // not in leak
790       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(false),
791       // not vulnerability
792       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
793       // exclude resolved
794       newResolvedGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(true))
795       // highest severity of bugs on leak period is minor -> B
796       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.B);
797   }
798
799   @Test
800   public void test_new_security_review_rating() {
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).setSeverity(Issue.STATUS_TO_REVIEW).setInLeak(false))
806       .assertThatLeakValueIs(NEW_SECURITY_REVIEW_RATING, Rating.B);
807
808     withNoIssues()
809       .assertThatLeakValueIs(NEW_SECURITY_REVIEW_RATING, Rating.A);
810   }
811
812   @Test
813   public void test_new_security_hotspots_reviewed() {
814     with(
815       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
816       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
817       // not in leak
818       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
819       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED, 75.0);
820
821     withNoIssues()
822       .assertNoLeakValue(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED);
823   }
824
825   @Test
826   public void test_new_security_hotspots_reviewed_status() {
827     with(
828       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
829       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
830       // not in leak
831       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
832       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 3.0);
833
834     withNoIssues()
835       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 0.0);
836   }
837
838   @Test
839   public void test_new_security_hotspots_to_review_status() {
840     with(
841       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
842       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
843       // not in leak
844       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
845       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1.0);
846
847     withNoIssues()
848       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 0.0);
849   }
850
851   @Test
852   public void test_new_sqale_debt_ratio_and_new_maintainability_rating() {
853     withNoIssues()
854       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
855       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
856
857     // technical_debt not computed
858     with(CoreMetrics.NEW_DEVELOPMENT_COST, 0)
859       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
860       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
861     with(CoreMetrics.NEW_DEVELOPMENT_COST, 20)
862       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
863       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
864
865     // development_cost not computed
866     with(CoreMetrics.NEW_TECHNICAL_DEBT, 0)
867       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
868       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
869     with(CoreMetrics.NEW_TECHNICAL_DEBT, 20)
870       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
871       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
872
873     // input measures are available
874     with(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
875       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
876       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
877       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
878
879     with(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
880       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 160.0)
881       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 12.5)
882       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.C);
883
884     with(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
885       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 10.0D)
886       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 200.0)
887       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.E);
888
889     // A is 5% --> min debt is exactly 200*0.05=10
890     with(CoreMetrics.NEW_DEVELOPMENT_COST, 200.0)
891       .and(CoreMetrics.NEW_TECHNICAL_DEBT, 10.0)
892       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 5.0)
893       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
894
895     with(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0)
896       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
897       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
898       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
899
900     with(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0)
901       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 80.0)
902       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0);
903
904     with(CoreMetrics.NEW_TECHNICAL_DEBT, -20.0)
905       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
906       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
907       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
908
909     // bug, debt can't be negative
910     with(CoreMetrics.NEW_TECHNICAL_DEBT, -20.0)
911       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 80.0)
912       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
913       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
914
915     // bug, cost can't be negative
916     with(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
917       .and(CoreMetrics.NEW_DEVELOPMENT_COST, -80.0)
918       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
919       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
920   }
921
922   private Verifier with(IssueGroupDto... groups) {
923     return new Verifier(groups);
924   }
925
926   private Verifier withNoIssues() {
927     return new Verifier(new IssueGroupDto[0]);
928   }
929
930   private Verifier with(Metric metric, double value) {
931     return new Verifier(new IssueGroupDto[0]).and(metric, value);
932   }
933
934   private Verifier with(Metric metric, String value) {
935     return new Verifier(new IssueGroupDto[0]).andText(metric, value);
936   }
937
938   private class Verifier {
939     private final IssueGroupDto[] groups;
940     private final InitialValues initialValues = new InitialValues();
941
942     private Verifier(IssueGroupDto[] groups) {
943       this.groups = groups;
944     }
945
946     Verifier and(Metric metric, double value) {
947       this.initialValues.values.put(metric, value);
948       return this;
949     }
950
951     Verifier andText(Metric metric, String value) {
952       this.initialValues.text.put(metric, value);
953       return this;
954     }
955
956     Verifier assertThatValueIs(Metric metric, double expectedValue) {
957       TestContext context = run(metric, false);
958       assertThat(context.doubleValue).isNotNull().isEqualTo(expectedValue);
959       return this;
960     }
961
962     Verifier assertThatLeakValueIs(Metric metric, double expectedValue) {
963       TestContext context = run(metric, true);
964       assertThat(context.doubleValue).isNotNull().isEqualTo(expectedValue);
965       return this;
966     }
967
968     Verifier assertThatLeakValueIs(Metric metric, Rating expectedRating) {
969       TestContext context = run(metric, true);
970       assertThat(context.ratingValue).isNotNull().isEqualTo(expectedRating);
971       return this;
972     }
973
974     Verifier assertNoLeakValue(Metric metric) {
975       TestContext context = run(metric, true);
976       assertThat(context.ratingValue).isNull();
977       return this;
978     }
979
980     Verifier assertThatValueIs(Metric metric, Rating expectedValue) {
981       TestContext context = run(metric, false);
982       assertThat(context.ratingValue).isNotNull().isEqualTo(expectedValue);
983       return this;
984     }
985
986     Verifier assertNoValue(Metric metric) {
987       TestContext context = run(metric, false);
988       assertThat(context.ratingValue).isNull();
989       return this;
990     }
991
992     private TestContext run(Metric metric, boolean expectLeakFormula) {
993       MeasureUpdateFormula formula = underTest.getFormulas().stream()
994         .filter(f -> f.getMetric().getKey().equals(metric.getKey()))
995         .findFirst()
996         .get();
997       assertThat(formula.isOnLeak()).isEqualTo(expectLeakFormula);
998       TestContext context = new TestContext(formula.getDependentMetrics(), initialValues);
999       formula.compute(context, newIssueCounter(groups));
1000       return context;
1001     }
1002   }
1003
1004   private static IssueCounter newIssueCounter(IssueGroupDto... issues) {
1005     return new IssueCounter(asList(issues));
1006   }
1007
1008   private static IssueGroupDto newGroup() {
1009     return newGroup(RuleType.CODE_SMELL);
1010   }
1011
1012   private static IssueGroupDto newGroup(RuleType ruleType) {
1013     IssueGroupDto dto = new IssueGroupDto();
1014     // set non-null fields
1015     dto.setRuleType(ruleType.getDbConstant());
1016     dto.setCount(1);
1017     dto.setEffort(0.0);
1018     dto.setSeverity(Severity.INFO);
1019     dto.setStatus(Issue.STATUS_OPEN);
1020     dto.setInLeak(false);
1021     return dto;
1022   }
1023
1024   private static IssueGroupDto newResolvedGroup(RuleType ruleType) {
1025     return newGroup(ruleType).setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setStatus(Issue.STATUS_CLOSED);
1026   }
1027
1028   private static IssueGroupDto newResolvedGroup(String resolution, String status) {
1029     return newGroup().setResolution(resolution).setStatus(status);
1030   }
1031
1032   private static class TestContext implements MeasureUpdateFormula.Context {
1033     private final Set<Metric> dependentMetrics;
1034     private final InitialValues initialValues;
1035     private Double doubleValue;
1036     private Rating ratingValue;
1037
1038     private TestContext(Collection<Metric> dependentMetrics, InitialValues initialValues) {
1039       this.dependentMetrics = new HashSet<>(dependentMetrics);
1040       this.initialValues = initialValues;
1041     }
1042
1043     @Override
1044     public List<Double> getChildrenValues() {
1045       return initialValues.childrenValues;
1046     }
1047
1048     @Override
1049     public long getChildrenHotspotsReviewed() {
1050       return initialValues.childrenHotspotsReviewed;
1051     }
1052
1053     @Override
1054     public long getChildrenHotspotsToReview() {
1055       return initialValues.childrenHotspotsToReview;
1056     }
1057
1058     @Override
1059     public long getChildrenNewHotspotsReviewed() {
1060       return initialValues.childrenNewHotspotsReviewed;
1061     }
1062
1063     @Override
1064     public long getChildrenNewHotspotsToReview() {
1065       return initialValues.childrenNewHotspotsToReview;
1066     }
1067
1068     @Override
1069     public ComponentDto getComponent() {
1070       throw new UnsupportedOperationException();
1071     }
1072
1073     @Override
1074     public DebtRatingGrid getDebtRatingGrid() {
1075       return new DebtRatingGrid(new double[] {0.05, 0.1, 0.2, 0.5});
1076     }
1077
1078     @Override
1079     public Optional<Double> getValue(Metric metric) {
1080       if (!dependentMetrics.contains(metric)) {
1081         throw new IllegalStateException("Metric " + metric.getKey() + " is not declared as a dependency");
1082       }
1083       if (initialValues.values.containsKey(metric)) {
1084         return Optional.of(initialValues.values.get(metric));
1085       }
1086       return Optional.empty();
1087     }
1088
1089     @Override
1090     public Optional<String> getText(Metric metric) {
1091       if (initialValues.text.containsKey(metric)) {
1092         return Optional.of(initialValues.text.get(metric));
1093       }
1094       return Optional.empty();
1095     }
1096
1097     @Override
1098     public void setValue(double value) {
1099       this.doubleValue = value;
1100     }
1101
1102     @Override
1103     public void setValue(Rating value) {
1104       this.ratingValue = value;
1105     }
1106   }
1107
1108   private class InitialValues {
1109     private final Map<Metric, Double> values = new HashMap<>();
1110     private final List<Double> childrenValues = new ArrayList<>();
1111     private final Map<Metric, String> text = new HashMap<>();
1112     private long childrenHotspotsReviewed = 0;
1113     private long childrenNewHotspotsReviewed = 0;
1114     private long childrenHotspotsToReview = 0;
1115     private long childrenNewHotspotsToReview = 0;
1116
1117   }
1118
1119   private class HierarchyTester {
1120     private final Metric metric;
1121     private final InitialValues initialValues;
1122     private final MeasureUpdateFormula formula;
1123
1124     public HierarchyTester(Metric metric) {
1125       this.metric = metric;
1126       this.initialValues = new InitialValues();
1127       this.formula = underTest.getFormulas().stream().filter(f -> f.getMetric().equals(metric)).findAny().get();
1128     }
1129
1130     public HierarchyTester withValue(Metric metric, Double value) {
1131       this.initialValues.values.put(metric, value);
1132       return this;
1133     }
1134
1135     public HierarchyTester withChildrenHotspotsCounts(long childrenHotspotsReviewed, long childrenNewHotspotsReviewed, long childrenHotspotsToReview,
1136       long childrenNewHotspotsToReview) {
1137       this.initialValues.childrenHotspotsReviewed = childrenHotspotsReviewed;
1138       this.initialValues.childrenNewHotspotsReviewed = childrenNewHotspotsReviewed;
1139       this.initialValues.childrenHotspotsToReview = childrenHotspotsToReview;
1140       this.initialValues.childrenNewHotspotsToReview = childrenNewHotspotsToReview;
1141       return this;
1142     }
1143
1144     public HierarchyTester withValue(Double value) {
1145       return withValue(metric, value);
1146     }
1147
1148     public HierarchyTester withChildrenValues(Double... values) {
1149       this.initialValues.childrenValues.addAll(asList(values));
1150       return this;
1151     }
1152
1153     public HierarchyTester expectedResult(@Nullable Double expected) {
1154       TestContext ctx = run();
1155       assertThat(ctx.doubleValue).isEqualTo(expected);
1156       return this;
1157     }
1158
1159     public HierarchyTester expectedRating(@Nullable Rating rating) {
1160       TestContext ctx = run();
1161       assertThat(ctx.ratingValue).isEqualTo(rating);
1162       return this;
1163     }
1164
1165     private TestContext run() {
1166       List<Metric> deps = new LinkedList<>(formula.getDependentMetrics());
1167       deps.add(formula.getMetric());
1168       deps.addAll(initialValues.values.keySet());
1169       deps.addAll(initialValues.text.keySet());
1170       TestContext context = new TestContext(deps, initialValues);
1171       formula.computeHierarchy(context);
1172       return context;
1173     }
1174   }
1175 }