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