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.

ComponentTreeBuilderTest.java 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 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.component;
  21. import java.util.Arrays;
  22. import java.util.EnumSet;
  23. import java.util.HashMap;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Objects;
  27. import java.util.Optional;
  28. import java.util.Random;
  29. import java.util.function.Function;
  30. import java.util.function.UnaryOperator;
  31. import org.junit.jupiter.api.Test;
  32. import org.junit.jupiter.api.extension.BeforeEachCallback;
  33. import org.junit.jupiter.api.extension.ExtensionContext;
  34. import org.junit.jupiter.api.extension.RegisterExtension;
  35. import org.sonar.ce.task.projectanalysis.analysis.Branch;
  36. import org.sonar.core.component.ComponentKeys;
  37. import org.sonar.scanner.protocol.output.ScannerReport;
  38. import org.sonar.server.project.Project;
  39. import static com.google.common.base.Preconditions.checkArgument;
  40. import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
  41. import static org.assertj.core.api.Assertions.assertThat;
  42. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  43. import static org.junit.Assert.fail;
  44. import static org.mockito.Mockito.mock;
  45. import static org.mockito.Mockito.when;
  46. import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
  47. import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
  48. import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.FILE;
  49. import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.PROJECT;
  50. import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.UNRECOGNIZED;
  51. import static org.sonar.scanner.protocol.output.ScannerReport.Component.newBuilder;
  52. class ComponentTreeBuilderTest {
  53. private static final ComponentKeyGenerator KEY_GENERATOR = (projectKey, path) -> "generated_" + ComponentKeys.createEffectiveKey(projectKey, path);
  54. private static final UnaryOperator<String> UUID_SUPPLIER = (componentKey) -> componentKey + "_uuid";
  55. private static final EnumSet<ScannerReport.Component.ComponentType> REPORT_TYPES = EnumSet.of(PROJECT, FILE);
  56. private static final String NO_SCM_BASE_PATH = "";
  57. // both no project as "" or null should be supported
  58. private static final ProjectAttributes SOME_PROJECT_ATTRIBUTES = new ProjectAttributes(
  59. randomAlphabetic(20), new Random().nextBoolean() ? null : randomAlphabetic(12), "1def5123");
  60. @RegisterExtension
  61. private final ScannerComponentProvider scannerComponentProvider = new ScannerComponentProvider();
  62. private final Project projectInDb = Project.from(newPrivateProjectDto(UUID_SUPPLIER.apply("K1")).setKey("K1").setDescription(null));
  63. @Test
  64. void build_throws_IAE_for_all_types_except_PROJECT_and_FILE() {
  65. Arrays.stream(ScannerReport.Component.ComponentType.values())
  66. .filter((type) -> type != UNRECOGNIZED)
  67. .filter((type) -> !REPORT_TYPES.contains(type))
  68. .forEach(
  69. (type) -> {
  70. scannerComponentProvider.clear();
  71. ScannerReport.Component project = newBuilder()
  72. .setType(PROJECT)
  73. .setKey(projectInDb.getKey())
  74. .setRef(1)
  75. .addChildRef(2)
  76. .setProjectRelativePath("root")
  77. .build();
  78. scannerComponentProvider.add(newBuilder()
  79. .setRef(2)
  80. .setType(type)
  81. .setProjectRelativePath("src")
  82. .setLines(1));
  83. try {
  84. call(project, NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
  85. fail("Should have thrown a IllegalArgumentException");
  86. } catch (IllegalArgumentException e) {
  87. assertThat(e).hasMessage("Unsupported component type '" + type + "'");
  88. }
  89. });
  90. }
  91. @Test
  92. void build_throws_IAE_if_root_is_not_PROJECT() {
  93. Arrays.stream(ScannerReport.Component.ComponentType.values())
  94. .filter((type) -> type != UNRECOGNIZED)
  95. .filter((type) -> !REPORT_TYPES.contains(type))
  96. .forEach(
  97. (type) -> {
  98. ScannerReport.Component component = newBuilder().setType(type).build();
  99. try {
  100. call(component);
  101. fail("Should have thrown a IllegalArgumentException");
  102. } catch (IllegalArgumentException e) {
  103. assertThat(e).hasMessage("Expected root component of type 'PROJECT'");
  104. }
  105. });
  106. }
  107. @Test
  108. void by_default_project_fields_are_loaded_from_report() {
  109. String nameInReport = "the name";
  110. String descriptionInReport = "the desc";
  111. String buildString = randomAlphabetic(21);
  112. Component root = call(newBuilder()
  113. .setType(PROJECT)
  114. .setKey(projectInDb.getKey())
  115. .setRef(42)
  116. .setName(nameInReport)
  117. .setDescription(descriptionInReport)
  118. .build(), NO_SCM_BASE_PATH, new ProjectAttributes("6.5", buildString, "4124af4"));
  119. assertThat(root.getUuid()).isEqualTo("generated_K1_uuid");
  120. assertThat(root.getKey()).isEqualTo("generated_K1");
  121. assertThat(root.getType()).isEqualTo(Component.Type.PROJECT);
  122. assertThat(root.getName()).isEqualTo(nameInReport);
  123. assertThat(root.getShortName()).isEqualTo(nameInReport);
  124. assertThat(root.getDescription()).isEqualTo(descriptionInReport);
  125. assertThat(root.getReportAttributes().getRef()).isEqualTo(42);
  126. assertThat(root.getProjectAttributes().getProjectVersion()).contains("6.5");
  127. assertThat(root.getProjectAttributes().getBuildString()).isEqualTo(Optional.of(buildString));
  128. assertThatFileAttributesAreNotSet(root);
  129. }
  130. @Test
  131. void project_name_is_loaded_from_db_if_absent_from_report() {
  132. Component root = call(newBuilder()
  133. .setType(PROJECT)
  134. .build(), NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
  135. assertThat(root.getName()).isEqualTo(projectInDb.getName());
  136. }
  137. @Test
  138. void project_name_is_loaded_from_report_if_present_and_on_main_branch() {
  139. String reportName = randomAlphabetic(5);
  140. ScannerReport.Component reportProject = newBuilder()
  141. .setType(PROJECT)
  142. .setName(reportName)
  143. .build();
  144. Component root = newUnderTest(SOME_PROJECT_ATTRIBUTES, true).buildProject(reportProject, NO_SCM_BASE_PATH);
  145. assertThat(root.getName()).isEqualTo(reportName);
  146. }
  147. @Test
  148. void project_name_is_loaded_from_db_if_not_on_main_branch() {
  149. String reportName = randomAlphabetic(5);
  150. ScannerReport.Component reportProject = newBuilder()
  151. .setType(PROJECT)
  152. .setName(reportName)
  153. .build();
  154. Component root = newUnderTest(SOME_PROJECT_ATTRIBUTES, false)
  155. .buildProject(reportProject, NO_SCM_BASE_PATH);
  156. assertThat(root.getName()).isEqualTo(projectInDb.getName());
  157. }
  158. @Test
  159. void project_description_is_loaded_from_db_if_absent_from_report() {
  160. Component root = call(newBuilder()
  161. .setType(PROJECT)
  162. .build(), NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
  163. assertThat(root.getDescription()).isEqualTo(projectInDb.getDescription());
  164. }
  165. @Test
  166. void project_description_is_loaded_from_report_if_present_and_on_main_branch() {
  167. String reportDescription = randomAlphabetic(5);
  168. ScannerReport.Component reportProject = newBuilder()
  169. .setType(PROJECT)
  170. .setDescription(reportDescription)
  171. .build();
  172. Component root = newUnderTest(SOME_PROJECT_ATTRIBUTES, true).buildProject(reportProject, NO_SCM_BASE_PATH);
  173. assertThat(root.getDescription()).isEqualTo(reportDescription);
  174. }
  175. @Test
  176. void project_description_is_loaded_from_db_if_not_on_main_branch() {
  177. String reportDescription = randomAlphabetic(5);
  178. ScannerReport.Component reportProject = newBuilder()
  179. .setType(PROJECT)
  180. .setDescription(reportDescription)
  181. .build();
  182. Component root = newUnderTest(SOME_PROJECT_ATTRIBUTES, false).buildProject(reportProject, NO_SCM_BASE_PATH);
  183. assertThat(root.getDescription()).isEqualTo(projectInDb.getDescription());
  184. }
  185. @Test
  186. void project_scmPath_is_empty_if_scmBasePath_is_empty() {
  187. Component root = call(newBuilder()
  188. .setType(PROJECT)
  189. .build(), NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
  190. assertThat(root.getReportAttributes().getScmPath()).isEmpty();
  191. }
  192. @Test
  193. void projectAttributes_is_constructor_argument() {
  194. Component root = call(newBuilder()
  195. .setType(PROJECT)
  196. .build(), NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
  197. assertThat(root.getProjectAttributes()).isSameAs(SOME_PROJECT_ATTRIBUTES);
  198. }
  199. @Test
  200. void any_component_with_projectRelativePath_has_this_value_as_scmPath_if_scmBasePath_is_empty() {
  201. ScannerReport.Component project = newBuilder()
  202. .setType(PROJECT)
  203. .setKey(projectInDb.getKey())
  204. .setRef(1)
  205. .addChildRef(2)
  206. .setProjectRelativePath("root")
  207. .build();
  208. scannerComponentProvider.add(newBuilder()
  209. .setRef(2)
  210. .setType(FILE)
  211. .setProjectRelativePath("src/js/Foo.js")
  212. .setLines(1));
  213. Component root = call(project, NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
  214. assertThat(root.getReportAttributes().getScmPath())
  215. .contains("root");
  216. Component directory = root.getChildren().iterator().next();
  217. assertThat(directory.getReportAttributes().getScmPath())
  218. .contains("src/js");
  219. Component file = directory.getChildren().iterator().next();
  220. assertThat(file.getReportAttributes().getScmPath())
  221. .contains("src/js/Foo.js");
  222. }
  223. @Test
  224. void any_component_with_projectRelativePath_has_this_value_appended_to_scmBasePath_and_a_slash_as_scmPath_if_scmBasePath_is_not_empty() {
  225. ScannerReport.Component project = createProject();
  226. String scmBasePath = randomAlphabetic(10);
  227. Component root = call(project, scmBasePath, SOME_PROJECT_ATTRIBUTES);
  228. assertThat(root.getReportAttributes().getScmPath())
  229. .contains(scmBasePath);
  230. Component directory = root.getChildren().iterator().next();
  231. assertThat(directory.getReportAttributes().getScmPath())
  232. .contains(scmBasePath + "/src/js");
  233. Component file = directory.getChildren().iterator().next();
  234. assertThat(file.getReportAttributes().getScmPath())
  235. .contains(scmBasePath + "/src/js/Foo.js");
  236. }
  237. private ScannerReport.Component createProject() {
  238. ScannerReport.Component project = newBuilder()
  239. .setType(PROJECT)
  240. .setKey(projectInDb.getKey())
  241. .setRef(1)
  242. .addChildRef(2)
  243. .build();
  244. scannerComponentProvider.add(newBuilder()
  245. .setRef(2)
  246. .setType(FILE)
  247. .setProjectRelativePath("src/js/Foo.js")
  248. .setLines(1));
  249. return project;
  250. }
  251. @Test
  252. void keys_of_directory_and_file_are_generated() {
  253. ScannerReport.Component project = createProject();
  254. Component root = call(project);
  255. assertThat(root.getKey()).isEqualTo("generated_" + projectInDb.getKey());
  256. assertThat(root.getChildren()).hasSize(1);
  257. Component directory = root.getChildren().iterator().next();
  258. assertThat(directory.getKey()).isEqualTo("generated_" + projectInDb.getKey() + ":src/js");
  259. assertThat(directory.getChildren()).hasSize(1);
  260. Component file = directory.getChildren().iterator().next();
  261. assertThat(file.getKey()).isEqualTo("generated_" + projectInDb.getKey() + ":src/js/Foo.js");
  262. assertThat(file.getChildren()).isEmpty();
  263. }
  264. @Test
  265. void modules_are_not_created() {
  266. ScannerReport.Component project = newBuilder()
  267. .setType(PROJECT)
  268. .setKey(projectInDb.getKey())
  269. .setRef(1)
  270. .addChildRef(2)
  271. .build();
  272. scannerComponentProvider.add(newBuilder()
  273. .setRef(2)
  274. .setType(FILE)
  275. .setProjectRelativePath("src/js/Foo.js")
  276. .setLines(1));
  277. Component root = call(project);
  278. List<Component> components = root.getChildren();
  279. assertThat(components).extracting("type").containsOnly(Component.Type.DIRECTORY);
  280. }
  281. @Test
  282. void folder_hierarchy_is_created() {
  283. ScannerReport.Component project = newBuilder()
  284. .setType(PROJECT)
  285. .setKey(projectInDb.getKey())
  286. .setRef(1)
  287. .addChildRef(4)
  288. .addChildRef(5)
  289. .addChildRef(6)
  290. .build();
  291. scannerComponentProvider.add(newBuilder()
  292. .setRef(4)
  293. .setType(FILE)
  294. .setProjectRelativePath("src/main/xoo/Foo1.js")
  295. .setLines(1));
  296. scannerComponentProvider.add(newBuilder()
  297. .setRef(5)
  298. .setType(FILE)
  299. .setProjectRelativePath("src/test/xoo/org/sonar/Foo2.js")
  300. .setLines(1));
  301. scannerComponentProvider.add(newBuilder()
  302. .setRef(6)
  303. .setType(FILE)
  304. .setProjectRelativePath("pom.xml")
  305. .setLines(1));
  306. Component root = call(project);
  307. assertThat(root.getChildren()).hasSize(2);
  308. Component pom = root.getChildren().get(1);
  309. assertThat(pom.getKey()).isEqualTo("generated_K1:pom.xml");
  310. assertThat(pom.getName()).isEqualTo("pom.xml");
  311. Component directory = root.getChildren().get(0);
  312. assertThat(directory.getKey()).isEqualTo("generated_K1:src");
  313. assertThat(directory.getName()).isEqualTo("src");
  314. // folders are collapsed and they only contain one directory
  315. Component d1 = directory.getChildren().get(0);
  316. assertThat(d1.getKey()).isEqualTo("generated_K1:src/main/xoo");
  317. assertThat(d1.getName()).isEqualTo("src/main/xoo");
  318. assertThat(d1.getShortName()).isEqualTo("main/xoo");
  319. Component d2 = directory.getChildren().get(1);
  320. assertThat(d2.getKey()).isEqualTo("generated_K1:src/test/xoo/org/sonar");
  321. assertThat(d2.getName()).isEqualTo("src/test/xoo/org/sonar");
  322. assertThat(d2.getShortName()).isEqualTo("test/xoo/org/sonar");
  323. }
  324. @Test
  325. void collapse_directories_from_root() {
  326. ScannerReport.Component project = newBuilder()
  327. .setType(PROJECT)
  328. .setKey(projectInDb.getKey())
  329. .setRef(1)
  330. .addChildRef(2)
  331. .build();
  332. scannerComponentProvider.add(newBuilder()
  333. .setRef(2)
  334. .setType(FILE)
  335. .setProjectRelativePath("src/test/xoo/org/sonar/Foo2.js")
  336. .setLines(1));
  337. Component root = call(project);
  338. // folders are collapsed and they only contain one directory
  339. Component dir = root.getChildren().get(0);
  340. assertThat(dir.getKey()).isEqualTo("generated_K1:src/test/xoo/org/sonar");
  341. assertThat(dir.getName()).isEqualTo("src/test/xoo/org/sonar");
  342. assertThat(dir.getShortName()).isEqualTo("src/test/xoo/org/sonar");
  343. Component file = dir.getChildren().get(0);
  344. assertThat(file.getKey()).isEqualTo("generated_K1:src/test/xoo/org/sonar/Foo2.js");
  345. assertThat(file.getName()).isEqualTo("src/test/xoo/org/sonar/Foo2.js");
  346. assertThat(file.getShortName()).isEqualTo("Foo2.js");
  347. }
  348. @Test
  349. void directories_are_collapsed() {
  350. ScannerReport.Component project = newBuilder()
  351. .setType(PROJECT)
  352. .setKey(projectInDb.getKey())
  353. .setRef(1)
  354. .addChildRef(2)
  355. .build();
  356. scannerComponentProvider.add(newBuilder()
  357. .setRef(2)
  358. .setType(FILE)
  359. .setProjectRelativePath("src/js/Foo.js")
  360. .setLines(1));
  361. Component root = call(project);
  362. Component directory = root.getChildren().iterator().next();
  363. assertThat(directory.getKey()).isEqualTo("generated_K1:src/js");
  364. assertThat(directory.getName()).isEqualTo("src/js");
  365. assertThat(directory.getShortName()).isEqualTo("src/js");
  366. Component file = directory.getChildren().iterator().next();
  367. assertThat(file.getKey()).isEqualTo("generated_K1:src/js/Foo.js");
  368. assertThat(file.getName()).isEqualTo("src/js/Foo.js");
  369. assertThat(file.getShortName()).isEqualTo("Foo.js");
  370. }
  371. @Test
  372. void names_of_directory_and_file_are_based_on_the_path() {
  373. ScannerReport.Component project = newBuilder()
  374. .setType(PROJECT)
  375. .setKey(projectInDb.getKey())
  376. .setRef(1)
  377. .addChildRef(2)
  378. .build();
  379. scannerComponentProvider.add(newBuilder()
  380. .setRef(2)
  381. .setType(FILE)
  382. .setProjectRelativePath("src/js/Foo.js")
  383. .setName("")
  384. .setLines(1));
  385. Component root = call(project);
  386. Component directory = root.getChildren().iterator().next();
  387. assertThat(directory.getName()).isEqualTo("src/js");
  388. assertThat(directory.getShortName()).isEqualTo("src/js");
  389. Component file = directory.getChildren().iterator().next();
  390. assertThat(file.getName()).isEqualTo("src/js/Foo.js");
  391. assertThat(file.getShortName()).isEqualTo("Foo.js");
  392. }
  393. @Test
  394. void create_full_hierarchy_of_directories() {
  395. ScannerReport.Component project = newBuilder()
  396. .setType(PROJECT)
  397. .setKey(projectInDb.getKey())
  398. .setRef(1)
  399. .addChildRef(2)
  400. .addChildRef(3)
  401. .build();
  402. scannerComponentProvider.add(newBuilder()
  403. .setRef(2)
  404. .setType(FILE)
  405. .setProjectRelativePath("src/java/Bar.java")
  406. .setName("")
  407. .setLines(2));
  408. scannerComponentProvider.add(newBuilder()
  409. .setRef(3)
  410. .setType(FILE)
  411. .setProjectRelativePath("src/js/Foo.js")
  412. .setName("")
  413. .setLines(1));
  414. Component root = call(project);
  415. Component directory = root.getChildren().iterator().next();
  416. assertThat(directory.getKey()).isEqualTo("generated_K1:src");
  417. assertThat(directory.getName()).isEqualTo("src");
  418. assertThat(directory.getShortName()).isEqualTo("src");
  419. Component directoryJava = directory.getChildren().get(0);
  420. assertThat(directoryJava.getKey()).isEqualTo("generated_K1:src/java");
  421. assertThat(directoryJava.getName()).isEqualTo("src/java");
  422. assertThat(directoryJava.getShortName()).isEqualTo("java");
  423. Component directoryJs = directory.getChildren().get(1);
  424. assertThat(directoryJs.getKey()).isEqualTo("generated_K1:src/js");
  425. assertThat(directoryJs.getName()).isEqualTo("src/js");
  426. assertThat(directoryJs.getShortName()).isEqualTo("js");
  427. Component file = directoryJs.getChildren().iterator().next();
  428. assertThat(file.getKey()).isEqualTo("generated_K1:src/js/Foo.js");
  429. assertThat(file.getName()).isEqualTo("src/js/Foo.js");
  430. assertThat(file.getShortName()).isEqualTo("Foo.js");
  431. }
  432. private void assertThatFileAttributesAreNotSet(Component root) {
  433. try {
  434. root.getFileAttributes();
  435. fail();
  436. } catch (IllegalStateException e) {
  437. assertThat(e).hasMessage("Only component of type FILE have a FileAttributes object");
  438. }
  439. }
  440. @Test
  441. void keys_of_directory_and_files_includes_always_root_project() {
  442. ScannerReport.Component project = newBuilder()
  443. .setType(PROJECT)
  444. .setKey("project 1")
  445. .setRef(1)
  446. .addChildRef(31).build();
  447. scannerComponentProvider.add(newBuilder().setRef(31).setType(FILE).setProjectRelativePath("file in project").setLines(1));
  448. Component root = call(project);
  449. Map<String, Component> componentsByKey = indexComponentByKey(root);
  450. assertThat(componentsByKey.values()).extracting("key").startsWith("generated_project 1");
  451. }
  452. @Test
  453. void uuids_are_provided_by_supplier() {
  454. ScannerReport.Component project = newBuilder()
  455. .setType(PROJECT)
  456. .setKey("c1")
  457. .setRef(1)
  458. .addChildRef(2)
  459. .build();
  460. scannerComponentProvider.add(newBuilder()
  461. .setRef(2)
  462. .setType(FILE)
  463. .setProjectRelativePath("src/js/Foo.js")
  464. .setLines(1));
  465. Component root = call(project);
  466. assertThat(root.getUuid()).isEqualTo("generated_c1_uuid");
  467. Component directory = root.getChildren().iterator().next();
  468. assertThat(directory.getUuid()).isEqualTo("generated_c1:src/js_uuid");
  469. Component file = directory.getChildren().iterator().next();
  470. assertThat(file.getUuid()).isEqualTo("generated_c1:src/js/Foo.js_uuid");
  471. }
  472. @Test
  473. void files_have_markedAsUnchanged_flag() {
  474. ScannerReport.Component project = newBuilder()
  475. .setType(PROJECT)
  476. .setKey("c1")
  477. .setRef(1)
  478. .addChildRef(2)
  479. .build();
  480. scannerComponentProvider.add(newBuilder()
  481. .setRef(2)
  482. .setType(FILE)
  483. .setMarkedAsUnchanged(true)
  484. .setProjectRelativePath("src/js/Foo.js")
  485. .setLines(1));
  486. Component root = call(project);
  487. assertThat(root.getUuid()).isEqualTo("generated_c1_uuid");
  488. Component directory = root.getChildren().iterator().next();
  489. assertThat(directory.getUuid()).isEqualTo("generated_c1:src/js_uuid");
  490. Component file = directory.getChildren().iterator().next();
  491. assertThat(file.getFileAttributes().isMarkedAsUnchanged()).isTrue();
  492. }
  493. @Test
  494. void issues_are_relocated_from_directories_and_modules_to_root() {
  495. ScannerReport.Component project = newBuilder()
  496. .setType(PROJECT)
  497. .setKey("c1")
  498. .setRef(1)
  499. .addChildRef(2)
  500. .build();
  501. ScannerReport.Component.Builder file = newBuilder()
  502. .setRef(2)
  503. .setType(FILE)
  504. .setProjectRelativePath("src/js/Foo.js")
  505. .setLines(1);
  506. scannerComponentProvider.add(file);
  507. call(project);
  508. }
  509. @Test
  510. void descriptions_of_module_directory_and_file_are_null_if_absent_from_report() {
  511. ScannerReport.Component project = newBuilder()
  512. .setType(PROJECT)
  513. .setRef(1)
  514. .addChildRef(2)
  515. .build();
  516. scannerComponentProvider.add(newBuilder()
  517. .setRef(2)
  518. .setType(FILE)
  519. .setProjectRelativePath("src/js/Foo.js")
  520. .setLines(1));
  521. Component root = call(project);
  522. Component directory = root.getChildren().iterator().next();
  523. assertThat(directory.getDescription()).isNull();
  524. Component file = directory.getChildren().iterator().next();
  525. assertThat(file.getDescription()).isNull();
  526. }
  527. @Test
  528. void descriptions_of_module_directory_and_file_are_null_if_empty_in_report() {
  529. ScannerReport.Component project = newBuilder()
  530. .setType(PROJECT)
  531. .setRef(1)
  532. .setDescription("")
  533. .addChildRef(2)
  534. .build();
  535. scannerComponentProvider.add(newBuilder()
  536. .setRef(2)
  537. .setType(FILE)
  538. .setDescription("")
  539. .setProjectRelativePath("src/js/Foo.js")
  540. .setLines(1));
  541. Component root = call(project);
  542. Component directory = root.getChildren().iterator().next();
  543. assertThat(directory.getDescription()).isNull();
  544. Component file = directory.getChildren().iterator().next();
  545. assertThat(file.getDescription()).isNull();
  546. }
  547. @Test
  548. void descriptions_of_module_directory_and_file_are_set_from_report_if_present() {
  549. ScannerReport.Component project = newBuilder()
  550. .setType(PROJECT)
  551. .setRef(1)
  552. .addChildRef(2)
  553. .build();
  554. scannerComponentProvider.add(newBuilder()
  555. .setRef(2)
  556. .setType(FILE)
  557. .setDescription("d")
  558. .setProjectRelativePath("src/js/Foo.js")
  559. .setLines(1));
  560. Component root = call(project);
  561. Component directory = root.getChildren().iterator().next();
  562. assertThat(directory.getDescription()).isNull();
  563. Component file = directory.getChildren().iterator().next();
  564. assertThat(file.getDescription()).isEqualTo("d");
  565. }
  566. @Test
  567. void only_nb_of_lines_is_mandatory_on_file_attributes() {
  568. ScannerReport.Component project = newBuilder()
  569. .setType(PROJECT)
  570. .setRef(1)
  571. .addChildRef(2)
  572. .build();
  573. scannerComponentProvider.add(newBuilder()
  574. .setRef(2)
  575. .setType(FILE)
  576. .setProjectRelativePath("src/js/Foo.js")
  577. .setLines(1));
  578. Component root = call(project);
  579. Component dir = root.getChildren().iterator().next();
  580. Component file = dir.getChildren().iterator().next();
  581. assertThat(file.getFileAttributes().getLines()).isOne();
  582. assertThat(file.getFileAttributes().getLanguageKey()).isNull();
  583. assertThat(file.getFileAttributes().isUnitTest()).isFalse();
  584. }
  585. @Test
  586. void language_file_attributes_is_null_if_empty_in_report() {
  587. ScannerReport.Component project = newBuilder()
  588. .setType(PROJECT)
  589. .setRef(1)
  590. .addChildRef(2)
  591. .build();
  592. scannerComponentProvider.add(newBuilder()
  593. .setRef(2)
  594. .setType(FILE)
  595. .setProjectRelativePath("src/js/Foo.js")
  596. .setLines(1)
  597. .setLanguage(""));
  598. Component root = call(project);
  599. Component dir2 = root.getChildren().iterator().next();
  600. Component file = dir2.getChildren().iterator().next();
  601. assertThat(file.getFileAttributes().getLanguageKey()).isNull();
  602. }
  603. @Test
  604. void file_attributes_are_fully_loaded_from_report() {
  605. ScannerReport.Component project = newBuilder()
  606. .setType(PROJECT)
  607. .setRef(1)
  608. .addChildRef(2)
  609. .build();
  610. scannerComponentProvider.add(newBuilder()
  611. .setRef(2)
  612. .setType(FILE)
  613. .setProjectRelativePath("src/js/Foo.js")
  614. .setLines(1)
  615. .setLanguage("js")
  616. .setIsTest(true));
  617. Component root = call(project);
  618. Component dir = root.getChildren().iterator().next();
  619. Component file = dir.getChildren().iterator().next();
  620. assertThat(file.getFileAttributes().getLines()).isOne();
  621. assertThat(file.getFileAttributes().getLanguageKey()).isEqualTo("js");
  622. assertThat(file.getFileAttributes().isUnitTest()).isTrue();
  623. }
  624. @Test
  625. void throw_IAE_if_lines_is_absent_from_report() {
  626. ScannerReport.Component project = newBuilder()
  627. .setType(PROJECT)
  628. .setRef(1)
  629. .addChildRef(2)
  630. .build();
  631. scannerComponentProvider.add(newBuilder()
  632. .setRef(2)
  633. .setType(FILE)
  634. .setProjectRelativePath("src/js/Foo.js"));
  635. assertThatThrownBy(() -> call(project))
  636. .isInstanceOf(IllegalArgumentException.class)
  637. .hasMessage("File 'src/js/Foo.js' has no line");
  638. }
  639. @Test
  640. void throw_IAE_if_lines_is_zero_in_report() {
  641. ScannerReport.Component project = newBuilder()
  642. .setType(PROJECT)
  643. .setRef(1)
  644. .addChildRef(2)
  645. .build();
  646. scannerComponentProvider.add(newBuilder()
  647. .setRef(2)
  648. .setType(FILE)
  649. .setProjectRelativePath("src/js/Foo.js")
  650. .setLines(0));
  651. assertThatThrownBy(() -> call(project))
  652. .isInstanceOf(IllegalArgumentException.class)
  653. .hasMessage("File 'src/js/Foo.js' has no line");
  654. }
  655. @Test
  656. void throw_IAE_if_lines_is_negative_in_report() {
  657. ScannerReport.Component project = newBuilder()
  658. .setType(PROJECT)
  659. .setRef(1)
  660. .addChildRef(2)
  661. .build();
  662. scannerComponentProvider.add(newBuilder()
  663. .setRef(2)
  664. .setType(FILE)
  665. .setProjectRelativePath("src/js/Foo.js")
  666. .setLines(-10));
  667. assertThatThrownBy(() -> call(project))
  668. .isInstanceOf(IllegalArgumentException.class)
  669. .hasMessage("File 'src/js/Foo.js' has no line");
  670. }
  671. private static class ScannerComponentProvider implements Function<Integer, ScannerReport.Component>, BeforeEachCallback {
  672. private final Map<Integer, ScannerReport.Component> components = new HashMap<>();
  673. public void clear() {
  674. components.clear();
  675. }
  676. @Override
  677. public ScannerReport.Component apply(Integer componentRef) {
  678. return Objects.requireNonNull(components.get(componentRef), "No Component for componentRef " + componentRef);
  679. }
  680. public ScannerReport.Component add(ScannerReport.Component.Builder builder) {
  681. ScannerReport.Component component = builder.build();
  682. ScannerReport.Component existing = components.put(component.getRef(), component);
  683. checkArgument(existing == null, "Component %s already set for ref %s", existing, component.getRef());
  684. return component;
  685. }
  686. @Override
  687. public void beforeEach(ExtensionContext extensionContext) {
  688. clear();
  689. }
  690. }
  691. private Component call(ScannerReport.Component project) {
  692. return call(project, NO_SCM_BASE_PATH, SOME_PROJECT_ATTRIBUTES);
  693. }
  694. private Component call(ScannerReport.Component project, String scmBasePath, ProjectAttributes projectAttributes) {
  695. return newUnderTest(projectAttributes, true).buildProject(project, scmBasePath);
  696. }
  697. private ComponentTreeBuilder newUnderTest(ProjectAttributes projectAttributes, boolean mainBranch) {
  698. Branch branch = mock(Branch.class);
  699. when(branch.isMain()).thenReturn(mainBranch);
  700. return new ComponentTreeBuilder(KEY_GENERATOR, UUID_SUPPLIER, scannerComponentProvider, projectInDb, branch, projectAttributes);
  701. }
  702. private static Map<String, Component> indexComponentByKey(Component root) {
  703. Map<String, Component> componentsByKey = new HashMap<>();
  704. new DepthTraversalTypeAwareCrawler(
  705. new TypeAwareVisitorAdapter(CrawlerDepthLimit.FILE, PRE_ORDER) {
  706. @Override
  707. public void visitAny(Component any) {
  708. componentsByKey.put(any.getKey(), any);
  709. }
  710. }).visit(root);
  711. return componentsByKey;
  712. }
  713. }