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.Rule;
24 import org.junit.Test;
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;
63 public class SecurityReviewMeasuresVisitorTest {
65 private static final int PROJECT_REF = 1;
66 private static final int ROOT_DIR_REF = 12;
67 private static final int DIRECTORY_REF = 123;
68 private static final int FILE_1_REF = 1231;
69 private static final int FILE_2_REF = 1232;
71 static final Component ROOT_PROJECT = builder(Component.Type.PROJECT, PROJECT_REF).setKey("project")
73 builder(DIRECTORY, ROOT_DIR_REF).setKey("dir")
75 builder(DIRECTORY, DIRECTORY_REF).setKey("directory")
77 builder(FILE, FILE_1_REF).setKey("file1").build(),
78 builder(FILE, FILE_2_REF).setKey("file2").build())
84 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
86 public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
87 .add(SECURITY_REVIEW_RATING)
88 .add(SECURITY_HOTSPOTS_REVIEWED)
89 .add(SECURITY_HOTSPOTS_REVIEWED_STATUS)
90 .add(SECURITY_HOTSPOTS_TO_REVIEW_STATUS);
92 public ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder);
94 public FillComponentIssuesVisitorRule fillComponentIssuesVisitorRule = new FillComponentIssuesVisitorRule(componentIssuesRepositoryRule, treeRootHolder);
96 public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
98 private VisitorsCrawler underTest = new VisitorsCrawler(asList(fillComponentIssuesVisitorRule,
99 new SecurityReviewMeasuresVisitor(componentIssuesRepositoryRule, measureRepository, metricRepository)));
102 public void compute_rating_and_reviewed_measures_when_100_percent_hotspots_reviewed() {
103 treeRootHolder.setRoot(ROOT_PROJECT);
104 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
105 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
106 // Should not be taken into account
108 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
109 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
110 newHotspot(STATUS_REVIEWED, RESOLUTION_SAFE),
113 underTest.visit(ROOT_PROJECT);
115 verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
116 verifyRatingAndReviewedMeasures(FILE_2_REF, A, 100.0);
117 verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 100.0);
118 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 100.0);
119 verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
123 public void compute_rating_and_reviewed__measures_when_more_than_80_percent_hotspots_reviewed() {
124 treeRootHolder.setRoot(ROOT_PROJECT);
125 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
126 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
127 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
128 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
129 // Should not be taken into account
131 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
132 newHotspot(STATUS_TO_REVIEW, null),
133 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
134 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
135 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
136 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
139 underTest.visit(ROOT_PROJECT);
141 verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
142 verifyRatingAndReviewedMeasures(FILE_2_REF, A, 80.0);
143 verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 87.5);
144 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 87.5);
145 verifyRatingAndReviewedMeasures(PROJECT_REF, A, 87.5);
149 public void compute_rating_and_reviewed__measures_when_more_than_70_percent_hotspots_reviewed() {
150 treeRootHolder.setRoot(ROOT_PROJECT);
151 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
152 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
153 // Should not be taken into account
155 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
156 newHotspot(STATUS_TO_REVIEW, null),
157 newHotspot(STATUS_TO_REVIEW, null),
158 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
159 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
160 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
161 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
162 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
165 underTest.visit(ROOT_PROJECT);
167 verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
168 verifyRatingAndReviewedMeasures(FILE_2_REF, B, 71.4);
169 verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, 75.0);
170 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, 75.0);
171 verifyRatingAndReviewedMeasures(PROJECT_REF, B, 75.0);
175 public void compute_rating_and_reviewed__measures_when_more_than_50_percent_hotspots_reviewed() {
176 treeRootHolder.setRoot(ROOT_PROJECT);
177 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
178 newHotspot(STATUS_TO_REVIEW, null),
179 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
180 // Should not be taken into account
182 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
183 newHotspot(STATUS_TO_REVIEW, null),
184 newHotspot(STATUS_TO_REVIEW, null),
185 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
186 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
187 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
190 underTest.visit(ROOT_PROJECT);
192 verifyRatingAndReviewedMeasures(FILE_1_REF, C, 50.0);
193 verifyRatingAndReviewedMeasures(FILE_2_REF, C, 60.0);
194 verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, 57.1);
195 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, 57.1);
196 verifyRatingAndReviewedMeasures(PROJECT_REF, C, 57.1);
200 public void compute_rating_and_reviewed__measures_when_more_30_than_percent_hotspots_reviewed() {
201 treeRootHolder.setRoot(ROOT_PROJECT);
202 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
203 newHotspot(STATUS_TO_REVIEW, null),
204 newHotspot(STATUS_TO_REVIEW, null),
205 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
206 // Should not be taken into account
208 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
209 newHotspot(STATUS_TO_REVIEW, null),
210 newHotspot(STATUS_TO_REVIEW, null),
211 newHotspot(STATUS_TO_REVIEW, null),
212 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
213 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
216 underTest.visit(ROOT_PROJECT);
218 verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.3);
219 verifyRatingAndReviewedMeasures(FILE_2_REF, D, 40.0);
220 verifyRatingAndReviewedMeasures(DIRECTORY_REF, D, 37.5);
221 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, 37.5);
222 verifyRatingAndReviewedMeasures(PROJECT_REF, D, 37.5);
226 public void compute_rating_and_reviewed__measures_when_less_than_30_percent_hotspots_reviewed() {
227 treeRootHolder.setRoot(ROOT_PROJECT);
228 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
229 newHotspot(STATUS_TO_REVIEW, null),
230 newHotspot(STATUS_TO_REVIEW, null),
231 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
232 // Should not be taken into account
234 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
235 newHotspot(STATUS_TO_REVIEW, null),
236 newHotspot(STATUS_TO_REVIEW, null),
237 newHotspot(STATUS_TO_REVIEW, null),
240 underTest.visit(ROOT_PROJECT);
242 verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.3);
243 verifyRatingAndReviewedMeasures(FILE_2_REF, E, 0.0);
244 verifyRatingAndReviewedMeasures(DIRECTORY_REF, E, 16.7);
245 verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, 16.7);
246 verifyRatingAndReviewedMeasures(PROJECT_REF, E, 16.7);
250 public void compute_A_rating_and_no_reviewed_when_no_hotspot() {
251 treeRootHolder.setRoot(ROOT_PROJECT);
253 underTest.visit(ROOT_PROJECT);
255 verifyRatingAndReviewedMeasures(PROJECT_REF, A, null);
259 public void compute_status_related_measures() {
260 treeRootHolder.setRoot(ROOT_PROJECT);
261 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
262 newHotspot(STATUS_TO_REVIEW, null),
263 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
264 // Should not be taken into account
266 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
267 newHotspot(STATUS_TO_REVIEW, null),
268 newHotspot(STATUS_TO_REVIEW, null),
269 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
270 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
271 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED),
274 underTest.visit(ROOT_PROJECT);
276 verifyHotspotStatusMeasures(FILE_1_REF, 1, 1);
277 verifyHotspotStatusMeasures(FILE_2_REF, 3, 2);
278 verifyHotspotStatusMeasures(DIRECTORY_REF, 4, 3);
279 verifyHotspotStatusMeasures(ROOT_DIR_REF, 4, 3);
280 verifyHotspotStatusMeasures(PROJECT_REF, 4, 3);
284 public void compute_0_status_related_measures_when_no_hotspot() {
285 treeRootHolder.setRoot(ROOT_PROJECT);
287 underTest.visit(ROOT_PROJECT);
289 verifyHotspotStatusMeasures(PROJECT_REF, 0, 0);
292 private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, @Nullable Double expectedHotspotsReviewed) {
293 verifySecurityReviewRating(componentRef, expectedReviewRating);
294 if (expectedHotspotsReviewed != null) {
295 verifySecurityHotspotsReviewed(componentRef, expectedHotspotsReviewed);
297 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_KEY)).isEmpty();
301 private void verifySecurityReviewRating(int componentRef, Rating rating) {
302 Measure measure = measureRepository.getAddedRawMeasure(componentRef, SECURITY_REVIEW_RATING_KEY).get();
303 assertThat(measure.getIntValue()).isEqualTo(rating.getIndex());
304 assertThat(measure.getData()).isEqualTo(rating.name());
307 private void verifySecurityHotspotsReviewed(int componentRef, double percent) {
308 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_KEY).get().getDoubleValue()).isEqualTo(percent);
311 private void verifyHotspotStatusMeasures(int componentRef, @Nullable Integer hotspotsReviewed, @Nullable Integer hotspotsToReview) {
312 if (hotspotsReviewed == null){
313 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY)).isEmpty();
315 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY).get().getIntValue()).isEqualTo(hotspotsReviewed);
317 if (hotspotsReviewed == null){
318 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY)).isEmpty();
320 assertThat(measureRepository.getAddedRawMeasure(componentRef, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY).get().getIntValue()).isEqualTo(hotspotsToReview);
324 private static DefaultIssue newHotspot(String status, @Nullable String resolution) {
325 return new DefaultIssue()
326 .setKey(Uuids.create())
329 .setResolution(resolution)
330 .setType(RuleType.SECURITY_HOTSPOT);
333 private static DefaultIssue newIssue() {
334 return new DefaultIssue()
335 .setKey(Uuids.create())
337 .setType(RuleType.BUG);