3 * Copyright (C) 2009-2020 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.measure.live;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.HashSet;
26 import java.util.Optional;
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;
41 import static java.util.Arrays.asList;
42 import static org.assertj.core.api.Assertions.assertThat;
44 public class IssueMetricFormulaFactoryImplTest {
46 public ExpectedException expectedException = ExpectedException.none();
48 private IssueMetricFormulaFactoryImpl underTest = new IssueMetricFormulaFactoryImpl();
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);
61 public void test_violations() {
62 withNoIssues().assertThatValueIs(CoreMetrics.VIOLATIONS, 0);
63 with(newGroup(), newGroup().setCount(4)).assertThatValueIs(CoreMetrics.VIOLATIONS, 5);
66 IssueGroupDto resolved = newResolvedGroup(Issue.RESOLUTION_FIXED, Issue.STATUS_RESOLVED);
67 with(newGroup(), newGroup(), resolved).assertThatValueIs(CoreMetrics.VIOLATIONS, 2);
69 // include issues on leak
70 IssueGroupDto onLeak = newGroup().setCount(11).setInLeak(true);
71 with(newGroup(), newGroup(), onLeak).assertThatValueIs(CoreMetrics.VIOLATIONS, 1 + 1 + 11);
75 public void test_bugs() {
76 withNoIssues().assertThatValueIs(CoreMetrics.BUGS, 0);
78 newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(3),
79 newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(5),
81 newResolvedGroup(RuleType.BUG).setCount(7),
83 newGroup(RuleType.CODE_SMELL).setCount(11))
84 .assertThatValueIs(CoreMetrics.BUGS, 3 + 5);
88 public void test_code_smells() {
89 withNoIssues().assertThatValueIs(CoreMetrics.CODE_SMELLS, 0);
91 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
92 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setCount(5),
94 newResolvedGroup(RuleType.CODE_SMELL).setCount(7),
96 newGroup(RuleType.BUG).setCount(11))
97 .assertThatValueIs(CoreMetrics.CODE_SMELLS, 3 + 5);
101 public void test_vulnerabilities() {
102 withNoIssues().assertThatValueIs(CoreMetrics.VULNERABILITIES, 0);
104 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setCount(3),
105 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(5),
107 newResolvedGroup(RuleType.VULNERABILITY).setCount(7),
108 // not vulnerabilities
109 newGroup(RuleType.BUG).setCount(11))
110 .assertThatValueIs(CoreMetrics.VULNERABILITIES, 3 + 5);
114 public void test_security_hotspots() {
115 withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 0);
117 newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.MAJOR).setCount(3),
118 newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.CRITICAL).setCount(5),
120 newResolvedGroup(RuleType.SECURITY_HOTSPOT).setCount(7),
122 newGroup(RuleType.BUG).setCount(11))
123 .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 3 + 5);
127 public void test_security_review_rating() {
129 newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
130 newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
131 .assertThatValueIs(CoreMetrics.SECURITY_REVIEW_RATING, Rating.B);
134 .assertThatValueIs(CoreMetrics.SECURITY_REVIEW_RATING, Rating.A);
138 public void test_security_hotspots_reviewed() {
140 newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
141 newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
142 .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED, 75.0);
145 .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED, 100.0);
149 public void count_unresolved_by_severity() {
151 .assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 0)
152 .assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 0)
153 .assertThatValueIs(CoreMetrics.MAJOR_VIOLATIONS, 0)
154 .assertThatValueIs(CoreMetrics.MINOR_VIOLATIONS, 0)
155 .assertThatValueIs(CoreMetrics.INFO_VIOLATIONS, 0);
158 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setCount(3),
159 newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(5),
160 newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(7),
161 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(11),
162 // exclude security hotspot
163 newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.CRITICAL).setCount(15),
165 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(13),
167 newResolvedGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setCount(17),
168 newResolvedGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(19),
169 newResolvedGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.INFO).setCount(21))
170 .assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 11 + 13)
171 .assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 7)
172 .assertThatValueIs(CoreMetrics.MAJOR_VIOLATIONS, 3 + 5)
173 .assertThatValueIs(CoreMetrics.MINOR_VIOLATIONS, 0)
174 .assertThatValueIs(CoreMetrics.INFO_VIOLATIONS, 0);
178 public void count_resolved() {
180 .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 0)
181 .assertThatValueIs(CoreMetrics.WONT_FIX_ISSUES, 0);
184 newResolvedGroup(Issue.RESOLUTION_FIXED, Issue.STATUS_RESOLVED).setCount(3),
185 newResolvedGroup(Issue.RESOLUTION_FALSE_POSITIVE, Issue.STATUS_CLOSED).setCount(5),
186 newResolvedGroup(Issue.RESOLUTION_WONT_FIX, Issue.STATUS_CLOSED).setSeverity(Severity.MAJOR).setCount(7),
187 newResolvedGroup(Issue.RESOLUTION_WONT_FIX, Issue.STATUS_CLOSED).setSeverity(Severity.BLOCKER).setCount(11),
188 newResolvedGroup(Issue.RESOLUTION_REMOVED, Issue.STATUS_CLOSED).setCount(13),
189 // exclude security hotspot
190 newResolvedGroup(Issue.RESOLUTION_WONT_FIX, Issue.STATUS_RESOLVED).setCount(15).setRuleType(RuleType.SECURITY_HOTSPOT.getDbConstant()),
191 // exclude unresolved
192 newGroup(RuleType.VULNERABILITY).setCount(17),
193 newGroup(RuleType.BUG).setCount(19))
194 .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 5)
195 .assertThatValueIs(CoreMetrics.WONT_FIX_ISSUES, 7 + 11);
199 public void count_by_status() {
201 .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 0)
202 .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 0)
203 .assertThatValueIs(CoreMetrics.REOPENED_ISSUES, 0);
206 newGroup().setStatus(Issue.STATUS_CONFIRMED).setSeverity(Severity.BLOCKER).setCount(3),
207 newGroup().setStatus(Issue.STATUS_CONFIRMED).setSeverity(Severity.INFO).setCount(5),
208 newGroup().setStatus(Issue.STATUS_REOPENED).setCount(7),
209 newGroup(RuleType.CODE_SMELL).setStatus(Issue.STATUS_OPEN).setCount(9),
210 newGroup(RuleType.BUG).setStatus(Issue.STATUS_OPEN).setCount(11),
211 // exclude security hotspot
212 newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN).setCount(12),
213 newResolvedGroup(Issue.RESOLUTION_FALSE_POSITIVE, Issue.STATUS_CLOSED).setCount(13))
214 .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 3 + 5)
215 .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 9 + 11)
216 .assertThatValueIs(CoreMetrics.REOPENED_ISSUES, 7);
220 public void test_technical_debt() {
221 withNoIssues().assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 0);
224 newGroup(RuleType.CODE_SMELL).setEffort(3.0).setInLeak(false),
225 newGroup(RuleType.CODE_SMELL).setEffort(5.0).setInLeak(true),
226 // exclude security hotspot
227 newGroup(RuleType.SECURITY_HOTSPOT).setEffort(9).setInLeak(true),
228 newGroup(RuleType.SECURITY_HOTSPOT).setEffort(11).setInLeak(false),
230 newGroup(RuleType.BUG).setEffort(7.0),
232 newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0))
233 .assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 3.0 + 5.0);
237 public void test_reliability_remediation_effort() {
238 withNoIssues().assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 0);
241 newGroup(RuleType.BUG).setEffort(3.0),
242 newGroup(RuleType.BUG).setEffort(5.0).setSeverity(Severity.BLOCKER),
244 newGroup(RuleType.CODE_SMELL).setEffort(7.0),
246 newResolvedGroup(RuleType.BUG).setEffort(17.0))
247 .assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 3.0 + 5.0);
251 public void test_security_remediation_effort() {
252 withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 0);
255 newGroup(RuleType.VULNERABILITY).setEffort(3.0),
256 newGroup(RuleType.VULNERABILITY).setEffort(5.0).setSeverity(Severity.BLOCKER),
258 newGroup(RuleType.CODE_SMELL).setEffort(7.0),
260 newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0))
261 .assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 3.0 + 5.0);
265 public void test_sqale_debt_ratio_and_sqale_rating() {
267 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
268 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
270 // technical_debt not computed
271 with(CoreMetrics.DEVELOPMENT_COST, 0)
272 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
273 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
274 with(CoreMetrics.DEVELOPMENT_COST, 20)
275 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
276 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
278 // development_cost not computed
279 with(CoreMetrics.TECHNICAL_DEBT, 0)
280 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
281 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
282 with(CoreMetrics.TECHNICAL_DEBT, 20)
283 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
284 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
286 // input measures are available
287 with(CoreMetrics.TECHNICAL_DEBT, 20.0)
288 .and(CoreMetrics.DEVELOPMENT_COST, 0.0)
289 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
290 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
292 with(CoreMetrics.TECHNICAL_DEBT, 20.0)
293 .and(CoreMetrics.DEVELOPMENT_COST, 160.0)
294 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 12.5)
295 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.C);
297 with(CoreMetrics.TECHNICAL_DEBT, 20.0)
298 .and(CoreMetrics.DEVELOPMENT_COST, 10.0)
299 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 200.0)
300 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.E);
302 // A is 5% --> min debt is exactly 200*0.05=10
303 with(CoreMetrics.DEVELOPMENT_COST, 200.0)
304 .and(CoreMetrics.TECHNICAL_DEBT, 10.0)
305 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 5.0)
306 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
308 with(CoreMetrics.TECHNICAL_DEBT, 0.0)
309 .and(CoreMetrics.DEVELOPMENT_COST, 0.0)
310 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
311 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
313 with(CoreMetrics.TECHNICAL_DEBT, 0.0)
314 .and(CoreMetrics.DEVELOPMENT_COST, 80.0)
315 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0);
317 with(CoreMetrics.TECHNICAL_DEBT, -20.0)
318 .and(CoreMetrics.DEVELOPMENT_COST, 0.0)
319 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
320 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
322 // bug, debt can't be negative
323 with(CoreMetrics.TECHNICAL_DEBT, -20.0)
324 .and(CoreMetrics.DEVELOPMENT_COST, 80.0)
325 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
326 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
328 // bug, cost can't be negative
329 with(CoreMetrics.TECHNICAL_DEBT, 20.0)
330 .and(CoreMetrics.DEVELOPMENT_COST, -80.0)
331 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0)
332 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
336 public void test_effort_to_reach_maintainability_rating_A() {
338 .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
340 // technical_debt not computed
341 with(CoreMetrics.DEVELOPMENT_COST, 0.0)
342 .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
343 with(CoreMetrics.DEVELOPMENT_COST, 20.0)
344 .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
346 // development_cost not computed
347 with(CoreMetrics.TECHNICAL_DEBT, 0.0)
348 .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
349 with(CoreMetrics.TECHNICAL_DEBT, 20.0)
350 // development cost is considered as zero, so the effort is to reach... zero
351 .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 20.0);
354 with(CoreMetrics.DEVELOPMENT_COST, 200.0)
355 .and(CoreMetrics.TECHNICAL_DEBT, 40.0)
356 // B is 5% --> goal is to reach 200*0.05=10 --> effort is 40-10=30
357 .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 40.0 - (200.0 * 0.05));
360 with(CoreMetrics.DEVELOPMENT_COST, 200.0)
361 .and(CoreMetrics.TECHNICAL_DEBT, 180.0)
362 // B is 5% --> goal is to reach 200*0.05=10 --> effort is 180-10=170
363 .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 180.0 - (200.0 * 0.05));
366 with(CoreMetrics.DEVELOPMENT_COST, 200.0)
367 .and(CoreMetrics.TECHNICAL_DEBT, 8.0)
368 // B is 5% --> goal is to reach 200*0.05=10 --> debt is already at 8 --> effort to reach A is zero
369 .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
371 // exactly lower range of B
372 with(CoreMetrics.DEVELOPMENT_COST, 200.0)
373 .and(CoreMetrics.TECHNICAL_DEBT, 10.0)
374 // B is 5% --> goal is to reach 200*0.05=10 --> debt is 10 --> effort to reach A is zero
375 // FIXME need zero to reach A but effective rating is B !
376 .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
380 public void test_reliability_rating() {
382 .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
385 newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(1),
386 newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setCount(5),
387 // excluded, not a bug
388 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(3))
389 // highest severity of bugs is CRITICAL --> D
390 .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.D);
393 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
394 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(5))
396 .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
400 public void test_security_rating() {
402 .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
405 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(1),
406 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setCount(5),
407 // excluded, not a vulnerability
408 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(3))
409 // highest severity of vulnerabilities is CRITICAL --> D
410 .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.D);
413 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
414 newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(5))
415 // no vulnerabilities --> A
416 .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
420 public void test_new_bugs() {
421 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 0.0);
424 newGroup(RuleType.BUG).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
425 newGroup(RuleType.BUG).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
426 newGroup(RuleType.BUG).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
428 newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(9),
429 newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
430 .assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 5 + 7);
434 public void test_new_code_smells() {
435 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 0.0);
438 newGroup(RuleType.CODE_SMELL).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
439 newGroup(RuleType.CODE_SMELL).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
440 newGroup(RuleType.CODE_SMELL).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
442 newGroup(RuleType.BUG).setInLeak(true).setCount(9),
443 newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
444 .assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 5 + 7);
448 public void test_new_vulnerabilities() {
449 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 0.0);
452 newGroup(RuleType.VULNERABILITY).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
453 newGroup(RuleType.VULNERABILITY).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
454 newGroup(RuleType.VULNERABILITY).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
455 // not vulnerabilities
456 newGroup(RuleType.BUG).setInLeak(true).setCount(9),
457 newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(11))
458 .assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 5 + 7);
462 public void test_new_security_hotspots() {
463 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 0.0);
466 newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(false).setSeverity(Severity.MAJOR).setCount(3),
467 newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(true).setSeverity(Severity.CRITICAL).setCount(5),
468 newGroup(RuleType.SECURITY_HOTSPOT).setInLeak(true).setSeverity(Severity.MINOR).setCount(7),
470 newGroup(RuleType.BUG).setInLeak(true).setCount(9),
471 newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(11))
472 .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 5 + 7);
476 public void test_new_violations() {
477 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 0.0);
480 newGroup(RuleType.BUG).setInLeak(true).setCount(5),
481 newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(7),
482 newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(9),
484 newGroup(RuleType.BUG).setInLeak(false).setCount(11),
485 newGroup(RuleType.CODE_SMELL).setInLeak(false).setCount(13),
486 newGroup(RuleType.VULNERABILITY).setInLeak(false).setCount(17))
487 .assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 5 + 7 + 9);
491 public void test_new_blocker_violations() {
493 .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 0.0);
496 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(3),
497 newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(5),
498 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(7),
500 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
502 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(false).setCount(11),
503 newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(false).setCount(13))
504 .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 3 + 5 + 7);
508 public void test_new_critical_violations() {
510 .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 0.0);
513 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(3),
514 newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(5),
515 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(7),
517 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(true).setCount(9),
519 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(false).setCount(11),
520 newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setInLeak(false).setCount(13))
521 .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 3 + 5 + 7);
525 public void test_new_major_violations() {
527 .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 0.0);
530 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(true).setCount(3),
531 newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setInLeak(true).setCount(5),
532 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setInLeak(true).setCount(7),
534 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
536 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(false).setCount(11),
537 newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setInLeak(false).setCount(13))
538 .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 3 + 5 + 7);
542 public void test_new_minor_violations() {
544 .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 0.0);
547 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MINOR).setInLeak(true).setCount(3),
548 newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setInLeak(true).setCount(5),
549 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setInLeak(true).setCount(7),
551 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
553 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MINOR).setInLeak(false).setCount(11),
554 newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setInLeak(false).setCount(13))
555 .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 3 + 5 + 7);
559 public void test_new_info_violations() {
561 .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 0.0);
564 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.INFO).setInLeak(true).setCount(3),
565 newGroup(RuleType.BUG).setSeverity(Severity.INFO).setInLeak(true).setCount(5),
566 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setInLeak(true).setCount(7),
568 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
570 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.INFO).setInLeak(false).setCount(11),
571 newGroup(RuleType.BUG).setSeverity(Severity.INFO).setInLeak(false).setCount(13))
572 .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 3 + 5 + 7);
576 public void test_new_technical_debt() {
577 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0);
580 newGroup(RuleType.CODE_SMELL).setEffort(3.0).setInLeak(true),
582 newGroup(RuleType.CODE_SMELL).setEffort(5.0).setInLeak(false),
584 newGroup(RuleType.SECURITY_HOTSPOT).setEffort(9.0).setInLeak(true),
585 newGroup(RuleType.BUG).setEffort(7.0).setInLeak(true),
587 newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0).setInLeak(true))
588 .assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 3.0);
592 public void test_new_reliability_remediation_effort() {
593 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 0.0);
596 newGroup(RuleType.BUG).setEffort(3.0).setInLeak(true),
598 newGroup(RuleType.BUG).setEffort(5.0).setInLeak(false),
600 newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
602 newResolvedGroup(RuleType.BUG).setEffort(17.0).setInLeak(true))
603 .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 3.0);
607 public void test_new_security_remediation_effort() {
608 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 0.0);
611 newGroup(RuleType.VULNERABILITY).setEffort(3.0).setInLeak(true),
613 newGroup(RuleType.VULNERABILITY).setEffort(5.0).setInLeak(false),
615 newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
617 newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0).setInLeak(true))
618 .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 3.0);
622 public void test_new_reliability_rating() {
623 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.A);
626 newGroup(RuleType.BUG).setSeverity(Severity.INFO).setCount(3).setInLeak(true),
627 newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setCount(1).setInLeak(true),
629 newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(false),
631 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
633 newResolvedGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(true))
634 // highest severity of bugs on leak period is minor -> B
635 .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.B);
639 public void test_new_security_rating() {
640 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.A);
643 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setCount(3).setInLeak(true),
644 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setCount(1).setInLeak(true),
646 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(false),
648 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
650 newResolvedGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(true))
651 // highest severity of bugs on leak period is minor -> B
652 .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.B);
656 public void test_new_security_review_rating() {
658 newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
659 newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
661 newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Issue.STATUS_TO_REVIEW).setInLeak(false))
662 .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REVIEW_RATING, Rating.B);
665 .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REVIEW_RATING, Rating.A);
669 public void test_new_security_hotspots_reviewed() {
671 newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
672 newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
674 newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
675 .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED, 75.0);
678 .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED, 100.0);
682 public void test_new_sqale_debt_ratio_and_new_maintainability_rating() {
684 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
685 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
687 // technical_debt not computed
688 withLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 0)
689 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
690 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
691 withLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 20)
692 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
693 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
695 // development_cost not computed
696 withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 0)
697 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
698 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
699 withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 20)
700 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
701 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
703 // input measures are available
704 withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
705 .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
706 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
707 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
709 withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
710 .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 160.0)
711 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 12.5)
712 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.C);
714 withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
715 .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 10.0)
716 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 200.0)
717 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.E);
719 // A is 5% --> min debt is exactly 200*0.05=10
720 withLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 200.0)
721 .andLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 10.0)
722 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 5.0)
723 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
725 withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0)
726 .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
727 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
728 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
730 withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0)
731 .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 80.0)
732 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0);
734 withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, -20.0)
735 .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 0.0)
736 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
737 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
739 // bug, debt can't be negative
740 withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, -20.0)
741 .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, 80.0)
742 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
743 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
745 // bug, cost can't be negative
746 withLeak(CoreMetrics.NEW_TECHNICAL_DEBT, 20.0)
747 .andLeak(CoreMetrics.NEW_DEVELOPMENT_COST, -80.0)
748 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0.0)
749 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
752 private Verifier with(IssueGroupDto... groups) {
753 return new Verifier(groups);
756 private Verifier withNoIssues() {
757 return new Verifier(new IssueGroupDto[0]);
760 private Verifier with(Metric metric, double value) {
761 return new Verifier(new IssueGroupDto[0]).and(metric, value);
764 private Verifier withLeak(Metric metric, double leakValue) {
765 return new Verifier(new IssueGroupDto[0]).andLeak(metric, leakValue);
768 private class Verifier {
769 private final IssueGroupDto[] groups;
770 private final Map<Metric, Double> values = new HashMap<>();
771 private final Map<Metric, Double> leakValues = new HashMap<>();
773 private Verifier(IssueGroupDto[] groups) {
774 this.groups = groups;
777 Verifier and(Metric metric, double value) {
778 this.values.put(metric, value);
782 Verifier andLeak(Metric metric, double value) {
783 this.leakValues.put(metric, value);
787 Verifier assertThatValueIs(Metric metric, double expectedValue) {
788 TestContext context = run(metric, false);
789 assertThat(context.doubleValue).isNotNull().isEqualTo(expectedValue);
793 Verifier assertThatLeakValueIs(Metric metric, double expectedValue) {
794 TestContext context = run(metric, true);
795 assertThat(context.doubleLeakValue).isNotNull().isEqualTo(expectedValue);
799 Verifier assertThatLeakValueIs(Metric metric, Rating expectedRating) {
800 TestContext context = run(metric, true);
801 assertThat(context.ratingLeakValue).isNotNull().isEqualTo(expectedRating);
805 Verifier assertThatValueIs(Metric metric, Rating expectedValue) {
806 TestContext context = run(metric, false);
807 assertThat(context.ratingValue).isNotNull().isEqualTo(expectedValue);
811 private TestContext run(Metric metric, boolean expectLeakFormula) {
812 IssueMetricFormula formula = underTest.getFormulas().stream()
813 .filter(f -> f.getMetric().getKey().equals(metric.getKey()))
816 assertThat(formula.isOnLeak()).isEqualTo(expectLeakFormula);
817 TestContext context = new TestContext(formula.getDependentMetrics(), values, leakValues);
818 formula.compute(context, newIssueCounter(groups));
823 private static IssueCounter newIssueCounter(IssueGroupDto... issues) {
824 return new IssueCounter(asList(issues));
827 private static IssueGroupDto newGroup() {
828 return newGroup(RuleType.CODE_SMELL);
831 private static IssueGroupDto newGroup(RuleType ruleType) {
832 IssueGroupDto dto = new IssueGroupDto();
833 // set non-null fields
834 dto.setRuleType(ruleType.getDbConstant());
837 dto.setSeverity(Severity.INFO);
838 dto.setStatus(Issue.STATUS_OPEN);
839 dto.setInLeak(false);
843 private static IssueGroupDto newResolvedGroup(RuleType ruleType) {
844 return newGroup(ruleType).setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setStatus(Issue.STATUS_CLOSED);
847 private static IssueGroupDto newResolvedGroup(String resolution, String status) {
848 return newGroup().setResolution(resolution).setStatus(status);
851 private static class TestContext implements IssueMetricFormula.Context {
852 private final Set<Metric> dependentMetrics;
853 private Double doubleValue;
854 private Rating ratingValue;
855 private Double doubleLeakValue;
856 private Rating ratingLeakValue;
857 private final Map<Metric, Double> values;
858 private final Map<Metric, Double> leakValues;
860 private TestContext(Collection<Metric> dependentMetrics, Map<Metric, Double> values, Map<Metric, Double> leakValues) {
861 this.dependentMetrics = new HashSet<>(dependentMetrics);
862 this.values = values;
863 this.leakValues = leakValues;
867 public ComponentDto getComponent() {
868 throw new UnsupportedOperationException();
872 public DebtRatingGrid getDebtRatingGrid() {
873 return new DebtRatingGrid(new double[] {0.05, 0.1, 0.2, 0.5});
877 public Optional<Double> getValue(Metric metric) {
878 if (!dependentMetrics.contains(metric)) {
879 throw new IllegalStateException("Metric " + metric.getKey() + " is not declared as a dependency");
881 if (values.containsKey(metric)) {
882 return Optional.of(values.get(metric));
884 return Optional.empty();
888 public Optional<Double> getLeakValue(Metric metric) {
889 if (!dependentMetrics.contains(metric)) {
890 throw new IllegalStateException("Metric " + metric.getKey() + " is not declared as a dependency");
892 if (leakValues.containsKey(metric)) {
893 return Optional.of(leakValues.get(metric));
895 return Optional.empty();
899 public void setValue(double value) {
900 this.doubleValue = value;
904 public void setValue(Rating value) {
905 this.ratingValue = value;
909 public void setLeakValue(double value) {
910 this.doubleLeakValue = value;
914 public void setLeakValue(Rating value) {
915 this.ratingLeakValue = value;