]> source.dussan.org Git - sonarqube.git/blob
08b85755a8d6d0f27c0fb5521f1d4155f1d1fb8b
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2020 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 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;
46
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;
67
68 public class NewSecurityReviewMeasuresVisitorTest {
69
70   private static final Offset<Double> VARIATION_COMPARISON_OFFSET = Offset.offset(0.01);
71
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);
76
77   private static final String LANGUAGE_KEY_1 = "lKey1";
78
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;
84
85   private static final Component ROOT_PROJECT = builder(Component.Type.PROJECT, PROJECT_REF).setKey("project")
86     .addChildren(
87       builder(DIRECTORY, ROOT_DIR_REF).setKey("dir")
88         .addChildren(
89           builder(DIRECTORY, DIRECTORY_REF).setKey("directory")
90             .addChildren(
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())
93             .build())
94         .build())
95     .build();
96
97   @Rule
98   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
99   @Rule
100   public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
101     .add(NEW_SECURITY_REVIEW_RATING)
102     .add(NEW_SECURITY_HOTSPOTS_REVIEWED);
103   @Rule
104   public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
105   @Rule
106   public PeriodHolderRule periodsHolder = new PeriodHolderRule().setPeriod(new Period("mode", null, LEAK_PERIOD_SNAPSHOT_IN_MILLISEC));
107   @Rule
108   public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
109   @Rule
110   public ComponentIssuesRepositoryRule componentIssuesRepositoryRule = new ComponentIssuesRepositoryRule(treeRootHolder);
111   @Rule
112   public FillComponentIssuesVisitorRule fillComponentIssuesVisitorRule = new FillComponentIssuesVisitorRule(componentIssuesRepositoryRule, treeRootHolder);
113
114   private VisitorsCrawler underTest = new VisitorsCrawler(Arrays.asList(fillComponentIssuesVisitorRule,
115     new NewSecurityReviewMeasuresVisitor(componentIssuesRepositoryRule, measureRepository, periodsHolder, analysisMetadataHolder, metricRepository)));
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).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));
131
132     underTest.visit(ROOT_PROJECT);
133
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);
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).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),
159       newIssue());
160
161     underTest.visit(ROOT_PROJECT);
162
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);
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).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),
188       newIssue());
189
190     underTest.visit(ROOT_PROJECT);
191
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);
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).setCreationDate(AFTER_LEAK_PERIOD_DATE),
204       newHotspot(STATUS_REVIEWED, RESOLUTION_FIXED).setCreationDate(AFTER_LEAK_PERIOD_DATE),
205       // Should not be taken into account
206       newIssue());
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),
216       newIssue());
217
218     underTest.visit(ROOT_PROJECT);
219
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);
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).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
235       newIssue());
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),
245       newIssue());
246
247     underTest.visit(ROOT_PROJECT);
248
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);
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).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
264       newIssue());
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),
272       newIssue());
273
274     underTest.visit(ROOT_PROJECT);
275
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);
281   }
282
283   @Test
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),
289       newIssue());
290
291     underTest.visit(ROOT_PROJECT);
292
293     verifyMeasures(PROJECT_REF, A, 100.0);
294   }
295
296   @Test
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
307       newIssue());
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));
317
318     underTest.visit(ROOT_PROJECT);
319
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);
325   }
326
327   @Test
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));
334
335     underTest.visit(ROOT_PROJECT);
336
337     assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF).values()).isEmpty();
338   }
339
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);
344   }
345
346   private static DefaultIssue newHotspot(String status, @Nullable String resolution) {
347     return new DefaultIssue()
348       .setKey(Uuids.create())
349       .setSeverity(MINOR)
350       .setStatus(status)
351       .setResolution(resolution)
352       .setType(RuleType.SECURITY_HOTSPOT)
353       .setCreationDate(DEFAULT_CREATION_DATE);
354   }
355
356   private static DefaultIssue newIssue() {
357     return new DefaultIssue()
358       .setKey(Uuids.create())
359       .setSeverity(MAJOR)
360       .setType(RuleType.BUG)
361       .setCreationDate(DEFAULT_CREATION_DATE);
362   }
363
364 }