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.issue;
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;
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;
58 public class TrackerRawInputFactoryTest {
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;
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();
71 public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(PROJECT);
73 public BatchReportReaderRule reportReader = new BatchReportReaderRule();
75 public ActiveRulesHolderRule activeRulesHolder = new ActiveRulesHolderRule();
77 public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
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);
86 public void load_source_hash_sequences() {
87 when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line"));
88 Input<DefaultIssue> input = underTest.create(FILE);
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();
95 assertThat(input.getBlockHashSequence()).isNotNull();
99 public void load_source_hash_sequences_only_on_files() {
100 Input<DefaultIssue> input = underTest.create(PROJECT);
102 assertThat(input.getLineHashSequence()).isNotNull();
103 assertThat(input.getBlockHashSequence()).isNotNull();
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);
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)
121 reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
122 Input<DefaultIssue> input = underTest.create(FILE);
124 Collection<DefaultIssue> issues = input.getIssues();
125 assertThat(issues).hasSize(1);
126 DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
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");
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();
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())
154 reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
155 Input<DefaultIssue> input = underTest.create(FILE);
157 Collection<DefaultIssue> issues = input.getIssues();
158 assertThat(issues).hasSize(1);
159 DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
161 // fields set by analysis report
162 assertThat(issue.ruleKey()).isEqualTo(ruleKey);
164 // fields set by compute engine
165 assertInitializedIssue(issue);
166 assertThat(issue.message()).isEqualTo("Rule 1");
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);
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)
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()))
199 reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
200 Input<DefaultIssue> input = underTest.create(FILE);
202 Collection<DefaultIssue> issues = input.getIssues();
203 assertThat(issues).hasSize(1);
204 DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
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);
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")
223 .setSeverity(Constants.Severity.BLOCKER)
225 .setType(ScannerReport.IssueType.SECURITY_HOTSPOT)
227 reportReader.putExternalIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
228 Input<DefaultIssue> input = underTest.create(FILE);
230 Collection<DefaultIssue> issues = input.getIssues();
231 assertThat(issues).hasSize(1);
232 DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
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);
242 // fields set by compute engine
243 assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
244 assertThat(issue.tags()).isEmpty();
245 assertInitializedExternalIssue(issue);
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")
256 .setSeverity(Constants.Severity.BLOCKER)
257 .setType(ScannerReport.IssueType.BUG)
259 reportReader.putExternalIssues(FILE.getReportAttributes().getRef(), asList(reportIssue));
260 Input<DefaultIssue> input = underTest.create(FILE);
262 Collection<DefaultIssue> issues = input.getIssues();
263 assertThat(issues).hasSize(1);
264 DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
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");
273 // fields set by compute engine
274 assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
275 assertThat(issue.tags()).isEmpty();
276 assertInitializedExternalIssue(issue);
280 public void excludes_issues_on_inactive_rules() {
281 RuleKey ruleKey = RuleKey.of("java", "S001");
282 when(issueFilter.accept(any(), eq(FILE))).thenReturn(true);
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)
293 reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
294 Input<DefaultIssue> input = underTest.create(FILE);
296 Collection<DefaultIssue> issues = input.getIssues();
297 assertThat(issues).isEmpty();
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)
314 reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
315 Input<DefaultIssue> input = underTest.create(FILE);
317 Collection<DefaultIssue> issues = input.getIssues();
318 assertThat(issues).isEmpty();
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)
332 reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue));
334 Input<DefaultIssue> input = underTest.create(FILE);
336 assertThat(input.getIssues()).isEmpty();
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()
347 .setMessage("not enough coverage")
349 when(commonRuleEngine.process(FILE)).thenReturn(singletonList(ceIssue));
351 Input<DefaultIssue> input = underTest.create(FILE);
353 assertThat(input.getIssues()).containsOnly(ceIssue);
354 assertInitializedIssue(input.getIssues().iterator().next());
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()
365 .setMessage("not enough coverage")
367 when(commonRuleEngine.process(FILE)).thenReturn(singletonList(ceIssue));
369 Input<DefaultIssue> input = underTest.create(FILE);
371 assertThat(input.getIssues()).isEmpty();
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();
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();
396 private void markRuleAsActive(RuleKey ruleKey) {
397 activeRulesHolder.put(new ActiveRule(ruleKey, Severity.CRITICAL, emptyMap(), 1_000L, null, "qp1"));
400 private void registerRule(RuleKey ruleKey, String name) {
401 DumbRule dumbRule = new DumbRule(ruleKey);
402 dumbRule.setName(name);
403 ruleRepository.add(dumbRule);