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