]> source.dussan.org Git - sonarqube.git/blob
09ead8925652788fd722da5b71fc9b2df03f81df
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2024 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.ce.task.projectanalysis.issue;
21
22 import java.util.Arrays;
23 import java.util.List;
24 import java.util.Map;
25 import javax.annotation.Nullable;
26 import org.assertj.core.data.MapEntry;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.sonar.api.issue.impact.Severity;
30 import org.sonar.api.issue.impact.SoftwareQuality;
31 import org.sonar.api.rules.RuleType;
32 import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
33 import org.sonar.ce.task.projectanalysis.component.Component;
34 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
35 import org.sonar.ce.task.projectanalysis.measure.MeasureRepoEntry;
36 import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
37 import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
38 import org.sonar.core.issue.DefaultIssue;
39 import org.sonar.db.rule.RuleTesting;
40
41 import static java.util.Arrays.stream;
42 import static org.assertj.core.api.Assertions.assertThat;
43 import static org.assertj.core.api.Assertions.entry;
44 import static org.mockito.ArgumentMatchers.any;
45 import static org.mockito.ArgumentMatchers.eq;
46 import static org.mockito.Mockito.mock;
47 import static org.mockito.Mockito.when;
48 import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
49 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
50 import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX;
51 import static org.sonar.api.issue.Issue.STATUS_CLOSED;
52 import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
53 import static org.sonar.api.issue.Issue.STATUS_OPEN;
54 import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
55 import static org.sonar.api.issue.impact.Severity.HIGH;
56 import static org.sonar.api.issue.impact.Severity.MEDIUM;
57 import static org.sonar.api.measures.CoreMetrics.ACCEPTED_ISSUES;
58 import static org.sonar.api.measures.CoreMetrics.ACCEPTED_ISSUES_KEY;
59 import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS;
60 import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS_KEY;
61 import static org.sonar.api.measures.CoreMetrics.BUGS;
62 import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
63 import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS;
64 import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS_KEY;
65 import static org.sonar.api.measures.CoreMetrics.CONFIRMED_ISSUES;
66 import static org.sonar.api.measures.CoreMetrics.CONFIRMED_ISSUES_KEY;
67 import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS;
68 import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY;
69 import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES;
70 import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES_KEY;
71 import static org.sonar.api.measures.CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES;
72 import static org.sonar.api.measures.CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES_KEY;
73 import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS;
74 import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS;
75 import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
76 import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS;
77 import static org.sonar.api.measures.CoreMetrics.NEW_ACCEPTED_ISSUES;
78 import static org.sonar.api.measures.CoreMetrics.NEW_ACCEPTED_ISSUES_KEY;
79 import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS;
80 import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS_KEY;
81 import static org.sonar.api.measures.CoreMetrics.NEW_BUGS;
82 import static org.sonar.api.measures.CoreMetrics.NEW_BUGS_KEY;
83 import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS;
84 import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS_KEY;
85 import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS;
86 import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY;
87 import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS;
88 import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS;
89 import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY;
90 import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS;
91 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS;
92 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY;
93 import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS;
94 import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY;
95 import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES;
96 import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES_KEY;
97 import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES;
98 import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES_KEY;
99 import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES;
100 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS;
101 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
102 import static org.sonar.api.measures.CoreMetrics.VIOLATIONS;
103 import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
104 import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES;
105 import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
106 import static org.sonar.api.rule.Severity.BLOCKER;
107 import static org.sonar.api.rule.Severity.CRITICAL;
108 import static org.sonar.api.rule.Severity.MAJOR;
109 import static org.sonar.api.rules.RuleType.BUG;
110 import static org.sonar.api.rules.RuleType.CODE_SMELL;
111 import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
112 import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
113 import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
114 import static org.sonar.ce.task.projectanalysis.measure.MeasureRepoEntry.entryOf;
115
116 public class IssueCounterTest {
117
118   private static final Component FILE1 = builder(Component.Type.FILE, 1).build();
119   private static final Component FILE2 = builder(Component.Type.FILE, 2).build();
120   private static final Component FILE3 = builder(Component.Type.FILE, 3).build();
121   private static final Component PROJECT = builder(Component.Type.PROJECT, 4).addChildren(FILE1, FILE2, FILE3).build();
122
123   @Rule
124   public BatchReportReaderRule reportReader = new BatchReportReaderRule();
125
126   @Rule
127   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
128
129   @Rule
130   public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
131     .add(VIOLATIONS)
132     .add(OPEN_ISSUES)
133     .add(REOPENED_ISSUES)
134     .add(CONFIRMED_ISSUES)
135     .add(BLOCKER_VIOLATIONS)
136     .add(CRITICAL_VIOLATIONS)
137     .add(MAJOR_VIOLATIONS)
138     .add(MINOR_VIOLATIONS)
139     .add(INFO_VIOLATIONS)
140     .add(NEW_VIOLATIONS)
141     .add(NEW_BLOCKER_VIOLATIONS)
142     .add(NEW_CRITICAL_VIOLATIONS)
143     .add(NEW_MAJOR_VIOLATIONS)
144     .add(NEW_MINOR_VIOLATIONS)
145     .add(NEW_INFO_VIOLATIONS)
146     .add(FALSE_POSITIVE_ISSUES)
147     .add(ACCEPTED_ISSUES)
148     .add(CODE_SMELLS)
149     .add(BUGS)
150     .add(VULNERABILITIES)
151     .add(SECURITY_HOTSPOTS)
152     .add(NEW_CODE_SMELLS)
153     .add(NEW_BUGS)
154     .add(NEW_VULNERABILITIES)
155     .add(NEW_SECURITY_HOTSPOTS)
156     .add(NEW_ACCEPTED_ISSUES)
157     .add(HIGH_IMPACT_ACCEPTED_ISSUES);
158
159   @Rule
160   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
161   private NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class);
162   private IssueCounter underTest = new IssueCounter(metricRepository, measureRepository, newIssueClassifier);
163
164   @Test
165   public void count_issues_by_status() {
166     // bottom-up traversal -> from files to project
167     underTest.beforeComponent(FILE1);
168     underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
169     underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR));
170     underTest.onIssue(FILE1, createIssue(RESOLUTION_FALSE_POSITIVE, STATUS_RESOLVED, MAJOR));
171     underTest.afterComponent(FILE1);
172
173     underTest.beforeComponent(FILE2);
174     underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER));
175     underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, MAJOR));
176     underTest.afterComponent(FILE2);
177
178     underTest.beforeComponent(FILE3);
179     // Security hotspot should be ignored
180     underTest.onIssue(FILE3, createSecurityHotspot().setStatus(STATUS_OPEN));
181     underTest.afterComponent(FILE3);
182
183     underTest.beforeComponent(PROJECT);
184     underTest.afterComponent(PROJECT);
185
186     assertMeasures(FILE1, entry(VIOLATIONS_KEY, 1), entry(OPEN_ISSUES_KEY, 1), entry(CONFIRMED_ISSUES_KEY, 0));
187     assertMeasures(FILE2, entry(VIOLATIONS_KEY, 2), entry(OPEN_ISSUES_KEY, 0), entry(CONFIRMED_ISSUES_KEY, 2));
188     assertMeasures(FILE3, entry(VIOLATIONS_KEY, 0));
189     assertMeasures(PROJECT, entry(VIOLATIONS_KEY, 3), entry(OPEN_ISSUES_KEY, 1), entry(CONFIRMED_ISSUES_KEY, 2));
190   }
191
192   @Test
193   public void count_issues_by_resolution() {
194     // bottom-up traversal -> from files to project
195     underTest.beforeComponent(FILE1);
196     underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
197     underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR));
198     underTest.onIssue(FILE1, createIssue(RESOLUTION_FALSE_POSITIVE, STATUS_RESOLVED, MAJOR));
199     underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, MAJOR));
200     underTest.afterComponent(FILE1);
201
202     underTest.beforeComponent(FILE2);
203     underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER));
204     underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, MAJOR));
205     underTest.onIssue(FILE2, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, MAJOR));
206     underTest.afterComponent(FILE2);
207
208     underTest.beforeComponent(FILE3);
209     // Security hotspot should be ignored
210     underTest.onIssue(FILE3, createSecurityHotspot().setResolution(RESOLUTION_WONT_FIX));
211     underTest.afterComponent(FILE3);
212
213     underTest.beforeComponent(PROJECT);
214     underTest.afterComponent(PROJECT);
215
216     assertMeasures(FILE1, entry(VIOLATIONS_KEY, 1), entry(FALSE_POSITIVE_ISSUES_KEY, 1), entry(ACCEPTED_ISSUES_KEY, 1));
217     assertMeasures(FILE2, entry(VIOLATIONS_KEY, 2), entry(FALSE_POSITIVE_ISSUES_KEY, 0), entry(ACCEPTED_ISSUES_KEY, 1));
218     assertMeasures(FILE3, entry(VIOLATIONS_KEY, 0));
219     assertMeasures(PROJECT, entry(VIOLATIONS_KEY, 3), entry(FALSE_POSITIVE_ISSUES_KEY, 1), entry(ACCEPTED_ISSUES_KEY, 2));
220   }
221
222   @Test
223   public void count_unresolved_issues_by_severity() {
224     // bottom-up traversal -> from files to project
225     underTest.beforeComponent(FILE1);
226     underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
227     // this resolved issue is ignored
228     underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR));
229     underTest.afterComponent(FILE1);
230
231     underTest.beforeComponent(FILE2);
232     underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER));
233     underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, MAJOR));
234     underTest.afterComponent(FILE2);
235
236     underTest.beforeComponent(PROJECT);
237     // Security hotspot should be ignored
238     underTest.onIssue(FILE3, createSecurityHotspot().setSeverity(MAJOR));
239     underTest.afterComponent(PROJECT);
240
241     assertMeasures(FILE1, entry(BLOCKER_VIOLATIONS_KEY, 1), entry(CRITICAL_VIOLATIONS_KEY, 0), entry(MAJOR_VIOLATIONS_KEY, 0));
242     assertMeasures(FILE2, entry(BLOCKER_VIOLATIONS_KEY, 1), entry(CRITICAL_VIOLATIONS_KEY, 0), entry(MAJOR_VIOLATIONS_KEY, 1));
243     assertMeasures(PROJECT, entry(BLOCKER_VIOLATIONS_KEY, 2), entry(CRITICAL_VIOLATIONS_KEY, 0), entry(MAJOR_VIOLATIONS_KEY, 1));
244   }
245
246   @Test
247   public void count_unresolved_issues_by_type() {
248     // bottom-up traversal -> from files to project
249     // file1 : one open code smell, one closed code smell (which will be excluded from metric)
250     underTest.beforeComponent(FILE1);
251     underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(CODE_SMELL));
252     underTest.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR).setType(CODE_SMELL));
253     underTest.afterComponent(FILE1);
254
255     // file2 : one bug
256     underTest.beforeComponent(FILE2);
257     underTest.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER).setType(BUG));
258     underTest.afterComponent(FILE2);
259
260     // file3 : one unresolved security hotspot
261     underTest.beforeComponent(FILE3);
262     underTest.onIssue(FILE3, createSecurityHotspot());
263     underTest.onIssue(FILE3, createSecurityHotspot().setResolution(RESOLUTION_WONT_FIX).setStatus(STATUS_CLOSED));
264     underTest.afterComponent(FILE3);
265
266     underTest.beforeComponent(PROJECT);
267     underTest.afterComponent(PROJECT);
268
269     assertMeasures(FILE1, entry(CODE_SMELLS_KEY, 1), entry(BUGS_KEY, 0), entry(VULNERABILITIES_KEY, 0), entry(SECURITY_HOTSPOTS_KEY, 0));
270     assertMeasures(FILE2, entry(CODE_SMELLS_KEY, 0), entry(BUGS_KEY, 1), entry(VULNERABILITIES_KEY, 0), entry(SECURITY_HOTSPOTS_KEY, 0));
271     assertMeasures(FILE3, entry(CODE_SMELLS_KEY, 0), entry(BUGS_KEY, 0), entry(VULNERABILITIES_KEY, 0), entry(SECURITY_HOTSPOTS_KEY, 1));
272     assertMeasures(PROJECT, entry(CODE_SMELLS_KEY, 1), entry(BUGS_KEY, 1), entry(VULNERABILITIES_KEY, 0), entry(SECURITY_HOTSPOTS_KEY, 1));
273   }
274
275   @Test
276   public void count_new_issues() {
277     when(newIssueClassifier.isEnabled()).thenReturn(true);
278
279     underTest.beforeComponent(FILE1);
280     // created before -> existing issues (so ignored)
281     underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(CODE_SMELL));
282     underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER).setType(BUG));
283
284     // created after -> 4 new issues but 1 is closed
285     underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, CRITICAL).setType(CODE_SMELL));
286     underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, CRITICAL).setType(BUG));
287     underTest.onIssue(FILE1, createNewIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR).setType(BUG));
288     underTest.onIssue(FILE1, createNewSecurityHotspot());
289     underTest.onIssue(FILE1, createNewSecurityHotspot().setResolution(RESOLUTION_WONT_FIX).setStatus(STATUS_CLOSED));
290     underTest.afterComponent(FILE1);
291
292     underTest.beforeComponent(FILE2);
293     underTest.afterComponent(FILE2);
294
295     underTest.beforeComponent(PROJECT);
296     underTest.afterComponent(PROJECT);
297
298     assertValues(FILE1, entry(NEW_VIOLATIONS_KEY, 2), entry(NEW_CRITICAL_VIOLATIONS_KEY, 2), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
299       entry(NEW_CODE_SMELLS_KEY, 1), entry(NEW_BUGS_KEY, 1), entry(NEW_VULNERABILITIES_KEY, 0), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
300     assertValues(PROJECT, entry(NEW_VIOLATIONS_KEY, 2), entry(NEW_CRITICAL_VIOLATIONS_KEY, 2), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
301       entry(NEW_CODE_SMELLS_KEY, 1), entry(NEW_BUGS_KEY, 1), entry(NEW_VULNERABILITIES_KEY, 0), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
302   }
303
304   @Test
305   public void count_new_accepted_issues() {
306     when(newIssueClassifier.isEnabled()).thenReturn(true);
307
308     underTest.beforeComponent(FILE1);
309     // created before -> existing issues (so ignored)
310     underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, CRITICAL));
311     underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, CRITICAL));
312
313     // created after -> 2 accepted, 1 open, 1 hotspot
314     underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, CRITICAL));
315     underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, CRITICAL));
316     underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, CRITICAL));
317     underTest.onIssue(FILE1, createNewSecurityHotspot());
318     underTest.afterComponent(FILE1);
319
320     underTest.beforeComponent(PROJECT);
321     underTest.afterComponent(PROJECT);
322
323     assertValues(FILE1, entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 2), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
324     assertValues(PROJECT, entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 2), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
325   }
326
327   @Test
328   public void count_high_impact_accepted_issues() {
329     when(newIssueClassifier.isEnabled()).thenReturn(true);
330
331     underTest.beforeComponent(FILE1);
332     // created before -> existing issues with 1 high impact accepted
333     underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, HIGH));
334     underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, HIGH));
335     underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, MEDIUM));
336
337     // created after -> 2 high impact accepted
338     underTest.onIssue(FILE1, createNewIssue(null, STATUS_OPEN, HIGH));
339     underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, HIGH));
340     underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, HIGH));
341     underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, MEDIUM));
342     underTest.onIssue(FILE1, createNewSecurityHotspot());
343     underTest.afterComponent(FILE1);
344
345     underTest.beforeComponent(PROJECT);
346     underTest.afterComponent(PROJECT);
347
348     assertValues(FILE1, entry(VIOLATIONS_KEY, 2), entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 3),
349       entry(HIGH_IMPACT_ACCEPTED_ISSUES_KEY, 3));
350     assertValues(PROJECT, entry(VIOLATIONS_KEY, 2), entry(NEW_VIOLATIONS_KEY, 1), entry(NEW_ACCEPTED_ISSUES_KEY, 3),
351       entry(HIGH_IMPACT_ACCEPTED_ISSUES_KEY, 3));
352   }
353
354   @Test
355   public void exclude_hotspots_from_issue_counts() {
356     // bottom-up traversal -> from files to project
357     underTest.beforeComponent(FILE1);
358     underTest.onIssue(FILE1, createSecurityHotspot());
359     underTest.onIssue(FILE1, createSecurityHotspot());
360     underTest.afterComponent(FILE1);
361
362     underTest.beforeComponent(FILE2);
363     underTest.onIssue(FILE2, createSecurityHotspot());
364     underTest.afterComponent(FILE2);
365
366     underTest.beforeComponent(FILE3);
367     underTest.afterComponent(FILE3);
368
369     underTest.beforeComponent(PROJECT);
370     underTest.afterComponent(PROJECT);
371
372     assertMeasures(FILE1, entry(VIOLATIONS_KEY, 0), entry(OPEN_ISSUES_KEY, 0), entry(CONFIRMED_ISSUES_KEY, 0));
373     assertMeasures(FILE2, entry(VIOLATIONS_KEY, 0), entry(OPEN_ISSUES_KEY, 0), entry(CONFIRMED_ISSUES_KEY, 0));
374     assertMeasures(FILE3, entry(VIOLATIONS_KEY, 0));
375     assertMeasures(PROJECT, entry(VIOLATIONS_KEY, 0), entry(OPEN_ISSUES_KEY, 0), entry(CONFIRMED_ISSUES_KEY, 0));
376   }
377
378   @Test
379   public void exclude_new_hotspots_from_issue_counts() {
380     when(newIssueClassifier.isEnabled()).thenReturn(true);
381
382     underTest.beforeComponent(FILE1);
383     // created before -> existing issues (so ignored)
384     underTest.onIssue(FILE1, createSecurityHotspot());
385     underTest.onIssue(FILE1, createSecurityHotspot());
386
387     // created after, but closed
388     underTest.onIssue(FILE1, createNewSecurityHotspot().setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_WONT_FIX));
389
390     for (String severity : Arrays.asList(CRITICAL, BLOCKER, MAJOR)) {
391       DefaultIssue issue = createNewSecurityHotspot();
392       issue.setSeverity(severity);
393       underTest.onIssue(FILE1, issue);
394     }
395     underTest.afterComponent(FILE1);
396
397     underTest.beforeComponent(FILE2);
398     underTest.afterComponent(FILE2);
399
400     underTest.beforeComponent(PROJECT);
401     underTest.afterComponent(PROJECT);
402
403     assertValues(FILE1, entry(NEW_VIOLATIONS_KEY, 0), entry(NEW_CRITICAL_VIOLATIONS_KEY, 0), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
404       entry(NEW_VULNERABILITIES_KEY, 0));
405     assertValues(PROJECT, entry(NEW_VIOLATIONS_KEY, 0), entry(NEW_CRITICAL_VIOLATIONS_KEY, 0), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
406       entry(NEW_VULNERABILITIES_KEY, 0));
407   }
408
409   @SafeVarargs
410   private final void assertValues(Component componentRef, MapEntry<String, Integer>... entries) {
411     assertThat(measureRepository.getRawMeasures(componentRef).entrySet()
412       .stream()
413       .map(e -> entry(e.getKey(), e.getValue().getIntValue())))
414       .contains(entries);
415   }
416
417   @SafeVarargs
418   private final void assertMeasures(Component componentRef, Map.Entry<String, Integer>... entries) {
419     List<MeasureRepoEntry> expected = stream(entries)
420       .map(e -> entryOf(e.getKey(), newMeasureBuilder().create(e.getValue())))
421       .toList();
422
423     assertThat(measureRepository.getRawMeasures(componentRef).entrySet().stream().map(e -> entryOf(e.getKey(), e.getValue())))
424       .containsAll(expected);
425   }
426
427   private DefaultIssue createNewIssue(@Nullable String resolution, String status, String severity) {
428     return createNewIssue(resolution, status, severity, CODE_SMELL);
429   }
430
431   private DefaultIssue createNewIssue(@Nullable String resolution, String status, Severity impactSeverity) {
432     DefaultIssue issue = createNewIssue(resolution, status, MAJOR, CODE_SMELL);
433     issue.addImpact(SoftwareQuality.MAINTAINABILITY, impactSeverity);
434     return issue;
435   }
436
437   private DefaultIssue createNewIssue(@Nullable String resolution, String status, String severity, RuleType ruleType) {
438     DefaultIssue issue = createIssue(resolution, status, severity, ruleType);
439     when(newIssueClassifier.isNew(any(), eq(issue))).thenReturn(true);
440     return issue;
441   }
442
443   private static DefaultIssue createIssue(@Nullable String resolution, String status, String severity) {
444     return createIssue(resolution, status, severity, CODE_SMELL);
445   }
446
447   private static DefaultIssue createIssue(@Nullable String resolution, String status, Severity impactSeverity) {
448     DefaultIssue issue = createIssue(resolution, status, MAJOR, CODE_SMELL);
449     issue.addImpact(SoftwareQuality.MAINTAINABILITY, impactSeverity);
450     return issue;
451   }
452
453   private static DefaultIssue createIssue(@Nullable String resolution, String status, String severity, RuleType ruleType) {
454     return new DefaultIssue()
455       .setResolution(resolution).setStatus(status)
456       .setSeverity(severity).setRuleKey(RuleTesting.XOO_X1)
457       .setType(ruleType);
458   }
459
460   private static DefaultIssue createSecurityHotspot() {
461     return createIssue(null, STATUS_OPEN, "MAJOR", SECURITY_HOTSPOT);
462   }
463
464   private DefaultIssue createNewSecurityHotspot() {
465     return createNewIssue(null, STATUS_OPEN, "MAJOR", SECURITY_HOTSPOT);
466   }
467 }