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