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