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