You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TrackerRawInputFactoryTest.java 17KB

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