3 * Copyright (C) 2009-2024 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.ce.task.projectanalysis.qualitymodel;
22 import javax.annotation.Nullable;
23 import org.junit.jupiter.api.Test;
24 import org.junit.jupiter.api.extension.RegisterExtension;
25 import org.sonar.api.rules.RuleType;
26 import org.sonar.ce.task.projectanalysis.component.Component;
27 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
28 import org.sonar.ce.task.projectanalysis.component.VisitorsCrawler;
29 import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesRepositoryRule;
30 import org.sonar.ce.task.projectanalysis.issue.FillComponentIssuesVisitorRule;
31 import org.sonar.ce.task.projectanalysis.measure.Measure;
32 import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
33 import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
34 import org.sonar.core.issue.DefaultIssue;
35 import org.sonar.core.util.Uuids;
36 import org.sonar.server.measure.Rating;
38 import static java.util.Arrays.asList;
39 import static org.assertj.core.api.Assertions.assertThat;
40 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
41 import static org.sonar.api.issue.Issue.RESOLUTION_SAFE;
42 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED;
43 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_KEY;
44 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS;
45 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY;
46 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS;
47 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY;
48 import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING;
49 import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY;
50 import static org.sonar.api.rule.Severity.MAJOR;
51 import static org.sonar.api.rule.Severity.MINOR;
52 import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
53 import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
54 import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
55 import static org.sonar.core.issue.DefaultIssue.STATUS_REVIEWED;
56 import static org.sonar.core.issue.DefaultIssue.STATUS_TO_REVIEW;
57 import static org.sonar.server.measure.Rating.A;
58 import static org.sonar.server.measure.Rating.B;
59 import static org.sonar.server.measure.Rating.C;
60 import static org.sonar.server.measure.Rating.D;
61 import static org.sonar.server.measure.Rating.E;
62 import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING;
63 import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY;
65 class SecurityReviewMeasuresVisitorTest {
67 private static final int PROJECT_REF = 1;
68 private static final int ROOT_DIR_REF = 12;
69 private static final int DIRECTORY_REF = 123;
70 private static final int FILE_1_REF = 1231;
71 private static final int FILE_2_REF = 1232;
73 private static final Component ROOT_PROJECT = builder(Component.Type.PROJECT, PROJECT_REF).setKey("project")
75 builder(DIRECTORY, ROOT_DIR_REF).setKey("dir")
77 builder(DIRECTORY, DIRECTORY_REF).setKey("directory")
79 builder(FILE, FILE_1_REF).setKey("file1").build(),
80 builder(FILE, FILE_2_REF).setKey("file2").build())
86 private final TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
88 private final MetricRepositoryRule metricRepository = new MetricRepositoryRule()
89 .add(SECURITY_REVIEW_RATING)
90 .add(SOFTWARE_QUALITY_SECURITY_REVIEW_RATING)
91 .add(SECURITY_HOTSPOTS_REVIEWED)
92 .add(SECURITY_HOTSPOTS_REVIEWED_STATUS)
93 .add(SECURITY_HOTSPOTS_TO_REVIEW_STATUS);
94 private final ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder);
96 private final FillComponentIssuesVisitorRule fillComponentIssuesVisitorRule =
97 new FillComponentIssuesVisitorRule(componentIssuesRepositoryRule, treeRootHolder);
99 private final MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
101 private final VisitorsCrawler underTest = new VisitorsCrawler(asList(fillComponentIssuesVisitorRule,
102 new SecurityReviewMeasuresVisitor(componentIssuesRepositoryRule, measureRepository, metricRepository)));
105 void compute_rating_and_reviewed_measures_when_100_percent_hotspots_reviewed() {
106 treeRootHolder.setRoot(ROOT_PROJECT);
107 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
108 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
109 // Should not be taken into account
111 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
112 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
113 newHotspot(STATUS_REVIEWED, RESOLUTION_SAFE),
116 underTest.visit(ROOT_PROJECT);
118 verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
119 verifyRatingAndReviewedMeasures(FILE_2_REF, A, A, 100.0);
120 verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, A, 100.0);
121 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, A, 100.0);
122 verifyRatingAndReviewedMeasures(PROJECT_REF, A, A, 100.0);
126 void compute_rating_and_reviewed__measures_when_more_than_80_percent_hotspots_reviewed() {
127 treeRootHolder.setRoot(ROOT_PROJECT);
128 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
129 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
130 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
131 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
132 // Should not be taken into account
134 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
135 newHotspot(STATUS_TO_REVIEW, null),
136 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
137 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
138 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
139 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
142 underTest.visit(ROOT_PROJECT);
144 verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
145 verifyRatingAndReviewedMeasures(FILE_2_REF, A, B, 80.0);
146 verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, B, 87.5);
147 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, B, 87.5);
148 verifyRatingAndReviewedMeasures(PROJECT_REF, A, B, 87.5);
152 void compute_rating_and_reviewed__measures_when_more_than_70_percent_hotspots_reviewed() {
153 treeRootHolder.setRoot(ROOT_PROJECT);
154 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
155 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
156 // Should not be taken into account
158 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
159 newHotspot(STATUS_TO_REVIEW, null),
160 newHotspot(STATUS_TO_REVIEW, null),
161 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
162 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
163 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
164 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
165 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
168 underTest.visit(ROOT_PROJECT);
170 verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
171 verifyRatingAndReviewedMeasures(FILE_2_REF, B, B, 71.4);
172 verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, B, 75.0);
173 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, B, 75.0);
174 verifyRatingAndReviewedMeasures(PROJECT_REF, B, B, 75.0);
178 void compute_rating_and_reviewed__measures_when_more_than_50_percent_hotspots_reviewed() {
179 treeRootHolder.setRoot(ROOT_PROJECT);
180 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
181 newHotspot(STATUS_TO_REVIEW, null),
182 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
183 // Should not be taken into account
185 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
186 newHotspot(STATUS_TO_REVIEW, null),
187 newHotspot(STATUS_TO_REVIEW, null),
188 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
189 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
190 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
193 underTest.visit(ROOT_PROJECT);
195 verifyRatingAndReviewedMeasures(FILE_1_REF, C, C,50.0);
196 verifyRatingAndReviewedMeasures(FILE_2_REF, C, C,60.0);
197 verifyRatingAndReviewedMeasures(DIRECTORY_REF, C,C, 57.1);
198 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, C,57.1);
199 verifyRatingAndReviewedMeasures(PROJECT_REF, C, C,57.1);
203 void compute_rating_and_reviewed__measures_when_more_30_than_percent_hotspots_reviewed() {
204 treeRootHolder.setRoot(ROOT_PROJECT);
205 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
206 newHotspot(STATUS_TO_REVIEW, null),
207 newHotspot(STATUS_TO_REVIEW, null),
208 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
209 // Should not be taken into account
211 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
212 newHotspot(STATUS_TO_REVIEW, null),
213 newHotspot(STATUS_TO_REVIEW, null),
214 newHotspot(STATUS_TO_REVIEW, null),
215 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
216 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
219 underTest.visit(ROOT_PROJECT);
221 verifyRatingAndReviewedMeasures(FILE_1_REF, D, D,33.3);
222 verifyRatingAndReviewedMeasures(FILE_2_REF, D, D,40.0);
223 verifyRatingAndReviewedMeasures(DIRECTORY_REF, D,D, 37.5);
224 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, D,37.5);
225 verifyRatingAndReviewedMeasures(PROJECT_REF, D, D,37.5);
229 void compute_rating_and_reviewed__measures_when_less_than_30_percent_hotspots_reviewed() {
230 treeRootHolder.setRoot(ROOT_PROJECT);
231 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
232 newHotspot(STATUS_TO_REVIEW, null),
233 newHotspot(STATUS_TO_REVIEW, null),
234 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
235 // Should not be taken into account
237 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
238 newHotspot(STATUS_TO_REVIEW, null),
239 newHotspot(STATUS_TO_REVIEW, null),
240 newHotspot(STATUS_TO_REVIEW, null),
243 underTest.visit(ROOT_PROJECT);
245 verifyRatingAndReviewedMeasures(FILE_1_REF, D, D,33.3);
246 verifyRatingAndReviewedMeasures(FILE_2_REF, E, D,0.0);
247 verifyRatingAndReviewedMeasures(DIRECTORY_REF, E,D, 16.7);
248 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, D,16.7);
249 verifyRatingAndReviewedMeasures(PROJECT_REF, E, D,16.7);
253 void compute_A_rating_and_no_reviewed_when_no_hotspot() {
254 treeRootHolder.setRoot(ROOT_PROJECT);
256 underTest.visit(ROOT_PROJECT);
258 verifyRatingAndReviewedMeasures(PROJECT_REF, A, A,null);
262 void compute_status_related_measures() {
263 treeRootHolder.setRoot(ROOT_PROJECT);
264 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
265 newHotspot(STATUS_TO_REVIEW, null),
266 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
267 // Should not be taken into account
269 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
270 newHotspot(STATUS_TO_REVIEW, null),
271 newHotspot(STATUS_TO_REVIEW, null),
272 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
273 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
274 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
277 underTest.visit(ROOT_PROJECT);
279 verifyHotspotStatusMeasures(FILE_1_REF, 1, 1);
280 verifyHotspotStatusMeasures(FILE_2_REF, 3, 2);
281 verifyHotspotStatusMeasures(DIRECTORY_REF, 4, 3);
282 verifyHotspotStatusMeasures(ROOT_DIR_REF, 4, 3);
283 verifyHotspotStatusMeasures(PROJECT_REF, 4, 3);
287 void compute_0_status_related_measures_when_no_hotspot() {
288 treeRootHolder.setRoot(ROOT_PROJECT);
290 underTest.visit(ROOT_PROJECT);
292 verifyHotspotStatusMeasures(PROJECT_REF, 0, 0);
295 private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, Rating expectedSoftwareQualityReviewRating,
296 @Nullable Double expectedHotspotsReviewed) {
297 verifySecurityReviewRating(componentRef, expectedReviewRating, expectedSoftwareQualityReviewRating);
298 if (expectedHotspotsReviewed != null) {
299 verifySecurityHotspotsReviewed(componentRef, expectedHotspotsReviewed);
301 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_KEY)).isEmpty();
305 private void verifySecurityReviewRating(int componentRef, Rating rating, Rating softwareQualityRating) {
306 Measure measure = measureRepository.getAddedRawMeasure(componentRef, SECURITY_REVIEW_RATING_KEY).get();
307 Measure softwareQualityMeasure = measureRepository.getAddedRawMeasure(componentRef, SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY).get();
308 assertThat(measure.getIntValue()).isEqualTo(rating.getIndex());
309 assertThat(measure.getData()).isEqualTo(rating.name());
310 assertThat(softwareQualityMeasure.getIntValue()).isEqualTo(softwareQualityRating.getIndex());
311 assertThat(softwareQualityMeasure.getData()).isEqualTo(softwareQualityRating.name());
314 private void verifySecurityHotspotsReviewed(int componentRef, double percent) {
315 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_KEY).get().getDoubleValue()).isEqualTo(percent);
318 private void verifyHotspotStatusMeasures(int componentRef, @Nullable Integer hotspotsReviewed, @Nullable Integer hotspotsToReview) {
319 if (hotspotsReviewed == null){
320 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY)).isEmpty();
322 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY).get().getIntValue()).isEqualTo(hotspotsReviewed);
324 if (hotspotsReviewed == null){
325 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY)).isEmpty();
327 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY).get().getIntValue()).isEqualTo(hotspotsToReview);
331 private static DefaultIssue newHotspot(String status, @Nullable String resolution) {
332 return new DefaultIssue()
333 .setKey(Uuids.create())
336 .setResolution(resolution)
337 .setType(RuleType.SECURITY_HOTSPOT);
340 private static DefaultIssue newIssue() {
341 return new DefaultIssue()
342 .setKey(Uuids.create())
344 .setType(RuleType.BUG);