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

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