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