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.

DefaultIssueTest.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 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.api.batch.sensor.issue.internal;
  21. import java.io.File;
  22. import java.io.IOException;
  23. import java.util.List;
  24. import org.junit.Before;
  25. import org.junit.Rule;
  26. import org.junit.Test;
  27. import org.junit.rules.TemporaryFolder;
  28. import org.sonar.api.batch.bootstrap.ProjectDefinition;
  29. import org.sonar.api.batch.fs.TextRange;
  30. import org.sonar.api.batch.fs.internal.DefaultInputDir;
  31. import org.sonar.api.batch.fs.internal.DefaultInputFile;
  32. import org.sonar.api.batch.fs.internal.DefaultInputModule;
  33. import org.sonar.api.batch.fs.internal.DefaultInputProject;
  34. import org.sonar.api.batch.fs.internal.DefaultTextPointer;
  35. import org.sonar.api.batch.fs.internal.DefaultTextRange;
  36. import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
  37. import org.sonar.api.batch.rule.Severity;
  38. import org.sonar.api.batch.sensor.internal.SensorStorage;
  39. import org.sonar.api.batch.sensor.issue.Issue.Flow;
  40. import org.sonar.api.batch.sensor.issue.MessageFormatting;
  41. import org.sonar.api.batch.sensor.issue.NewIssue.FlowType;
  42. import org.sonar.api.batch.sensor.issue.fix.NewQuickFix;
  43. import org.sonar.api.issue.impact.SoftwareQuality;
  44. import org.sonar.api.rule.RuleKey;
  45. import static org.assertj.core.api.Assertions.assertThat;
  46. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  47. import static org.assertj.core.api.Assertions.tuple;
  48. import static org.mockito.Mockito.mock;
  49. import static org.mockito.Mockito.verify;
  50. public class DefaultIssueTest {
  51. private static final RuleKey RULE_KEY = RuleKey.of("repo", "rule");
  52. @Rule
  53. public TemporaryFolder temp = new TemporaryFolder();
  54. private final SensorStorage storage = mock(SensorStorage.class);
  55. private final DefaultInputFile inputFile = new TestInputFileBuilder("foo", "src/Foo.php")
  56. .initMetadata("Foo\nBar\n")
  57. .build();
  58. private DefaultInputProject project;
  59. private final NewQuickFix quickFix = mock(NewQuickFix.class);
  60. @Before
  61. public void prepare() throws IOException {
  62. project = new DefaultInputProject(ProjectDefinition.create()
  63. .setKey("foo")
  64. .setBaseDir(temp.newFolder())
  65. .setWorkDir(temp.newFolder()));
  66. }
  67. @Test
  68. public void build_file_issue() {
  69. DefaultIssue issue = new DefaultIssue(project, storage)
  70. .at(new DefaultIssueLocation()
  71. .on(inputFile)
  72. .at(inputFile.selectLine(1))
  73. .message("Wrong way!"))
  74. .forRule(RULE_KEY)
  75. .gap(10.0)
  76. .setRuleDescriptionContextKey("spring")
  77. .setCodeVariants(List.of("variant1", "variant2"));
  78. assertThat(issue.primaryLocation().inputComponent()).isEqualTo(inputFile);
  79. assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule"));
  80. assertThat(issue.primaryLocation().textRange().start().line()).isOne();
  81. assertThat(issue.gap()).isEqualTo(10.0);
  82. assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!");
  83. assertThat(issue.ruleDescriptionContextKey()).contains("spring");
  84. assertThat(issue.codeVariants()).containsOnly("variant1", "variant2");
  85. issue.save();
  86. verify(storage).store(issue);
  87. }
  88. @Test
  89. public void build_issue_with_flows() {
  90. TextRange range1 = new DefaultTextRange(new DefaultTextPointer(1, 1), new DefaultTextPointer(1, 2));
  91. TextRange range2 = new DefaultTextRange(new DefaultTextPointer(2, 1), new DefaultTextPointer(2, 2));
  92. DefaultIssue issue = new DefaultIssue(project, storage)
  93. .at(new DefaultIssueLocation().on(inputFile))
  94. .addFlow(List.of(new DefaultIssueLocation().message("loc1").on(inputFile)), FlowType.DATA, "desc")
  95. .addFlow(List.of(new DefaultIssueLocation().message("loc1").on(inputFile).at(range1), new DefaultIssueLocation().message("loc1").on(inputFile).at(range2)))
  96. .forRule(RULE_KEY);
  97. assertThat(issue.flows())
  98. .extracting(Flow::type, Flow::description)
  99. .containsExactly(tuple(FlowType.DATA, "desc"), tuple(FlowType.UNDEFINED, null));
  100. assertThat(issue.flows().get(0).locations()).hasSize(1);
  101. assertThat(issue.flows().get(1).locations()).hasSize(2);
  102. }
  103. @Test
  104. public void build_issue_with_secondary_locations() {
  105. TextRange range1 = new DefaultTextRange(new DefaultTextPointer(1, 1), new DefaultTextPointer(1, 2));
  106. TextRange range2 = new DefaultTextRange(new DefaultTextPointer(2, 1), new DefaultTextPointer(2, 2));
  107. DefaultIssue issue = new DefaultIssue(project, storage)
  108. .at(new DefaultIssueLocation().on(inputFile))
  109. .addLocation(new DefaultIssueLocation().on(inputFile).at(range1).message("loc1"))
  110. .addLocation(new DefaultIssueLocation().on(inputFile).at(range2).message("loc2"))
  111. .forRule(RULE_KEY);
  112. assertThat(issue.flows())
  113. .extracting(Flow::type, Flow::description)
  114. .containsExactly(tuple(FlowType.UNDEFINED, null), tuple(FlowType.UNDEFINED, null));
  115. assertThat(issue.flows().get(0).locations()).hasSize(1);
  116. assertThat(issue.flows().get(1).locations()).hasSize(1);
  117. }
  118. @Test
  119. public void move_directory_issue_to_project_root() {
  120. DefaultIssue issue = new DefaultIssue(project, storage)
  121. .at(new DefaultIssueLocation()
  122. .on(new DefaultInputDir("foo", "src/main").setModuleBaseDir(project.getBaseDir()))
  123. .message("Wrong way!"))
  124. .forRule(RuleKey.of("repo", "rule"))
  125. .overrideSeverity(Severity.BLOCKER);
  126. assertThat(issue.primaryLocation().inputComponent()).isEqualTo(project);
  127. assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule"));
  128. assertThat(issue.primaryLocation().textRange()).isNull();
  129. assertThat(issue.primaryLocation().message()).isEqualTo("[src/main] Wrong way!");
  130. assertThat(issue.overriddenSeverity()).isEqualTo(Severity.BLOCKER);
  131. issue.save();
  132. verify(storage).store(issue);
  133. }
  134. @Test
  135. public void move_submodule_issue_to_project_root() {
  136. File subModuleDirectory = new File(project.getBaseDir().toString(), "bar");
  137. subModuleDirectory.mkdir();
  138. ProjectDefinition subModuleDefinition = ProjectDefinition.create()
  139. .setKey("foo/bar")
  140. .setBaseDir(subModuleDirectory)
  141. .setWorkDir(subModuleDirectory);
  142. project.definition().addSubProject(subModuleDefinition);
  143. DefaultInputModule subModule = new DefaultInputModule(subModuleDefinition);
  144. DefaultIssue issue = new DefaultIssue(project, storage)
  145. .at(new DefaultIssueLocation()
  146. .on(subModule)
  147. .message("Wrong way! with code snippet", List.of(new DefaultMessageFormatting().start(16).end(27).type(MessageFormatting.Type.CODE))))
  148. .forRule(RULE_KEY)
  149. .overrideSeverity(Severity.BLOCKER);
  150. assertThat(issue.primaryLocation().inputComponent()).isEqualTo(project);
  151. assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule"));
  152. assertThat(issue.primaryLocation().textRange()).isNull();
  153. assertThat(issue.primaryLocation().message()).isEqualTo("[bar] Wrong way! with code snippet");
  154. assertThat(issue.overriddenSeverity()).isEqualTo(Severity.BLOCKER);
  155. assertThat(issue.primaryLocation().messageFormattings().get(0)).extracting(MessageFormatting::start,
  156. MessageFormatting::end, MessageFormatting::type)
  157. .as("Formatting ranges are padded with the new message")
  158. .containsExactly(22, 33, MessageFormatting.Type.CODE);
  159. issue.save();
  160. verify(storage).store(issue);
  161. }
  162. @Test
  163. public void build_project_issue() throws IOException {
  164. DefaultInputModule inputModule = new DefaultInputModule(ProjectDefinition.create().setKey("foo").setBaseDir(temp.newFolder()).setWorkDir(temp.newFolder()));
  165. DefaultIssue issue = new DefaultIssue(project, storage)
  166. .at(new DefaultIssueLocation()
  167. .on(inputModule)
  168. .message("Wrong way!"))
  169. .forRule(RULE_KEY)
  170. .gap(10.0);
  171. assertThat(issue.primaryLocation().inputComponent()).isEqualTo(inputModule);
  172. assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule"));
  173. assertThat(issue.primaryLocation().textRange()).isNull();
  174. assertThat(issue.gap()).isEqualTo(10.0);
  175. assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!");
  176. issue.save();
  177. verify(storage).store(issue);
  178. }
  179. @Test
  180. public void at_fails_if_called_twice() {
  181. DefaultIssueLocation loc = new DefaultIssueLocation().on(inputFile);
  182. DefaultIssue issue = new DefaultIssue(project, storage).at(loc);
  183. assertThatThrownBy(() -> issue.at(loc)).isInstanceOf(IllegalStateException.class);
  184. }
  185. @Test
  186. public void at_fails_if_location_is_null() {
  187. DefaultIssue issue = new DefaultIssue(project, storage);
  188. assertThatThrownBy(() -> issue.at(null)).isInstanceOf(IllegalArgumentException.class);
  189. }
  190. @Test
  191. public void default_issue_has_no_quickfix() {
  192. DefaultIssue issue = new DefaultIssue(project, storage);
  193. assertThat(issue.isQuickFixAvailable()).isFalse();
  194. }
  195. @Test
  196. public void issue_can_have_quickfix() {
  197. DefaultIssue issue = new DefaultIssue(project, storage).setQuickFixAvailable(true);
  198. assertThat(issue.isQuickFixAvailable()).isTrue();
  199. }
  200. @Test
  201. public void issue_can_override_impacts() {
  202. DefaultIssue issue = new DefaultIssue(project, storage).overrideImpact(SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.LOW);
  203. assertThat(issue.overridenImpacts()).containsEntry(SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.LOW);
  204. }
  205. @Test
  206. public void quickfix_only_sets_flag_to_true() {
  207. DefaultIssue issue = new DefaultIssue(project);
  208. NewQuickFix newQuickFix = issue.newQuickFix();
  209. assertThat(newQuickFix).isInstanceOf(NoOpNewQuickFix.class);
  210. assertThat(issue.isQuickFixAvailable()).isFalse();
  211. issue.addQuickFix(newQuickFix);
  212. assertThat(issue.isQuickFixAvailable()).isTrue();
  213. }
  214. }