]> source.dussan.org Git - sonarqube.git/blob
7a916eb75328f5f03c79579ce278af206ed3207f
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2019 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.Collection;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Map;
26 import java.util.Optional;
27 import java.util.Set;
28 import org.junit.Rule;
29 import org.junit.Test;
30 import org.junit.rules.ExpectedException;
31 import org.sonar.api.issue.Issue;
32 import org.sonar.api.measures.CoreMetrics;
33 import org.sonar.api.measures.Metric;
34 import org.sonar.api.rule.Severity;
35 import org.sonar.api.rules.RuleType;
36 import org.sonar.db.component.ComponentDto;
37 import org.sonar.db.issue.IssueGroupDto;
38 import org.sonar.server.measure.DebtRatingGrid;
39 import org.sonar.server.measure.Rating;
40
41 import static java.util.Arrays.asList;
42 import static org.assertj.core.api.Assertions.assertThat;
43
44 public class IssueMetricFormulaFactoryImplTest {
45   @Rule
46   public ExpectedException expectedException = ExpectedException.none();
47
48   private IssueMetricFormulaFactoryImpl underTest = new IssueMetricFormulaFactoryImpl();
49
50   @Test
51   public void getFormulaMetrics_include_the_dependent_metrics() {
52     for (IssueMetricFormula formula : underTest.getFormulas()) {
53       assertThat(underTest.getFormulaMetrics()).contains(formula.getMetric());
54       for (Metric dependentMetric : formula.getDependentMetrics()) {
55         assertThat(underTest.getFormulaMetrics()).contains(dependentMetric);
56       }
57     }
58   }
59
60   @Test
61   public void test_violations() {
62     withNoIssues().assertThatValueIs(CoreMetrics.VIOLATIONS, 0);
63     with(newGroup(), newGroup().setCount(4)).assertThatValueIs(CoreMetrics.VIOLATIONS, 5);
64
65     // exclude resolved
66     IssueGroupDto resolved = newResolvedGroup(Issue.RESOLUTION_FIXED, Issue.STATUS_RESOLVED);
67     with(newGroup(), newGroup(), resolved).assertThatValueIs(CoreMetrics.VIOLATIONS, 2);
68
69     // include issues on leak
70     IssueGroupDto onLeak = newGroup().setCount(11).setInLeak(true);
71     with(newGroup(), newGroup(), onLeak).assertThatValueIs(CoreMetrics.VIOLATIONS, 1 + 1 + 11);
72   }
73
74   @Test
75   public void test_bugs() {
76     withNoIssues().assertThatValueIs(CoreMetrics.BUGS, 0);
77     with(
78       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(3),
79       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(5),
80       // exclude resolved
81       newResolvedGroup(RuleType.BUG).setCount(7),
82       // not bugs
83       newGroup(RuleType.CODE_SMELL).setCount(11))
84         .assertThatValueIs(CoreMetrics.BUGS, 3 + 5);
85   }
86
87   @Test
88   public void test_code_smells() {
89     withNoIssues().assertThatValueIs(CoreMetrics.CODE_SMELLS, 0);
90     with(
91       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
92       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setCount(5),
93       // exclude resolved
94       newResolvedGroup(RuleType.CODE_SMELL).setCount(7),
95       // not code smells
96       newGroup(RuleType.BUG).setCount(11))
97         .assertThatValueIs(CoreMetrics.CODE_SMELLS, 3 + 5);
98   }
99
100   @Test
101   public void test_vulnerabilities() {
102     withNoIssues().assertThatValueIs(CoreMetrics.VULNERABILITIES, 0);
103     with(
104       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setCount(3),
105       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(5),
106       // exclude resolved
107       newResolvedGroup(RuleType.VULNERABILITY).setCount(7),
108       // not vulnerabilities
109       newGroup(RuleType.BUG).setCount(11))
110         .assertThatValueIs(CoreMetrics.VULNERABILITIES, 3 + 5);
111   }
112
113   @Test
114   public void test_security_hotspots() {
115     withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 0);
116     with(
117       newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.MAJOR).setCount(3),
118       newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.CRITICAL).setCount(5),
119       // exclude resolved
120       newResolvedGroup(RuleType.SECURITY_HOTSPOT).setCount(7),
121       // not hotspots
122       newGroup(RuleType.BUG).setCount(11))
123       .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 3 + 5);
124   }
125
126   @Test
127   public void count_unresolved_by_severity() {
128     withNoIssues()
129       .assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 0)
130       .assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 0)
131       .assertThatValueIs(CoreMetrics.MAJOR_VIOLATIONS, 0)
132       .assertThatValueIs(CoreMetrics.MINOR_VIOLATIONS, 0)
133       .assertThatValueIs(CoreMetrics.INFO_VIOLATIONS, 0);
134
135     with(
136       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setCount(3),
137       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(5),
138       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(7),
139       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(11),
140       // exclude security hotspot
141       newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.CRITICAL).setCount(15),
142       // include leak
143       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(13),
144       // exclude resolved
145       newResolvedGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setCount(17),
146       newResolvedGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(19),
147       newResolvedGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.INFO).setCount(21))
148         .assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 11 + 13)
149         .assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 7)
150         .assertThatValueIs(CoreMetrics.MAJOR_VIOLATIONS, 3 + 5)
151         .assertThatValueIs(CoreMetrics.MINOR_VIOLATIONS, 0)
152         .assertThatValueIs(CoreMetrics.INFO_VIOLATIONS, 0);
153   }
154
155   @Test
156   public void count_resolved() {
157     withNoIssues()
158       .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 0)
159       .assertThatValueIs(CoreMetrics.WONT_FIX_ISSUES, 0);
160
161     with(
162       newResolvedGroup(Issue.RESOLUTION_FIXED, Issue.STATUS_RESOLVED).setCount(3),
163       newResolvedGroup(Issue.RESOLUTION_FALSE_POSITIVE, Issue.STATUS_CLOSED).setCount(5),
164       newResolvedGroup(Issue.RESOLUTION_WONT_FIX, Issue.STATUS_CLOSED).setSeverity(Severity.MAJOR).setCount(7),
165       newResolvedGroup(Issue.RESOLUTION_WONT_FIX, Issue.STATUS_CLOSED).setSeverity(Severity.BLOCKER).setCount(11),
166       newResolvedGroup(Issue.RESOLUTION_REMOVED, Issue.STATUS_CLOSED).setCount(13),
167       // exclude security hotspot
168       newResolvedGroup(Issue.RESOLUTION_WONT_FIX, Issue.STATUS_RESOLVED).setCount(15).setRuleType(RuleType.SECURITY_HOTSPOT.getDbConstant()),
169       // exclude unresolved
170       newGroup(RuleType.VULNERABILITY).setCount(17),
171       newGroup(RuleType.BUG).setCount(19))
172         .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 5)
173         .assertThatValueIs(CoreMetrics.WONT_FIX_ISSUES, 7 + 11);
174   }
175
176   @Test
177   public void count_by_status() {
178     withNoIssues()
179       .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 0)
180       .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 0)
181       .assertThatValueIs(CoreMetrics.REOPENED_ISSUES, 0);
182
183     with(
184       newGroup().setStatus(Issue.STATUS_CONFIRMED).setSeverity(Severity.BLOCKER).setCount(3),
185       newGroup().setStatus(Issue.STATUS_CONFIRMED).setSeverity(Severity.INFO).setCount(5),
186       newGroup().setStatus(Issue.STATUS_REOPENED).setCount(7),
187       newGroup(RuleType.CODE_SMELL).setStatus(Issue.STATUS_OPEN).setCount(9),
188       newGroup(RuleType.BUG).setStatus(Issue.STATUS_OPEN).setCount(11),
189       // exclude security hotspot
190       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN).setCount(12),
191       newResolvedGroup(Issue.RESOLUTION_FALSE_POSITIVE, Issue.STATUS_CLOSED).setCount(13))
192         .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 3 + 5)
193         .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 9 + 11)
194         .assertThatValueIs(CoreMetrics.REOPENED_ISSUES, 7);
195   }
196
197   @Test
198   public void test_technical_debt() {
199     withNoIssues().assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 0);
200
201     with(
202       newGroup(RuleType.CODE_SMELL).setEffort(3.0).setInLeak(false),
203       newGroup(RuleType.CODE_SMELL).setEffort(5.0).setInLeak(true),
204       // exclude security hotspot
205       newGroup(RuleType.SECURITY_HOTSPOT).setEffort(9).setInLeak(true),
206       newGroup(RuleType.SECURITY_HOTSPOT).setEffort(11).setInLeak(false),
207       // not code smells
208       newGroup(RuleType.BUG).setEffort(7.0),
209       // exclude resolved
210       newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0))
211         .assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 3.0 + 5.0);
212   }
213
214   @Test
215   public void test_reliability_remediation_effort() {
216     withNoIssues().assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 0);
217
218     with(
219       newGroup(RuleType.BUG).setEffort(3.0),
220       newGroup(RuleType.BUG).setEffort(5.0).setSeverity(Severity.BLOCKER),
221       // not bugs
222       newGroup(RuleType.CODE_SMELL).setEffort(7.0),
223       // exclude resolved
224       newResolvedGroup(RuleType.BUG).setEffort(17.0))
225         .assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 3.0 + 5.0);
226   }
227
228   @Test
229   public void test_security_remediation_effort() {
230     withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 0);
231
232     with(
233       newGroup(RuleType.VULNERABILITY).setEffort(3.0),
234       newGroup(RuleType.VULNERABILITY).setEffort(5.0).setSeverity(Severity.BLOCKER),
235       // not vulnerability
236       newGroup(RuleType.CODE_SMELL).setEffort(7.0),
237       // exclude resolved
238       newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0))
239         .assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 3.0 + 5.0);
240   }
241
242   @Test
243   public void test_sqale_debt_ratio_and_sqale_rating() {
244     withNoIssues()
245       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
246       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
247
248     // technical_debt not computed
249     with(CoreMetrics.DEVELOPMENT_COST, 0)
250       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
251       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
252     with(CoreMetrics.DEVELOPMENT_COST, 20)
253       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
254       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
255
256     // development_cost not computed
257     with(CoreMetrics.TECHNICAL_DEBT, 0)
258       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
259       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
260     with(CoreMetrics.TECHNICAL_DEBT, 20)
261       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
262       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
263
264     // input measures are available
265     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
266       .and(CoreMetrics.DEVELOPMENT_COST, 0.0)
267       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
268       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
269
270     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
271       .and(CoreMetrics.DEVELOPMENT_COST, 160.0)
272       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 12.5)
273       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.C);
274
275     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
276       .and(CoreMetrics.DEVELOPMENT_COST, 10.0)
277       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 200.0)
278       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.E);
279
280     // A is 5% --> min debt is exactly 200*0.05=10
281     with(CoreMetrics.DEVELOPMENT_COST, 200.0)
282       .and(CoreMetrics.TECHNICAL_DEBT, 10.0)
283       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 5.0)
284       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
285
286     with(CoreMetrics.TECHNICAL_DEBT, 0.0)
287       .and(CoreMetrics.DEVELOPMENT_COST, 0.0)
288       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
289       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
290
291     with(CoreMetrics.TECHNICAL_DEBT, 0.0)
292       .and(CoreMetrics.DEVELOPMENT_COST, 80.0)
293       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0);
294
295     with(CoreMetrics.TECHNICAL_DEBT, -20.0)
296       .and(CoreMetrics.DEVELOPMENT_COST, 0.0)
297       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
298       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
299
300     // bug, debt can't be negative
301     with(CoreMetrics.TECHNICAL_DEBT, -20.0)
302       .and(CoreMetrics.DEVELOPMENT_COST, 80.0)
303       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
304       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
305
306     // bug, cost can't be negative
307     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
308       .and(CoreMetrics.DEVELOPMENT_COST, -80.0)
309       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
310       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
311   }
312
313   @Test
314   public void test_effort_to_reach_maintainability_rating_A() {
315     withNoIssues()
316       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
317
318     // technical_debt not computed
319     with(CoreMetrics.DEVELOPMENT_COST, 0.0)
320       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
321     with(CoreMetrics.DEVELOPMENT_COST, 20.0)
322       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
323
324     // development_cost not computed
325     with(CoreMetrics.TECHNICAL_DEBT, 0.0)
326       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
327     with(CoreMetrics.TECHNICAL_DEBT, 20.0)
328       // development cost is considered as zero, so the effort is to reach... zero
329       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 20.0);
330
331     // B to A
332     with(CoreMetrics.DEVELOPMENT_COST, 200.0)
333       .and(CoreMetrics.TECHNICAL_DEBT, 40.0)
334       // B is 5% --> goal is to reach 200*0.05=10 --> effort is 40-10=30
335       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 40.0 - (200.0 * 0.05));
336
337     // E to A
338     with(CoreMetrics.DEVELOPMENT_COST, 200.0)
339       .and(CoreMetrics.TECHNICAL_DEBT, 180.0)
340       // B is 5% --> goal is to reach 200*0.05=10 --> effort is 180-10=170
341       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 180.0 - (200.0 * 0.05));
342
343     // already A
344     with(CoreMetrics.DEVELOPMENT_COST, 200.0)
345       .and(CoreMetrics.TECHNICAL_DEBT, 8.0)
346       // B is 5% --> goal is to reach 200*0.05=10 --> debt is already at 8 --> effort to reach A is zero
347       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
348
349     // exactly lower range of B
350     with(CoreMetrics.DEVELOPMENT_COST, 200.0)
351       .and(CoreMetrics.TECHNICAL_DEBT, 10.0)
352       // B is 5% --> goal is to reach 200*0.05=10 --> debt is 10 --> effort to reach A is zero
353       // FIXME need zero to reach A but effective rating is B !
354       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
355   }
356
357   @Test
358   public void test_reliability_rating() {
359     withNoIssues()
360       .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
361
362     with(
363       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(1),
364       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setCount(5),
365       // excluded, not a bug
366       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(3))
367         // highest severity of bugs is CRITICAL --> D
368         .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.D);
369
370     with(
371       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
372       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(5))
373         // no bugs --> A
374         .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
375   }
376
377   @Test
378   public void test_security_rating() {
379     withNoIssues()
380       .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
381
382     with(
383       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(1),
384       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setCount(5),
385       // excluded, not a vulnerability
386       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(3))
387         // highest severity of vulnerabilities is CRITICAL --> D
388         .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.D);
389
390     with(
391       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
392       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(5))
393         // no vulnerabilities --> A
394         .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
395   }
396
397   @Test
398   public void test_new_bugs() {
399     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 0.0);
400
401     with(
402       newGroup(RuleType.BUG).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
403       newGroup(RuleType.BUG).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
404       newGroup(RuleType.BUG).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
405       // not bugs
406       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(9),
407       newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
408         .assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 5 + 7);
409   }
410
411   @Test
412   public void test_new_code_smells() {
413     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 0.0);
414
415     with(
416       newGroup(RuleType.CODE_SMELL).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
417       newGroup(RuleType.CODE_SMELL).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
418       newGroup(RuleType.CODE_SMELL).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
419       // not code smells
420       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
421       newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
422         .assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 5 + 7);
423   }
424
425   @Test
426   public void test_new_vulnerabilities() {
427     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 0.0);
428
429     with(
430       newGroup(RuleType.VULNERABILITY).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
431       newGroup(RuleType.VULNERABILITY).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
432       newGroup(RuleType.VULNERABILITY).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
433       // not vulnerabilities
434       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
435       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(11))
436         .assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 5 + 7);
437   }
438
439   @Test
440   public void test_new_security_hotspots() {
441     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 0.0);
442
443     with(
444       newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
445       newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
446       newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
447       // not hotspots
448       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
449       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(11))
450         .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 5 + 7);
451   }
452
453   @Test
454   public void test_new_violations() {
455     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 0.0);
456
457     with(
458       newGroup(RuleType.BUG).setInLeak(true).setCount(5),
459       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(7),
460       newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(9),
461       // not in leak
462       newGroup(RuleType.BUG).setInLeak(false).setCount(11),
463       newGroup(RuleType.CODE_SMELL).setInLeak(false).setCount(13),
464       newGroup(RuleType.VULNERABILITY).setInLeak(false).setCount(17))
465         .assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 5 + 7 + 9);
466   }
467
468   @Test
469   public void test_new_blocker_violations() {
470     withNoIssues()
471       .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 0.0);
472
473     with(
474       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(3),
475       newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(5),
476       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(7),
477       // not blocker
478       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
479       // not in leak
480       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(false).setCount(11),
481       newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(false).setCount(13))
482         .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 3 + 5 + 7);
483   }
484
485   @Test
486   public void test_new_critical_violations() {
487     withNoIssues()
488       .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 0.0);
489
490     with(
491       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(3),
492       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(5),
493       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(7),
494       // not CRITICAL
495       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(true).setCount(9),
496       // not in leak
497       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(false).setCount(11),
498       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setInLeak(false).setCount(13))
499         .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 3 + 5 + 7);
500   }
501
502   @Test
503   public void test_new_major_violations() {
504     withNoIssues()
505       .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 0.0);
506
507     with(
508       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(true).setCount(3),
509       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setInLeak(true).setCount(5),
510       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setInLeak(true).setCount(7),
511       // not MAJOR
512       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
513       // not in leak
514       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(false).setCount(11),
515       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setInLeak(false).setCount(13))
516         .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 3 + 5 + 7);
517   }
518
519   @Test
520   public void test_new_minor_violations() {
521     withNoIssues()
522       .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 0.0);
523
524     with(
525       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MINOR).setInLeak(true).setCount(3),
526       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setInLeak(true).setCount(5),
527       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setInLeak(true).setCount(7),
528       // not MINOR
529       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
530       // not in leak
531       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MINOR).setInLeak(false).setCount(11),
532       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setInLeak(false).setCount(13))
533         .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 3 + 5 + 7);
534   }
535
536   @Test
537   public void test_new_info_violations() {
538     withNoIssues()
539       .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 0.0);
540
541     with(
542       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.INFO).setInLeak(true).setCount(3),
543       newGroup(RuleType.BUG).setSeverity(Severity.INFO).setInLeak(true).setCount(5),
544       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setInLeak(true).setCount(7),
545       // not INFO
546       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
547       // not in leak
548       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.INFO).setInLeak(false).setCount(11),
549       newGroup(RuleType.BUG).setSeverity(Severity.INFO).setInLeak(false).setCount(13))
550         .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 3 + 5 + 7);
551   }
552
553   @Test
554   public void test_new_technical_debt() {
555     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0);
556
557     with(
558       newGroup(RuleType.CODE_SMELL).setEffort(3.0).setInLeak(true),
559       // not in leak
560       newGroup(RuleType.CODE_SMELL).setEffort(5.0).setInLeak(false),
561       // not code smells
562       newGroup(RuleType.SECURITY_HOTSPOT).setEffort(9.0).setInLeak(true),
563       newGroup(RuleType.BUG).setEffort(7.0).setInLeak(true),
564       // exclude resolved
565       newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0).setInLeak(true))
566         .assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 3.0);
567   }
568
569   @Test
570   public void test_new_reliability_remediation_effort() {
571     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 0.0);
572
573     with(
574       newGroup(RuleType.BUG).setEffort(3.0).setInLeak(true),
575       // not in leak
576       newGroup(RuleType.BUG).setEffort(5.0).setInLeak(false),
577       // not bugs
578       newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
579       // exclude resolved
580       newResolvedGroup(RuleType.BUG).setEffort(17.0).setInLeak(true))
581         .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 3.0);
582   }
583
584   @Test
585   public void test_new_security_remediation_effort() {
586     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 0.0);
587
588     with(
589       newGroup(RuleType.VULNERABILITY).setEffort(3.0).setInLeak(true),
590       // not in leak
591       newGroup(RuleType.VULNERABILITY).setEffort(5.0).setInLeak(false),
592       // not vulnerability
593       newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
594       // exclude resolved
595       newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0).setInLeak(true))
596         .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 3.0);
597   }
598
599   @Test
600   public void test_new_reliability_rating() {
601     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.A);
602
603     with(
604       newGroup(RuleType.BUG).setSeverity(Severity.INFO).setCount(3).setInLeak(true),
605       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setCount(1).setInLeak(true),
606       // not in leak
607       newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(false),
608       // not bug
609       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
610       // exclude resolved
611       newResolvedGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(true))
612         // highest severity of bugs on leak period is minor -> B
613         .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.B);
614   }
615
616   @Test
617   public void test_new_security_rating() {
618     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.A);
619
620     with(
621       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setCount(3).setInLeak(true),
622       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setCount(1).setInLeak(true),
623       // not in leak
624       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(false),
625       // not vulnerability
626       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
627       // exclude resolved
628       newResolvedGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(true))
629         // highest severity of bugs on leak period is minor -> B
630         .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.B);
631   }
632
633   @Test
634   public void test_new_sqale_debt_ratio_and_new_maintainability_rating() {
635     withNoIssues()
636       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
637       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
638
639     // technical_debt not computed
640     withLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 0)
641       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
642       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
643     withLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 20)
644       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
645       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
646
647     // development_cost not computed
648     withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 0)
649       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
650       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
651     withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 20)
652       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
653       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
654
655     // input measures are available
656     withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
657       .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
658       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
659       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
660
661     withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
662       .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 160.0)
663       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 12.5)
664       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.C);
665
666     withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
667       .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 10.0)
668       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 200.0)
669       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.E);
670
671     // A is 5% --> min debt is exactly 200*0.05=10
672     withLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 200.0)
673       .andLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 10.0)
674       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 5.0)
675       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
676
677     withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0)
678       .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
679       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
680       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
681
682     withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0)
683       .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 80.0)
684       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0);
685
686     withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, -20.0)
687       .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
688       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
689       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
690
691     // bug, debt can't be negative
692     withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, -20.0)
693       .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 80.0)
694       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
695       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
696
697     // bug, cost can't be negative
698     withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
699       .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, -80.0)
700       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
701       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
702   }
703
704   private Verifier with(IssueGroupDto... groups) {
705     return new Verifier(groups);
706   }
707
708   private Verifier withNoIssues() {
709     return new Verifier(new IssueGroupDto[0]);
710   }
711
712   private Verifier with(Metric metric, double value) {
713     return new Verifier(new IssueGroupDto[0]).and(metric, value);
714   }
715
716   private Verifier withLeak(Metric metric, double leakValue) {
717     return new Verifier(new IssueGroupDto[0]).andLeak(metric, leakValue);
718   }
719
720   private class Verifier {
721     private final IssueGroupDto[] groups;
722     private final Map<Metric, Double> values = new HashMap<>();
723     private final Map<Metric, Double> leakValues = new HashMap<>();
724
725     private Verifier(IssueGroupDto[] groups) {
726       this.groups = groups;
727     }
728
729     Verifier and(Metric metric, double value) {
730       this.values.put(metric, value);
731       return this;
732     }
733
734     Verifier andLeak(Metric metric, double value) {
735       this.leakValues.put(metric, value);
736       return this;
737     }
738
739     Verifier assertThatValueIs(Metric metric, double expectedValue) {
740       TestContext context = run(metric, false);
741       assertThat(context.doubleValue).isNotNull().isEqualTo(expectedValue);
742       return this;
743     }
744
745     Verifier assertThatLeakValueIs(Metric metric, double expectedValue) {
746       TestContext context = run(metric, true);
747       assertThat(context.doubleLeakValue).isNotNull().isEqualTo(expectedValue);
748       return this;
749     }
750
751     Verifier assertThatLeakValueIs(Metric metric, Rating expectedRating) {
752       TestContext context = run(metric, true);
753       assertThat(context.ratingLeakValue).isNotNull().isEqualTo(expectedRating);
754       return this;
755     }
756
757     Verifier assertThatValueIs(Metric metric, Rating expectedValue) {
758       TestContext context = run(metric, false);
759       assertThat(context.ratingValue).isNotNull().isEqualTo(expectedValue);
760       return this;
761     }
762
763     private TestContext run(Metric metric, boolean expectLeakFormula) {
764       IssueMetricFormula formula = underTest.getFormulas().stream()
765         .filter(f -> f.getMetric().getKey().equals(metric.getKey()))
766         .findFirst()
767         .get();
768       assertThat(formula.isOnLeak()).isEqualTo(expectLeakFormula);
769       TestContext context = new TestContext(formula.getDependentMetrics(), values, leakValues);
770       formula.compute(context, newIssueCounter(groups));
771       return context;
772     }
773   }
774
775   private static IssueCounter newIssueCounter(IssueGroupDto... issues) {
776     return new IssueCounter(asList(issues));
777   }
778
779   private static IssueGroupDto newGroup() {
780     return newGroup(RuleType.CODE_SMELL);
781   }
782
783   private static IssueGroupDto newGroup(RuleType ruleType) {
784     IssueGroupDto dto = new IssueGroupDto();
785     // set non-null fields
786     dto.setRuleType(ruleType.getDbConstant());
787     dto.setCount(1);
788     dto.setEffort(0.0);
789     dto.setSeverity(Severity.INFO);
790     dto.setStatus(Issue.STATUS_OPEN);
791     dto.setInLeak(false);
792     return dto;
793   }
794
795   private static IssueGroupDto newResolvedGroup(RuleType ruleType) {
796     return newGroup(ruleType).setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setStatus(Issue.STATUS_CLOSED);
797   }
798
799   private static IssueGroupDto newResolvedGroup(String resolution, String status) {
800     return newGroup().setResolution(resolution).setStatus(status);
801   }
802
803   private static class TestContext implements IssueMetricFormula.Context {
804     private final Set<Metric> dependentMetrics;
805     private Double doubleValue;
806     private Rating ratingValue;
807     private Double doubleLeakValue;
808     private Rating ratingLeakValue;
809     private final Map<Metric, Double> values;
810     private final Map<Metric, Double> leakValues;
811
812     private TestContext(Collection<Metric> dependentMetrics, Map<Metric, Double> values, Map<Metric, Double> leakValues) {
813       this.dependentMetrics = new HashSet<>(dependentMetrics);
814       this.values = values;
815       this.leakValues = leakValues;
816     }
817
818     @Override
819     public ComponentDto getComponent() {
820       throw new UnsupportedOperationException();
821     }
822
823     @Override
824     public DebtRatingGrid getDebtRatingGrid() {
825       return new DebtRatingGrid(new double[] {0.05, 0.1, 0.2, 0.5});
826     }
827
828     @Override
829     public Optional<Double> getValue(Metric metric) {
830       if (!dependentMetrics.contains(metric)) {
831         throw new IllegalStateException("Metric " + metric.getKey() + " is not declared as a dependency");
832       }
833       if (values.containsKey(metric)) {
834         return Optional.of(values.get(metric));
835       }
836       return Optional.empty();
837     }
838
839     @Override
840     public Optional<Double> getLeakValue(Metric metric) {
841       if (!dependentMetrics.contains(metric)) {
842         throw new IllegalStateException("Metric " + metric.getKey() + " is not declared as a dependency");
843       }
844       if (leakValues.containsKey(metric)) {
845         return Optional.of(leakValues.get(metric));
846       }
847       return Optional.empty();
848     }
849
850     @Override
851     public void setValue(double value) {
852       this.doubleValue = value;
853     }
854
855     @Override
856     public void setValue(Rating value) {
857       this.ratingValue = value;
858     }
859
860     @Override
861     public void setLeakValue(double value) {
862       this.doubleLeakValue = value;
863     }
864
865     @Override
866     public void setLeakValue(Rating value) {
867       this.ratingLeakValue = value;
868     }
869   }
870 }