]> source.dussan.org Git - sonarqube.git/blob
b00a73f5dc9be4a88e8f6b93775a61dd46cd1c72
[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.qualitymodel;
21
22 import java.util.Arrays;
23 import javax.annotation.Nullable;
24 import org.assertj.core.api.Assertions;
25 import org.assertj.core.data.Offset;
26 import org.junit.Before;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.sonar.api.rules.RuleType;
30 import org.sonar.ce.task.projectanalysis.component.Component;
31 import org.sonar.ce.task.projectanalysis.component.FileAttributes;
32 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
33 import org.sonar.ce.task.projectanalysis.component.VisitorsCrawler;
34 import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesRepositoryRule;
35 import org.sonar.ce.task.projectanalysis.issue.FillComponentIssuesVisitorRule;
36 import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
37 import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
38 import org.sonar.ce.task.projectanalysis.issue.NewIssueClassifier;
39 import org.sonar.core.issue.DefaultIssue;
40 import org.sonar.core.util.UuidFactoryFast;
41 import org.sonar.core.util.Uuids;
42 import org.sonar.server.measure.Rating;
43
44 import static org.assertj.core.api.Assertions.assertThat;
45 import static org.mockito.ArgumentMatchers.any;
46 import static org.mockito.ArgumentMatchers.eq;
47 import static org.mockito.Mockito.mock;
48 import static org.mockito.Mockito.when;
49 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
50 import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
51 import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
52 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED;
53 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_KEY;
54 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS;
55 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
56 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS;
57 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
58 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING;
59 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY;
60 import static org.sonar.api.rule.Severity.MAJOR;
61 import static org.sonar.api.rule.Severity.MINOR;
62 import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
63 import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
64 import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
65 import static org.sonar.ce.task.projectanalysis.measure.MeasureAssert.assertThat;
66 import static org.sonar.server.measure.Rating.A;
67 import static org.sonar.server.measure.Rating.B;
68 import static org.sonar.server.measure.Rating.C;
69 import static org.sonar.server.measure.Rating.D;
70 import static org.sonar.server.measure.Rating.E;
71
72 public class NewSecurityReviewMeasuresVisitorTest {
73   private static final Offset<Double> VALUE_COMPARISON_OFFSET = Offset.offset(0.01);
74   private static final String LANGUAGE_KEY_1 = "lKey1";
75
76   private static final int PROJECT_REF = 1;
77   private static final int ROOT_DIR_REF = 12;
78   private static final int DIRECTORY_REF = 123;
79   private static final int FILE_1_REF = 1231;
80   private static final int FILE_2_REF = 1232;
81
82   private static final Component ROOT_PROJECT = builder(Component.Type.PROJECT, PROJECT_REF).setKey("project")
83     .addChildren(
84       builder(DIRECTORY, ROOT_DIR_REF).setKey("dir")
85         .addChildren(
86           builder(DIRECTORY, DIRECTORY_REF).setKey("directory")
87             .addChildren(
88               builder(FILE, FILE_1_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_KEY_1, 1)).setKey("file1").build(),
89               builder(FILE, FILE_2_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_KEY_1, 1)).setKey("file2").build())
90             .build())
91         .build())
92     .build();
93
94   @Rule
95   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
96   @Rule
97   public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
98     .add(NEW_SECURITY_REVIEW_RATING)
99     .add(NEW_SECURITY_HOTSPOTS_REVIEWED)
100     .add(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS)
101     .add(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS);
102   @Rule
103   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
104   @Rule
105   public ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder);
106   @Rule
107   public FillComponentIssuesVisitorRule fillComponentIssuesVisitorRule = new FillComponentIssuesVisitorRule(componentIssuesRepositoryRule, treeRootHolder);
108   private final NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class);
109   private final VisitorsCrawler underTest = new VisitorsCrawler(Arrays.asList(fillComponentIssuesVisitorRule,
110     new NewSecurityReviewMeasuresVisitor(componentIssuesRepositoryRule, measureRepository, metricRepository, newIssueClassifier)));
111
112   @Before
113   public void setup() {
114     when(newIssueClassifier.isEnabled()).thenReturn(true);
115   }
116
117   @Test
118   public void compute_measures_when_100_percent_hotspots_reviewed() {
119     treeRootHolder.setRoot(ROOT_PROJECT);
120     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
121       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
122       // Should not be taken into account
123       oldHotspot(STATUS_TO_REVIEW, null),
124       oldHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
125       newIssue());
126     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
127       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
128       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED));
129     fillComponentIssuesVisitorRule.setIssues(ROOT_DIR_REF,
130       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED));
131
132     underTest.visit(ROOT_PROJECT);
133
134     verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
135     verifyRatingAndReviewedMeasures(FILE_2_REF, A, 100.0);
136     verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 100.0);
137     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 100.0);
138     verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
139   }
140
141   @Test
142   public void compute_measures_when_more_than_80_percent_hotspots_reviewed() {
143     treeRootHolder.setRoot(ROOT_PROJECT);
144     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
145       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
146       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
147       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
148       // Should not be taken into account
149       newIssue());
150     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
151       newHotspot(STATUS_TO_REVIEW, null),
152       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
153       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
154       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
155       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
156       // Should not be taken into account
157       oldHotspot(STATUS_TO_REVIEW, null),
158       oldHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
159       newIssue());
160
161     underTest.visit(ROOT_PROJECT);
162
163     verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
164     verifyRatingAndReviewedMeasures(FILE_2_REF, A, 80.0);
165     verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 87.5);
166     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 87.5);
167     verifyRatingAndReviewedMeasures(PROJECT_REF, A, 87.5);
168   }
169
170   @Test
171   public void compute_measures_when_more_than_70_percent_hotspots_reviewed() {
172     treeRootHolder.setRoot(ROOT_PROJECT);
173     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
174       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
175       // Should not be taken into account
176       newIssue());
177     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
178       newHotspot(STATUS_TO_REVIEW, null),
179       newHotspot(STATUS_TO_REVIEW, null),
180       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
181       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
182       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
183       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
184       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
185       // Should not be taken into account
186       oldHotspot(STATUS_TO_REVIEW, null),
187       oldHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
188       newIssue());
189
190     underTest.visit(ROOT_PROJECT);
191
192     verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
193     verifyRatingAndReviewedMeasures(FILE_2_REF, B, 71.42);
194     verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, 75.0);
195     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, 75.0);
196     verifyRatingAndReviewedMeasures(PROJECT_REF, B, 75.0);
197   }
198
199   @Test
200   public void compute_measures_when_more_than_50_percent_hotspots_reviewed() {
201     treeRootHolder.setRoot(ROOT_PROJECT);
202     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
203       newHotspot(STATUS_TO_REVIEW, null),
204       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
205       // Should not be taken into account
206       newIssue());
207     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
208       newHotspot(STATUS_TO_REVIEW, null),
209       newHotspot(STATUS_TO_REVIEW, null),
210       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
211       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
212       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
213       // Should not be taken into account
214       oldHotspot(STATUS_TO_REVIEW, null),
215       oldHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
216       newIssue());
217
218     underTest.visit(ROOT_PROJECT);
219
220     verifyRatingAndReviewedMeasures(FILE_1_REF, C, 50.0);
221     verifyRatingAndReviewedMeasures(FILE_2_REF, C, 60.0);
222     verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, 57.14);
223     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, 57.14);
224     verifyRatingAndReviewedMeasures(PROJECT_REF, C, 57.14);
225   }
226
227   @Test
228   public void compute_measures_when_more_30_than_percent_hotspots_reviewed() {
229     treeRootHolder.setRoot(ROOT_PROJECT);
230     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
231       newHotspot(STATUS_TO_REVIEW, null),
232       newHotspot(STATUS_TO_REVIEW, null),
233       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
234       // Should not be taken into account
235       newIssue());
236     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
237       newHotspot(STATUS_TO_REVIEW, null),
238       newHotspot(STATUS_TO_REVIEW, null),
239       newHotspot(STATUS_TO_REVIEW, null),
240       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
241       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
242       // Should not be taken into account
243       oldHotspot(STATUS_TO_REVIEW, null),
244       oldHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
245       newIssue());
246
247     underTest.visit(ROOT_PROJECT);
248
249     verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.33);
250     verifyRatingAndReviewedMeasures(FILE_2_REF, D, 40.0);
251     verifyRatingAndReviewedMeasures(DIRECTORY_REF, D, 37.5);
252     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, 37.5);
253     verifyRatingAndReviewedMeasures(PROJECT_REF, D, 37.5);
254   }
255
256   @Test
257   public void compute_measures_when_less_than_30_percent_hotspots_reviewed() {
258     treeRootHolder.setRoot(ROOT_PROJECT);
259     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
260       newHotspot(STATUS_TO_REVIEW, null),
261       newHotspot(STATUS_TO_REVIEW, null),
262       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
263       // Should not be taken into account
264       newIssue());
265     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
266       newHotspot(STATUS_TO_REVIEW, null),
267       newHotspot(STATUS_TO_REVIEW, null),
268       newHotspot(STATUS_TO_REVIEW, null),
269       // Should not be taken into account
270       oldHotspot(STATUS_TO_REVIEW, null),
271       oldHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
272       newIssue());
273
274     underTest.visit(ROOT_PROJECT);
275
276     verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.33);
277     verifyRatingAndReviewedMeasures(FILE_2_REF, E, 0.0);
278     verifyRatingAndReviewedMeasures(DIRECTORY_REF, E, 16.66);
279     verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, 16.66);
280     verifyRatingAndReviewedMeasures(PROJECT_REF, E, 16.66);
281   }
282
283   @Test
284   public void compute_A_rating_and_no_percent_when_no_new_hotspot_on_new_code() {
285     treeRootHolder.setRoot(ROOT_PROJECT);
286     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
287       oldHotspot(STATUS_TO_REVIEW, null),
288       oldHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
289       newIssue());
290
291     underTest.visit(ROOT_PROJECT);
292
293     verifyRatingAndReviewedMeasures(PROJECT_REF, A, null);
294   }
295
296   @Test
297   public void compute_status_related_measures() {
298     treeRootHolder.setRoot(ROOT_PROJECT);
299     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
300       newHotspot(STATUS_TO_REVIEW, null),
301       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
302       // Should not be taken into account
303       newIssue());
304     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
305       newHotspot(STATUS_TO_REVIEW, null),
306       newHotspot(STATUS_TO_REVIEW, null),
307       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
308       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
309       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
310       newIssue());
311
312     underTest.visit(ROOT_PROJECT);
313
314     verifyHotspotStatusMeasures(FILE_1_REF, null, null);
315     verifyHotspotStatusMeasures(FILE_2_REF, null, null);
316     verifyHotspotStatusMeasures(DIRECTORY_REF, null, null);
317     verifyHotspotStatusMeasures(ROOT_DIR_REF, null, null);
318     verifyHotspotStatusMeasures(PROJECT_REF, 4, 3);
319   }
320
321   @Test
322   public void compute_0_status_related_measures_when_no_hotspot() {
323     treeRootHolder.setRoot(ROOT_PROJECT);
324
325     underTest.visit(ROOT_PROJECT);
326
327     verifyHotspotStatusMeasures(PROJECT_REF, 0, 0);
328   }
329
330   @Test
331   public void no_measure_if_there_is_no_period() {
332     when(newIssueClassifier.isEnabled()).thenReturn(false);
333     treeRootHolder.setRoot(ROOT_PROJECT);
334     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
335       newHotspot(STATUS_TO_REVIEW, null),
336       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED));
337
338     underTest.visit(ROOT_PROJECT);
339
340     assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF).values()).isEmpty();
341   }
342
343   private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, @Nullable Double expectedHotspotsReviewed) {
344     assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_REVIEW_RATING_KEY)).hasValue(expectedReviewRating.getIndex());
345     if (expectedHotspotsReviewed != null) {
346       assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY)).hasValue(expectedHotspotsReviewed,
347         VALUE_COMPARISON_OFFSET);
348     } else {
349       assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY)).isAbsent();
350     }
351   }
352
353   private void verifyHotspotStatusMeasures(int componentRef, @Nullable Integer hotspotsReviewed, @Nullable Integer hotspotsToReview) {
354     if (hotspotsReviewed == null) {
355       Assertions.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY)).isEmpty();
356     } else {
357       assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY)).hasValue(hotspotsReviewed);
358     }
359     if (hotspotsReviewed == null) {
360       Assertions.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY)).isEmpty();
361     } else {
362       assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY)).hasValue(hotspotsToReview);
363     }
364   }
365
366   private DefaultIssue newHotspot(String status, @Nullable String resolution) {
367     return createHotspot(status, resolution, true);
368   }
369
370   private DefaultIssue oldHotspot(String status, @Nullable String resolution) {
371     return createHotspot(status, resolution, false);
372   }
373
374   private DefaultIssue createHotspot(String status, @Nullable String resolution, boolean isNew) {
375     DefaultIssue issue = new DefaultIssue()
376       .setKey(UuidFactoryFast.getInstance().create())
377       .setSeverity(MINOR)
378       .setStatus(status)
379       .setResolution(resolution)
380       .setType(RuleType.SECURITY_HOTSPOT);
381     when(newIssueClassifier.isNew(any(), eq(issue))).thenReturn(isNew);
382     return issue;
383   }
384
385   private DefaultIssue newIssue() {
386     DefaultIssue issue = new DefaultIssue()
387       .setKey(Uuids.create())
388       .setSeverity(MAJOR)
389       .setType(RuleType.BUG);
390     when(newIssueClassifier.isNew(any(), eq(issue))).thenReturn(false);
391     return issue;
392
393   }
394
395 }