3 * Copyright (C) 2009-2018 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.computation.task.projectanalysis.qualitymodel.DebtRatingGrid;
39 import org.sonar.server.computation.task.projectanalysis.qualitymodel.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 count_unresolved_by_severity() {
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);
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),
128 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true).setCount(13),
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);
140 public void count_resolved() {
142 .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 0)
143 .assertThatValueIs(CoreMetrics.WONT_FIX_ISSUES, 0);
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);
159 public void count_by_status() {
161 .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 0)
162 .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 0)
163 .assertThatValueIs(CoreMetrics.REOPENED_ISSUES, 0);
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);
178 public void test_technical_debt() {
179 withNoIssues().assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 0);
182 newGroup(RuleType.CODE_SMELL).setEffort(3.0).setInLeak(false),
183 newGroup(RuleType.CODE_SMELL).setEffort(5.0).setInLeak(true),
185 newGroup(RuleType.BUG).setEffort(7.0),
187 newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0))
188 .assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 3.0 + 5.0);
192 public void test_reliability_remediation_effort() {
193 withNoIssues().assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 0);
196 newGroup(RuleType.BUG).setEffort(3.0),
197 newGroup(RuleType.BUG).setEffort(5.0).setSeverity(Severity.BLOCKER),
199 newGroup(RuleType.CODE_SMELL).setEffort(7.0),
201 newResolvedGroup(RuleType.BUG).setEffort(17.0))
202 .assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 3.0 + 5.0);
206 public void test_security_remediation_effort() {
207 withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 0);
210 newGroup(RuleType.VULNERABILITY).setEffort(3.0),
211 newGroup(RuleType.VULNERABILITY).setEffort(5.0).setSeverity(Severity.BLOCKER),
213 newGroup(RuleType.CODE_SMELL).setEffort(7.0),
215 newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0))
216 .assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 3.0 + 5.0);
220 public void test_sqale_debt_ratio_and_sqale_rating() {
222 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
223 .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
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);
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);
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);
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);
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);
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);
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);
268 with(CoreMetrics.TECHNICAL_DEBT, 0.0)
269 .and(CoreMetrics.DEVELOPMENT_COST, 80.0)
270 .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0.0);
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);
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);
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);
291 public void test_effort_to_reach_maintainability_rating_A() {
293 .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
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);
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);
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));
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));
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);
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);
335 public void test_reliability_rating() {
337 .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
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);
348 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
349 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(5))
351 .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
355 public void test_security_rating() {
357 .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
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);
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);
375 public void test_new_bugs() {
376 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 0.0);
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),
383 newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(9),
384 newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
385 .assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 5 + 7);
389 public void test_new_code_smells() {
390 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 0.0);
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),
397 newGroup(RuleType.BUG).setInLeak(true).setCount(9),
398 newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
399 .assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 5 + 7);
403 public void test_new_vulnerabilities() {
404 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 0.0);
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);
417 public void test_new_violations() {
418 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 0.0);
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),
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);
432 public void test_new_blocker_violations() {
434 .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 0.0);
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),
441 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
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);
449 public void test_new_critical_violations() {
451 .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 0.0);
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),
458 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(true).setCount(9),
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);
466 public void test_new_major_violations() {
468 .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 0.0);
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),
475 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
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);
483 public void test_new_minor_violations() {
485 .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 0.0);
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),
492 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
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);
500 public void test_new_info_violations() {
502 .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 0.0);
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),
509 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(true).setCount(9),
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);
517 public void test_new_technical_debt() {
518 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0);
521 newGroup(RuleType.CODE_SMELL).setEffort(3.0).setInLeak(true),
523 newGroup(RuleType.CODE_SMELL).setEffort(5.0).setInLeak(false),
525 newGroup(RuleType.BUG).setEffort(7.0).setInLeak(true),
527 newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0).setInLeak(true))
528 .assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 3.0);
532 public void test_new_reliability_remediation_effort() {
533 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 0.0);
536 newGroup(RuleType.BUG).setEffort(3.0).setInLeak(true),
538 newGroup(RuleType.BUG).setEffort(5.0).setInLeak(false),
540 newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
542 newResolvedGroup(RuleType.BUG).setEffort(17.0).setInLeak(true))
543 .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 3.0);
547 public void test_new_security_remediation_effort() {
548 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 0.0);
551 newGroup(RuleType.VULNERABILITY).setEffort(3.0).setInLeak(true),
553 newGroup(RuleType.VULNERABILITY).setEffort(5.0).setInLeak(false),
555 newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
557 newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0).setInLeak(true))
558 .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 3.0);
562 public void test_new_reliability_rating() {
563 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.A);
566 newGroup(RuleType.BUG).setSeverity(Severity.INFO).setCount(3).setInLeak(true),
567 newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setCount(1).setInLeak(true),
569 newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(false),
571 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
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);
579 public void test_new_security_rating() {
580 withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.A);
583 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setCount(3).setInLeak(true),
584 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setCount(1).setInLeak(true),
586 newGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(false),
588 newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
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);
596 public void test_new_sqale_debt_ratio_and_new_maintainability_rating() {
598 .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
599 .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
666 private Verifier with(IssueGroupDto... groups) {
667 return new Verifier(groups);
670 private Verifier withNoIssues() {
671 return new Verifier(new IssueGroupDto[0]);
674 private Verifier with(Metric metric, double value) {
675 return new Verifier(new IssueGroupDto[0]).and(metric, value);
678 private Verifier withLeak(Metric metric, double leakValue) {
679 return new Verifier(new IssueGroupDto[0]).andLeak(metric, leakValue);
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<>();
687 private Verifier(IssueGroupDto[] groups) {
688 this.groups = groups;
691 Verifier and(Metric metric, double value) {
692 this.values.put(metric, value);
696 Verifier andLeak(Metric metric, double value) {
697 this.leakValues.put(metric, value);
701 Verifier assertThatValueIs(Metric metric, double expectedValue) {
702 TestContext context = run(metric, false);
703 assertThat(context.doubleValue).isNotNull().isEqualTo(expectedValue);
707 Verifier assertThatLeakValueIs(Metric metric, double expectedValue) {
708 TestContext context = run(metric, true);
709 assertThat(context.doubleLeakValue).isNotNull().isEqualTo(expectedValue);
713 Verifier assertThatLeakValueIs(Metric metric, Rating expectedRating) {
714 TestContext context = run(metric, true);
715 assertThat(context.ratingLeakValue).isNotNull().isEqualTo(expectedRating);
719 Verifier assertThatValueIs(Metric metric, Rating expectedValue) {
720 TestContext context = run(metric, false);
721 assertThat(context.ratingValue).isNotNull().isEqualTo(expectedValue);
725 private TestContext run(Metric metric, boolean expectLeakFormula) {
726 IssueMetricFormula formula = underTest.getFormulas().stream()
727 .filter(f -> f.getMetric().getKey().equals(metric.getKey()))
730 assertThat(formula.isOnLeak()).isEqualTo(expectLeakFormula);
731 TestContext context = new TestContext(formula.getDependentMetrics(), values, leakValues);
732 formula.compute(context, newIssueCounter(groups));
737 private static IssueCounter newIssueCounter(IssueGroupDto... issues) {
738 return new IssueCounter(asList(issues));
741 private static IssueGroupDto newGroup() {
742 return newGroup(RuleType.CODE_SMELL);
745 private static IssueGroupDto newGroup(RuleType ruleType) {
746 IssueGroupDto dto = new IssueGroupDto();
747 // set non-null fields
748 dto.setRuleType(ruleType.getDbConstant());
751 dto.setSeverity(Severity.INFO);
752 dto.setStatus(Issue.STATUS_OPEN);
753 dto.setInLeak(false);
757 private static IssueGroupDto newResolvedGroup(RuleType ruleType) {
758 return newGroup(ruleType).setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setStatus(Issue.STATUS_CLOSED);
761 private static IssueGroupDto newResolvedGroup(String resolution, String status) {
762 return newGroup().setResolution(resolution).setStatus(status);
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;
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;
781 public ComponentDto getComponent() {
782 throw new UnsupportedOperationException();
786 public DebtRatingGrid getDebtRatingGrid() {
787 return new DebtRatingGrid(new double[] {0.05, 0.1, 0.2, 0.5});
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");
795 if (values.containsKey(metric)) {
796 return Optional.of(values.get(metric));
798 return Optional.empty();
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");
806 if (leakValues.containsKey(metric)) {
807 return Optional.of(leakValues.get(metric));
809 return Optional.empty();
813 public void setValue(double value) {
814 this.doubleValue = value;
818 public void setValue(Rating value) {
819 this.ratingValue = value;
823 public void setLeakValue(double value) {
824 this.doubleLeakValue = value;
828 public void setLeakValue(Rating value) {
829 this.ratingLeakValue = value;