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