3 * Copyright (C) 2009-2020 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 java.util.Arrays;
23 import java.util.Date;
24 import javax.annotation.Nullable;
25 import org.assertj.core.data.Offset;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.sonar.api.rules.RuleType;
29 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
30 import org.sonar.ce.task.projectanalysis.analysis.Branch;
31 import org.sonar.ce.task.projectanalysis.component.Component;
32 import org.sonar.ce.task.projectanalysis.component.FileAttributes;
33 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
34 import org.sonar.ce.task.projectanalysis.component.VisitorsCrawler;
35 import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesRepositoryRule;
36 import org.sonar.ce.task.projectanalysis.issue.FillComponentIssuesVisitorRule;
37 import org.sonar.ce.task.projectanalysis.measure.MeasureAssert;
38 import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
39 import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
40 import org.sonar.ce.task.projectanalysis.period.Period;
41 import org.sonar.ce.task.projectanalysis.period.PeriodHolderRule;
42 import org.sonar.core.issue.DefaultIssue;
43 import org.sonar.core.util.Uuids;
44 import org.sonar.db.component.BranchType;
45 import org.sonar.server.measure.Rating;
47 import static org.assertj.core.api.Assertions.assertThat;
48 import static org.mockito.Mockito.mock;
49 import static org.mockito.Mockito.when;
50 import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
51 import static org.sonar.api.issue.Issue.STATUS_REVIEWED;
52 import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
53 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED;
54 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_KEY;
55 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING;
56 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY;
57 import static org.sonar.api.rule.Severity.MAJOR;
58 import static org.sonar.api.rule.Severity.MINOR;
59 import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
60 import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
61 import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
62 import static org.sonar.server.measure.Rating.A;
63 import static org.sonar.server.measure.Rating.B;
64 import static org.sonar.server.measure.Rating.C;
65 import static org.sonar.server.measure.Rating.D;
66 import static org.sonar.server.measure.Rating.E;
68 public class NewSecurityReviewMeasuresVisitorTest {
70 private static final Offset<Double> VARIATION_COMPARISON_OFFSET = Offset.offset(0.01);
72 private static final long LEAK_PERIOD_SNAPSHOT_IN_MILLISEC = 12323l;
73 private static final Date DEFAULT_CREATION_DATE = new Date(1000l);
74 private static final Date BEFORE_LEAK_PERIOD_DATE = new Date(LEAK_PERIOD_SNAPSHOT_IN_MILLISEC - 5000L);
75 private static final Date AFTER_LEAK_PERIOD_DATE = new Date(LEAK_PERIOD_SNAPSHOT_IN_MILLISEC + 5000L);
77 private static final String LANGUAGE_KEY_1 = "lKey1";
79 private static final int PROJECT_REF = 1;
80 private static final int ROOT_DIR_REF = 12;
81 private static final int DIRECTORY_REF = 123;
82 private static final int FILE_1_REF = 1231;
83 private static final int FILE_2_REF = 1232;
85 private static final Component ROOT_PROJECT = builder(Component.Type.PROJECT, PROJECT_REF).setKey("project")
87 builder(DIRECTORY, ROOT_DIR_REF).setKey("dir")
89 builder(DIRECTORY, DIRECTORY_REF).setKey("directory")
91 builder(FILE, FILE_1_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_KEY_1, 1)).setKey("file1").build(),
92 builder(FILE, FILE_2_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_KEY_1, 1)).setKey("file2").build())
98 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
100 public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
101 .add(NEW_SECURITY_REVIEW_RATING)
102 .add(NEW_SECURITY_HOTSPOTS_REVIEWED);
104 public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
106 public PeriodHolderRule periodsHolder = new PeriodHolderRule().setPeriod(new Period("mode", null, LEAK_PERIOD_SNAPSHOT_IN_MILLISEC));
108 public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
110 public ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder);
112 public FillComponentIssuesVisitorRule fillComponentIssuesVisitorRule = new FillComponentIssuesVisitorRule(componentIssuesRepositoryRule, treeRootHolder);
114 private VisitorsCrawler underTest = new VisitorsCrawler(Arrays.asList(fillComponentIssuesVisitorRule,
115 new NewSecurityReviewMeasuresVisitor(componentIssuesRepositoryRule, measureRepository, periodsHolder, analysisMetadataHolder, metricRepository)));
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).setCreationDate(AFTER_LEAK_PERIOD_DATE),
122 // Should not be taken into account
123 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
124 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
125 newIssue().setCreationDate(AFTER_LEAK_PERIOD_DATE));
126 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
127 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
128 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE));
129 fillComponentIssuesVisitorRule.setIssues(ROOT_DIR_REF,
130 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE));
132 underTest.visit(ROOT_PROJECT);
134 verifyMeasures(FILE_1_REF, A, 100.0);
135 verifyMeasures(FILE_2_REF, A, 100.0);
136 verifyMeasures(DIRECTORY_REF, A, 100.0);
137 verifyMeasures(ROOT_DIR_REF, A, 100.0);
138 verifyMeasures(PROJECT_REF, A, 100.0);
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).setCreationDate(AFTER_LEAK_PERIOD_DATE),
146 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
147 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
148 // Should not be taken into account
149 newIssue().setCreationDate(AFTER_LEAK_PERIOD_DATE));
150 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
151 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
152 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
153 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
154 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
155 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
156 // Should not be taken into account
157 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
158 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
161 underTest.visit(ROOT_PROJECT);
163 verifyMeasures(FILE_1_REF, A, 100.0);
164 verifyMeasures(FILE_2_REF, A, 80.0);
165 verifyMeasures(DIRECTORY_REF, A, 87.5);
166 verifyMeasures(ROOT_DIR_REF, A, 87.5);
167 verifyMeasures(PROJECT_REF, A, 87.5);
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).setCreationDate(AFTER_LEAK_PERIOD_DATE),
175 // Should not be taken into account
176 newIssue().setCreationDate(AFTER_LEAK_PERIOD_DATE));
177 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
178 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
179 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
180 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
181 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
182 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
183 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
184 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
185 // Should not be taken into account
186 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
187 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
190 underTest.visit(ROOT_PROJECT);
192 verifyMeasures(FILE_1_REF, A, 100.0);
193 verifyMeasures(FILE_2_REF, B, 71.42);
194 verifyMeasures(DIRECTORY_REF, B, 75.0);
195 verifyMeasures(ROOT_DIR_REF, B, 75.0);
196 verifyMeasures(PROJECT_REF, B, 75.0);
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).setCreationDate(AFTER_LEAK_PERIOD_DATE),
204 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
205 // Should not be taken into account
207 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
208 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
209 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
210 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
211 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
212 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
213 // Should not be taken into account
214 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
215 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
218 underTest.visit(ROOT_PROJECT);
220 verifyMeasures(FILE_1_REF, C, 50.0);
221 verifyMeasures(FILE_2_REF, C, 60.0);
222 verifyMeasures(DIRECTORY_REF, C, 57.14);
223 verifyMeasures(ROOT_DIR_REF, C, 57.14);
224 verifyMeasures(PROJECT_REF, C, 57.14);
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).setCreationDate(AFTER_LEAK_PERIOD_DATE),
232 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
233 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
234 // Should not be taken into account
236 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
237 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
238 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
239 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
240 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
241 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
242 // Should not be taken into account
243 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
244 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
247 underTest.visit(ROOT_PROJECT);
249 verifyMeasures(FILE_1_REF, D, 33.33);
250 verifyMeasures(FILE_2_REF, D, 40.0);
251 verifyMeasures(DIRECTORY_REF, D, 37.5);
252 verifyMeasures(ROOT_DIR_REF, D, 37.5);
253 verifyMeasures(PROJECT_REF, D, 37.5);
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).setCreationDate(AFTER_LEAK_PERIOD_DATE),
261 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
262 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
263 // Should not be taken into account
265 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
266 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
267 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
268 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
269 // Should not be taken into account
270 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
271 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
274 underTest.visit(ROOT_PROJECT);
276 verifyMeasures(FILE_1_REF, D, 33.33);
277 verifyMeasures(FILE_2_REF, E, 0.0);
278 verifyMeasures(DIRECTORY_REF, E, 16.66);
279 verifyMeasures(ROOT_DIR_REF, E, 16.66);
280 verifyMeasures(PROJECT_REF, E, 16.66);
284 public void compute_A_rating_and_100_percent_when_no_new_hotspot_on_new_code() {
285 treeRootHolder.setRoot(ROOT_PROJECT);
286 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
287 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
288 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
291 underTest.visit(ROOT_PROJECT);
293 verifyMeasures(PROJECT_REF, A, 100.0);
297 public void compute_measures_on_pr() {
298 periodsHolder.setPeriod(null);
299 Branch b = mock(Branch.class);
300 when(b.getType()).thenReturn(BranchType.PULL_REQUEST);
301 analysisMetadataHolder.setBranch(b);
302 treeRootHolder.setRoot(ROOT_PROJECT);
303 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
304 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
305 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
306 // Should not be taken into account
308 fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
309 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
310 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(AFTER_LEAK_PERIOD_DATE),
311 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
312 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
313 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
314 // Dates is not taken into account on PR
315 newHotspot(STATUS_TO_REVIEW, null).setCreationDate(BEFORE_LEAK_PERIOD_DATE),
316 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(BEFORE_LEAK_PERIOD_DATE));
318 underTest.visit(ROOT_PROJECT);
320 verifyMeasures(FILE_1_REF, C, 50.0);
321 verifyMeasures(FILE_2_REF, C, 57.14);
322 verifyMeasures(DIRECTORY_REF, C, 55.55);
323 verifyMeasures(ROOT_DIR_REF, C, 55.55);
324 verifyMeasures(PROJECT_REF, C, 55.55);
328 public void no_measure_if_there_is_no_period() {
329 periodsHolder.setPeriod(null);
330 treeRootHolder.setRoot(ROOT_PROJECT);
331 fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
332 newHotspot(STATUS_TO_REVIEW, null),
333 newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED));
335 underTest.visit(ROOT_PROJECT);
337 assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF).values()).isEmpty();
340 private void verifyMeasures(int componentRef, Rating expectedReviewRating, double expectedHotspotsReviewed) {
341 MeasureAssert.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_REVIEW_RATING_KEY)).hasVariation(expectedReviewRating.getIndex());
342 MeasureAssert.assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY)).hasVariation(expectedHotspotsReviewed,
343 VARIATION_COMPARISON_OFFSET);
346 private static DefaultIssue newHotspot(String status, @Nullable String resolution) {
347 return new DefaultIssue()
348 .setKey(Uuids.create())
351 .setResolution(resolution)
352 .setType(RuleType.SECURITY_HOTSPOT)
353 .setCreationDate(DEFAULT_CREATION_DATE);
356 private static DefaultIssue newIssue() {
357 return new DefaultIssue()
358 .setKey(Uuids.create())
360 .setType(RuleType.BUG)
361 .setCreationDate(DEFAULT_CREATION_DATE);