]> source.dussan.org Git - sonarqube.git/blob
2547336bd8f76ae0492a7897aa61279f471947fa
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 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.issue;
21
22 import com.google.common.collect.Iterators;
23 import com.tngtech.java.junit.dataprovider.DataProvider;
24 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
25 import com.tngtech.java.junit.dataprovider.UseDataProvider;
26 import java.util.Collection;
27 import java.util.Collections;
28 import org.junit.Rule;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.sonar.api.rule.RuleKey;
32 import org.sonar.api.rule.Severity;
33 import org.sonar.api.rules.RuleType;
34 import org.sonar.api.utils.Duration;
35 import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
36 import org.sonar.ce.task.projectanalysis.component.Component;
37 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
38 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
39 import org.sonar.ce.task.projectanalysis.issue.commonrule.CommonRuleEngine;
40 import org.sonar.ce.task.projectanalysis.issue.filter.IssueFilter;
41 import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRule;
42 import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolderRule;
43 import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
44 import org.sonar.core.issue.DefaultIssue;
45 import org.sonar.core.issue.tracking.Input;
46 import org.sonar.db.protobuf.DbIssues;
47 import org.sonar.scanner.protocol.Constants;
48 import org.sonar.scanner.protocol.output.ScannerReport;
49 import org.sonar.scanner.protocol.output.ScannerReport.IssueType;
50 import org.sonar.scanner.protocol.output.ScannerReport.TextRange;
51 import org.sonar.server.rule.CommonRuleKeys;
52
53 import static java.util.Arrays.asList;
54 import static java.util.Collections.emptyMap;
55 import static java.util.Collections.singletonList;
56 import static org.assertj.core.api.Assertions.assertThat;
57 import static org.mockito.ArgumentMatchers.any;
58 import static org.mockito.ArgumentMatchers.eq;
59 import static org.mockito.Mockito.mock;
60 import static org.mockito.Mockito.when;
61 import static org.sonar.api.issue.Issue.STATUS_OPEN;
62 import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
63
64 @RunWith(DataProviderRunner.class)
65 public class TrackerRawInputFactoryTest {
66
67   private static final String FILE_UUID = "fake_uuid";
68   private static final String ANOTHER_FILE_UUID = "another_fake_uuid";
69   private static int FILE_REF = 2;
70   private static int NOT_IN_REPORT_FILE_REF = 3;
71   private static int ANOTHER_FILE_REF = 4;
72
73   private static ReportComponent FILE = ReportComponent.builder(Component.Type.FILE, FILE_REF).setUuid(FILE_UUID).build();
74   private static ReportComponent ANOTHER_FILE = ReportComponent.builder(Component.Type.FILE, ANOTHER_FILE_REF).setUuid(ANOTHER_FILE_UUID).build();
75   private static ReportComponent PROJECT = ReportComponent.builder(Component.Type.PROJECT, 1).addChildren(FILE, ANOTHER_FILE).build();
76
77   @Rule
78   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(PROJECT);
79   @Rule
80   public BatchReportReaderRule reportReader = new BatchReportReaderRule();
81   @Rule
82   public ActiveRulesHolderRule activeRulesHolder = new ActiveRulesHolderRule();
83   @Rule
84   public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
85
86   private SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class);
87   private CommonRuleEngine commonRuleEngine = mock(CommonRuleEngine.class);
88   private IssueFilter issueFilter = mock(IssueFilter.class);
89   private TrackerRawInputFactory underTest = new TrackerRawInputFactory(treeRootHolder, reportReader, sourceLinesHash,
90     commonRuleEngine, issueFilter, ruleRepository, activeRulesHolder);
91
92   @Test
93   public void load_source_hash_sequences() {
94     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
95     Input<DefaultIssue> input = underTest.create(FILE);
96
97     assertThat(input.getLineHashSequence()).isNotNull();
98     assertThat(input.getLineHashSequence().getHashForLine(1)).isEqualTo("line");
99     assertThat(input.getLineHashSequence().getHashForLine(2)).isEmpty();
100     assertThat(input.getLineHashSequence().getHashForLine(3)).isEmpty();
101
102     assertThat(input.getBlockHashSequence()).isNotNull();
103   }
104
105   @Test
106   public void load_source_hash_sequences_only_on_files() {
107     Input<DefaultIssue> input = underTest.create(PROJECT);
108
109     assertThat(input.getLineHashSequence()).isNotNull();
110     assertThat(input.getBlockHashSequence()).isNotNull();
111   }
112
113   @Test
114   public void load_issues_from_report() {
115     RuleKey ruleKey = RuleKey.of("java", "S001");
116     markRuleAsActive(ruleKey);
117     when(issueFilter.accept(any(), eq(FILE))).thenReturn(true);
118
119     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
120     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
121       .setTextRange(TextRange.newBuilder().setStartLine(2).build())
122       .setMsg("the message")
123       .setRuleRepository(ruleKey.repository())
124       .setRuleKey(ruleKey.rule())
125       .setSeverity(Constants.Severity.BLOCKER)
126       .setGap(3.14)
127       .setQuickFixAvailable(true)
128       .build();
129     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
130     Input<DefaultIssue> input = underTest.create(FILE);
131
132     Collection<DefaultIssue> issues = input.getIssues();
133     assertThat(issues).hasSize(1);
134     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
135
136     // fields set by analysis report
137     assertThat(issue.ruleKey()).isEqualTo(ruleKey);
138     assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
139     assertThat(issue.line()).isEqualTo(2);
140     assertThat(issue.gap()).isEqualTo(3.14);
141     assertThat(issue.message()).isEqualTo("the message");
142     assertThat(issue.isQuickFixAvailable()).isTrue();
143
144     // fields set by compute engine
145     assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
146     assertThat(issue.tags()).isEmpty();
147     assertInitializedIssue(issue);
148     assertThat(issue.effort()).isNull();
149   }
150
151   @Test
152   public void set_rule_name_as_message_when_issue_message_from_report_is_empty() {
153     RuleKey ruleKey = RuleKey.of("java", "S001");
154     markRuleAsActive(ruleKey);
155     registerRule(ruleKey, "Rule 1");
156     when(issueFilter.accept(any(), eq(FILE))).thenReturn(true);
157     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
158     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
159       .setRuleRepository(ruleKey.repository())
160       .setRuleKey(ruleKey.rule())
161       .setMsg("")
162       .build();
163     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
164     Input<DefaultIssue> input = underTest.create(FILE);
165
166     Collection<DefaultIssue> issues = input.getIssues();
167     assertThat(issues).hasSize(1);
168     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
169
170     // fields set by analysis report
171     assertThat(issue.ruleKey()).isEqualTo(ruleKey);
172
173     // fields set by compute engine
174     assertInitializedIssue(issue);
175     assertThat(issue.message()).isEqualTo("Rule 1");
176   }
177
178   // SONAR-10781
179   @Test
180   public void load_issues_from_report_missing_secondary_location_component() {
181     RuleKey ruleKey = RuleKey.of("java", "S001");
182     markRuleAsActive(ruleKey);
183     when(issueFilter.accept(any(), eq(FILE))).thenReturn(true);
184
185     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
186     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
187       .setTextRange(TextRange.newBuilder().setStartLine(2).build())
188       .setMsg("the message")
189       .setRuleRepository(ruleKey.repository())
190       .setRuleKey(ruleKey.rule())
191       .setSeverity(Constants.Severity.BLOCKER)
192       .setGap(3.14)
193       .addFlow(ScannerReport.Flow.newBuilder()
194         .addLocation(ScannerReport.IssueLocation.newBuilder()
195           .setComponentRef(FILE_REF)
196           .setMsg("Secondary location in same file")
197           .setTextRange(TextRange.newBuilder().setStartLine(2).build()))
198         .addLocation(ScannerReport.IssueLocation.newBuilder()
199           .setComponentRef(NOT_IN_REPORT_FILE_REF)
200           .setMsg("Secondary location in a missing file")
201           .setTextRange(TextRange.newBuilder().setStartLine(3).build()))
202         .addLocation(ScannerReport.IssueLocation.newBuilder()
203           .setComponentRef(ANOTHER_FILE_REF)
204           .setMsg("Secondary location in another file")
205           .setTextRange(TextRange.newBuilder().setStartLine(3).build()))
206         .build())
207       .build();
208     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
209     Input<DefaultIssue> input = underTest.create(FILE);
210
211     Collection<DefaultIssue> issues = input.getIssues();
212     assertThat(issues).hasSize(1);
213     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
214
215     DbIssues.Locations locations = issue.getLocations();
216     // fields set by analysis report
217     assertThat(locations.getFlowList()).hasSize(1);
218     assertThat(locations.getFlow(0).getLocationList()).hasSize(2);
219     // Not component id if location is in the same file
220     assertThat(locations.getFlow(0).getLocation(0).getComponentId()).isEmpty();
221     assertThat(locations.getFlow(0).getLocation(1).getComponentId()).isEqualTo(ANOTHER_FILE_UUID);
222   }
223
224   @Test
225   @UseDataProvider("ruleTypeAndStatusByIssueType")
226   public void load_external_issues_from_report(IssueType issueType, RuleType expectedRuleType, String expectedStatus) {
227     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
228     ScannerReport.ExternalIssue reportIssue = ScannerReport.ExternalIssue.newBuilder()
229       .setTextRange(TextRange.newBuilder().setStartLine(2).build())
230       .setMsg("the message")
231       .setEngineId("eslint")
232       .setRuleId("S001")
233       .setSeverity(Constants.Severity.BLOCKER)
234       .setEffort(20L)
235       .setType(issueType)
236       .build();
237     reportReader.putExternalIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
238     Input<DefaultIssue> input = underTest.create(FILE);
239
240     Collection<DefaultIssue> issues = input.getIssues();
241     assertThat(issues).hasSize(1);
242     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
243
244     // fields set by analysis report
245     assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("external_eslint", "S001"));
246     assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
247     assertThat(issue.line()).isEqualTo(2);
248     assertThat(issue.effort()).isEqualTo(Duration.create(20L));
249     assertThat(issue.message()).isEqualTo("the message");
250     assertThat(issue.type()).isEqualTo(expectedRuleType);
251
252     // fields set by compute engine
253     assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
254     assertThat(issue.tags()).isEmpty();
255     assertInitializedExternalIssue(issue, expectedStatus);
256   }
257
258   @DataProvider
259   public static Object[][] ruleTypeAndStatusByIssueType() {
260     return new Object[][] {
261       {IssueType.CODE_SMELL, RuleType.CODE_SMELL, STATUS_OPEN},
262       {IssueType.BUG, RuleType.BUG, STATUS_OPEN},
263       {IssueType.VULNERABILITY, RuleType.VULNERABILITY, STATUS_OPEN},
264       {IssueType.SECURITY_HOTSPOT, RuleType.SECURITY_HOTSPOT, STATUS_TO_REVIEW}
265     };
266   }
267
268   @Test
269   @UseDataProvider("ruleTypeAndStatusByIssueType")
270   public void load_external_issues_from_report_with_default_effort(IssueType issueType, RuleType expectedRuleType, String expectedStatus) {
271     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
272     ScannerReport.ExternalIssue reportIssue = ScannerReport.ExternalIssue.newBuilder()
273       .setTextRange(TextRange.newBuilder().setStartLine(2).build())
274       .setMsg("the message")
275       .setEngineId("eslint")
276       .setRuleId("S001")
277       .setSeverity(Constants.Severity.BLOCKER)
278       .setType(issueType)
279       .build();
280     reportReader.putExternalIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
281     Input<DefaultIssue> input = underTest.create(FILE);
282
283     Collection<DefaultIssue> issues = input.getIssues();
284     assertThat(issues).hasSize(1);
285     DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
286
287     // fields set by analysis report
288     assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("external_eslint", "S001"));
289     assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
290     assertThat(issue.line()).isEqualTo(2);
291     assertThat(issue.effort()).isEqualTo(Duration.create(0L));
292     assertThat(issue.message()).isEqualTo("the message");
293     assertThat(issue.type()).isEqualTo(expectedRuleType);
294
295     // fields set by compute engine
296     assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
297     assertThat(issue.tags()).isEmpty();
298     assertInitializedExternalIssue(issue, expectedStatus);
299   }
300
301   @Test
302   public void excludes_issues_on_inactive_rules() {
303     RuleKey ruleKey = RuleKey.of("java", "S001");
304     when(issueFilter.accept(any(), eq(FILE))).thenReturn(true);
305
306     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
307     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
308       .setTextRange(TextRange.newBuilder().setStartLine(2).build())
309       .setMsg("the message")
310       .setRuleRepository(ruleKey.repository())
311       .setRuleKey(ruleKey.rule())
312       .setSeverity(Constants.Severity.BLOCKER)
313       .setGap(3.14)
314       .build();
315     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
316     Input<DefaultIssue> input = underTest.create(FILE);
317
318     Collection<DefaultIssue> issues = input.getIssues();
319     assertThat(issues).isEmpty();
320   }
321
322   @Test
323   public void filter_excludes_issues_from_report() {
324     RuleKey ruleKey = RuleKey.of("java", "S001");
325     markRuleAsActive(ruleKey);
326     when(issueFilter.accept(any(), eq(FILE))).thenReturn(false);
327     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
328     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
329       .setTextRange(TextRange.newBuilder().setStartLine(2).build())
330       .setMsg("the message")
331       .setRuleRepository(ruleKey.repository())
332       .setRuleKey(ruleKey.rule())
333       .setSeverity(Constants.Severity.BLOCKER)
334       .setGap(3.14)
335       .build();
336     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
337     Input<DefaultIssue> input = underTest.create(FILE);
338
339     Collection<DefaultIssue> issues = input.getIssues();
340     assertThat(issues).isEmpty();
341   }
342
343   @Test
344   public void exclude_issues_on_common_rules() {
345     RuleKey ruleKey = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), "S001");
346     markRuleAsActive(ruleKey);
347     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
348     ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
349       .setMsg("the message")
350       .setRuleRepository(ruleKey.repository())
351       .setRuleKey(ruleKey.rule())
352       .setSeverity(Constants.Severity.BLOCKER)
353       .build();
354     reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
355
356     Input<DefaultIssue> input = underTest.create(FILE);
357
358     assertThat(input.getIssues()).isEmpty();
359   }
360
361   @Test
362   public void load_issues_of_compute_engine_common_rules() {
363     RuleKey ruleKey = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), "InsufficientCoverage");
364     markRuleAsActive(ruleKey);
365     when(issueFilter.accept(any(), eq(FILE))).thenReturn(true);
366     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
367     DefaultIssue ceIssue = new DefaultIssue()
368       .setRuleKey(ruleKey)
369       .setMessage("not enough coverage")
370       .setGap(10.0);
371     when(commonRuleEngine.process(FILE)).thenReturn(singletonList(ceIssue));
372
373     Input<DefaultIssue> input = underTest.create(FILE);
374
375     assertThat(input.getIssues()).containsOnly(ceIssue);
376     assertInitializedIssue(input.getIssues().iterator().next());
377   }
378
379   @Test
380   public void filter_exclude_issues_on_common_rule() {
381     RuleKey ruleKey = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), "InsufficientCoverage");
382     markRuleAsActive(ruleKey);
383     when(issueFilter.accept(any(), eq(FILE))).thenReturn(false);
384     when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
385     DefaultIssue ceIssue = new DefaultIssue()
386       .setRuleKey(ruleKey)
387       .setMessage("not enough coverage")
388       .setGap(10.0);
389     when(commonRuleEngine.process(FILE)).thenReturn(singletonList(ceIssue));
390
391     Input<DefaultIssue> input = underTest.create(FILE);
392
393     assertThat(input.getIssues()).isEmpty();
394   }
395
396   private void assertInitializedIssue(DefaultIssue issue) {
397     assertInitializedExternalIssue(issue, STATUS_OPEN);
398     assertThat(issue.effort()).isNull();
399     assertThat(issue.effortInMinutes()).isNull();
400   }
401
402   private void assertInitializedExternalIssue(DefaultIssue issue, String expectedStatus) {
403     assertThat(issue.projectKey()).isEqualTo(PROJECT.getKey());
404     assertThat(issue.componentKey()).isEqualTo(FILE.getKey());
405     assertThat(issue.componentUuid()).isEqualTo(FILE.getUuid());
406     assertThat(issue.resolution()).isNull();
407     assertThat(issue.status()).isEqualTo(expectedStatus);
408     assertThat(issue.key()).isNull();
409     assertThat(issue.authorLogin()).isNull();
410   }
411
412   private void markRuleAsActive(RuleKey ruleKey) {
413     activeRulesHolder.put(new ActiveRule(ruleKey, Severity.CRITICAL, emptyMap(), 1_000L, null, "qp1"));
414   }
415
416   private void registerRule(RuleKey ruleKey, String name) {
417     DumbRule dumbRule = new DumbRule(ruleKey);
418     dumbRule.setName(name);
419     ruleRepository.add(dumbRule);
420   }
421
422
423 }