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.

ProjectReactorBuilderTest.java 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2020 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.scanner.scan;
  21. import java.io.File;
  22. import java.io.FileInputStream;
  23. import java.io.IOException;
  24. import java.net.URL;
  25. import java.util.HashMap;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.Properties;
  29. import org.apache.commons.io.FileUtils;
  30. import org.apache.commons.lang.StringUtils;
  31. import org.junit.Rule;
  32. import org.junit.Test;
  33. import org.junit.rules.ExpectedException;
  34. import org.sonar.api.batch.bootstrap.ProjectDefinition;
  35. import org.sonar.api.batch.bootstrap.ProjectReactor;
  36. import org.sonar.api.notifications.AnalysisWarnings;
  37. import org.sonar.api.utils.MessageException;
  38. import org.sonar.api.utils.log.LogTester;
  39. import org.sonar.scanner.bootstrap.ProcessedScannerProperties;
  40. import org.sonar.scanner.bootstrap.RawScannerProperties;
  41. import static java.util.Collections.emptyMap;
  42. import static java.util.Collections.singletonMap;
  43. import static org.assertj.core.api.Assertions.assertThat;
  44. import static org.mockito.Mockito.mock;
  45. public class ProjectReactorBuilderTest {
  46. @Rule
  47. public ExpectedException thrown = ExpectedException.none();
  48. @Rule
  49. public LogTester logTester = new LogTester();
  50. @Test
  51. public void shouldDefineSimpleProject() {
  52. ProjectDefinition projectDefinition = loadProjectDefinition("simple-project");
  53. assertThat(projectDefinition.getKey()).isEqualTo("com.foo.project");
  54. assertThat(projectDefinition.getName()).isEqualTo("Foo Project");
  55. assertThat(projectDefinition.getVersion()).isEqualTo("1.0-SNAPSHOT");
  56. assertThat(projectDefinition.getDescription()).isEqualTo("Description of Foo Project");
  57. assertThat(projectDefinition.sources()).contains("sources");
  58. }
  59. @Test
  60. public void should_fail_if_sources_are_missing_in_leaf_module() {
  61. thrown.expect(MessageException.class);
  62. thrown.expectMessage("The folder 'unexisting-source-dir' does not exist for 'com.foo.project' (base directory = "
  63. + getResource(this.getClass(), "simple-project-with-unexisting-source-dir") + ")");
  64. loadProjectDefinition("simple-project-with-unexisting-source-dir");
  65. }
  66. @Test
  67. public void should_not_fail_if_sources_are_missing_in_intermediate_module() {
  68. loadProjectDefinition("multi-module-pom-in-root");
  69. }
  70. @Test
  71. public void shouldNotFailIfBlankSourceDirectory() {
  72. loadProjectDefinition("simple-project-with-blank-source-dir");
  73. }
  74. @Test
  75. public void modulesDuplicateIds() {
  76. thrown.expect(MessageException.class);
  77. thrown.expectMessage("Two modules have the same id: 'module1'. Each module must have a unique id.");
  78. loadProjectDefinition("multi-module-duplicate-id");
  79. }
  80. @Test
  81. public void sonarModuleIdIsForbidden() {
  82. thrown.expect(MessageException.class);
  83. thrown.expectMessage("'sonar' is not a valid module id. Please check property 'sonar.modules'.");
  84. loadProjectDefinition("multi-module-sonar-module");
  85. }
  86. @Test
  87. public void modulesRepeatedIds() {
  88. ProjectDefinition rootProject = loadProjectDefinition("multi-module-repeated-id");
  89. List<ProjectDefinition> modules = rootProject.getSubProjects();
  90. assertThat(modules.size()).isEqualTo(1);
  91. // Module 1
  92. ProjectDefinition module1 = modules.get(0);
  93. assertThat(module1.getKey()).isEqualTo("com.foo.project:module1");
  94. assertThat(module1.getName()).isEqualTo("Foo Module 1");
  95. // Module 1 -> Module 1
  96. ProjectDefinition module1_module1 = module1.getSubProjects().get(0);
  97. assertThat(module1_module1.getKey()).isEqualTo("com.foo.project:module1:module1");
  98. assertThat(module1_module1.getName()).isEqualTo("Foo Sub Module 1");
  99. }
  100. @Test
  101. public void shouldDefineMultiModuleProjectWithDefinitionsAllInRootProject() throws IOException {
  102. execMultiModule("multi-module-definitions-all-in-root");
  103. }
  104. @Test
  105. public void shouldDefineMultiModuleProjectWithPomFileAtRootLevel() throws IOException {
  106. ProjectDefinition project = execMultiModule("multi-module-pom-in-root");
  107. assertThat(project.sources()).containsExactlyInAnyOrder("pom.xml", "sources");
  108. }
  109. public ProjectDefinition execMultiModule(String key) throws IOException {
  110. ProjectDefinition rootProject = loadProjectDefinition(key);
  111. // CHECK ROOT
  112. assertThat(rootProject.getKey()).isEqualTo("com.foo.project");
  113. assertThat(rootProject.getName()).isEqualTo("Foo Project");
  114. assertThat(rootProject.getVersion()).isEqualTo("1.0-SNAPSHOT");
  115. assertThat(rootProject.getDescription()).isEqualTo("Description of Foo Project");
  116. assertThat(rootProject.sources().contains("sources")).isTrue();
  117. assertThat(rootProject.tests().contains("tests")).isTrue();
  118. // and module properties must have been cleaned
  119. assertThat(rootProject.properties().get("module1.sonar.projectKey")).isNull();
  120. assertThat(rootProject.properties().get("module2.sonar.projectKey")).isNull();
  121. // Check baseDir and workDir
  122. assertThat(rootProject.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), key));
  123. assertThat(rootProject.getWorkDir().getCanonicalFile()).isEqualTo(new File(getResource(this.getClass(), key), ".sonar"));
  124. // CHECK MODULES
  125. List<ProjectDefinition> modules = rootProject.getSubProjects();
  126. assertThat(modules.size()).isEqualTo(2);
  127. // Module 1
  128. ProjectDefinition module1 = modules.get(0);
  129. assertThat(module1.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), key + "/module1"));
  130. assertThat(module1.getKey()).isEqualTo("com.foo.project:module1");
  131. assertThat(module1.getName()).isEqualTo("module1");
  132. assertThat(module1.getVersion()).isEqualTo("1.0-SNAPSHOT");
  133. // Description should not be inherited from parent if not set
  134. assertThat(module1.getDescription()).isNull();
  135. assertThat(module1.sources()).contains("sources");
  136. assertThat(module1.tests()).contains("tests");
  137. // and module properties must have been cleaned
  138. assertThat(module1.properties().get("module1.sonar.projectKey")).isNull();
  139. assertThat(module1.properties().get("module2.sonar.projectKey")).isNull();
  140. // Check baseDir and workDir
  141. assertThat(module1.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), key + "/module1"));
  142. assertThat(module1.getWorkDir().getCanonicalFile()).isEqualTo(new File(getResource(this.getClass(), key), ".sonar/com.foo.project_module1"));
  143. // Module 2
  144. ProjectDefinition module2 = modules.get(1);
  145. assertThat(module2.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), key + "/module2"));
  146. assertThat(module2.getKey()).isEqualTo("com.foo.project:com.foo.project.module2");
  147. assertThat(module2.getName()).isEqualTo("Foo Module 2");
  148. assertThat(module2.getVersion()).isEqualTo("1.0-SNAPSHOT");
  149. assertThat(module2.getDescription()).isEqualTo("Description of Module 2");
  150. assertThat(module2.sources()).contains("src");
  151. assertThat(module2.tests()).contains("tests");
  152. // and module properties must have been cleaned
  153. assertThat(module2.properties().get("module1.sonar.projectKey")).isNull();
  154. assertThat(module2.properties().get("module2.sonar.projectKey")).isNull();
  155. // Check baseDir and workDir
  156. assertThat(module2.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), key + "/module2"));
  157. assertThat(module2.getWorkDir().getCanonicalFile()).isEqualTo(
  158. new File(getResource(this.getClass(), key), ".sonar/com.foo.project_com.foo.project.module2"));
  159. return rootProject;
  160. }
  161. // SONAR-4876
  162. @Test
  163. public void shouldDefineMultiModuleProjectWithModuleKey() {
  164. ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-moduleKey");
  165. // CHECK ROOT
  166. // module properties must have been cleaned
  167. assertThat(rootProject.properties().get("module1.sonar.moduleKey")).isNull();
  168. assertThat(rootProject.properties().get("module2.sonar.moduleKey")).isNull();
  169. // CHECK MODULES
  170. List<ProjectDefinition> modules = rootProject.getSubProjects();
  171. assertThat(modules.size()).isEqualTo(2);
  172. // Module 2
  173. ProjectDefinition module2 = modules.get(1);
  174. assertThat(module2.getKey()).isEqualTo("com.foo.project.module2");
  175. }
  176. // SONARPLUGINS-2421
  177. @Test
  178. public void shouldDefineMultiLanguageProjectWithDefinitionsAllInRootProject() throws IOException {
  179. ProjectDefinition rootProject = loadProjectDefinition("multi-language-definitions-all-in-root");
  180. // CHECK ROOT
  181. assertThat(rootProject.getKey()).isEqualTo("example");
  182. assertThat(rootProject.getName()).isEqualTo("Example");
  183. assertThat(rootProject.getVersion()).isEqualTo("1.0");
  184. // CHECK MODULES
  185. List<ProjectDefinition> modules = rootProject.getSubProjects();
  186. assertThat(modules.size()).isEqualTo(2);
  187. // Module 1
  188. ProjectDefinition module1 = modules.get(0);
  189. assertThat(module1.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-language-definitions-all-in-root"));
  190. assertThat(module1.sources()).contains("src/main/java");
  191. // and module properties must have been cleaned
  192. assertThat(module1.getWorkDir().getCanonicalFile())
  193. .isEqualTo(new File(getResource(this.getClass(), "multi-language-definitions-all-in-root"), ".sonar/example_java-module"));
  194. // Module 2
  195. ProjectDefinition module2 = modules.get(1);
  196. assertThat(module2.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-language-definitions-all-in-root"));
  197. assertThat(module2.sources()).contains("src/main/groovy");
  198. // and module properties must have been cleaned
  199. assertThat(module2.getWorkDir().getCanonicalFile())
  200. .isEqualTo(new File(getResource(this.getClass(), "multi-language-definitions-all-in-root"), ".sonar/example_groovy-module"));
  201. }
  202. @Test
  203. public void shouldDefineMultiModuleProjectWithBaseDir() {
  204. ProjectDefinition rootProject = loadProjectDefinition("multi-module-with-basedir");
  205. List<ProjectDefinition> modules = rootProject.getSubProjects();
  206. assertThat(modules.size()).isEqualTo(1);
  207. assertThat(modules.get(0).getKey()).isEqualTo("com.foo.project:com.foo.project.module1");
  208. }
  209. @Test
  210. public void shouldFailIfUnexistingModuleBaseDir() {
  211. thrown.expect(MessageException.class);
  212. thrown.expectMessage("The base directory of the module 'module1' does not exist: "
  213. + getResource(this.getClass(), "multi-module-with-unexisting-basedir").getAbsolutePath() + File.separator + "module1");
  214. loadProjectDefinition("multi-module-with-unexisting-basedir");
  215. }
  216. @Test
  217. public void shouldFailIfUnexistingSourceFolderInheritedInMultimodule() {
  218. thrown.expect(MessageException.class);
  219. thrown.expectMessage("The folder 'unexisting-source-dir' does not exist for 'com.foo.project:module1' (base directory = "
  220. + getResource(this.getClass(), "multi-module-with-unexisting-source-dir").getAbsolutePath() + File.separator + "module1)");
  221. loadProjectDefinition("multi-module-with-unexisting-source-dir");
  222. }
  223. @Test
  224. public void shouldFailIfExplicitUnexistingTestFolder() {
  225. thrown.expect(MessageException.class);
  226. thrown.expectMessage("The folder 'tests' does not exist for 'com.foo.project' (base directory = "
  227. + getResource(this.getClass(), "simple-project-with-unexisting-test-dir").getAbsolutePath());
  228. loadProjectDefinition("simple-project-with-unexisting-test-dir");
  229. }
  230. @Test
  231. public void shouldFailIfExplicitUnexistingTestFolderOnModule() {
  232. thrown.expect(MessageException.class);
  233. thrown.expectMessage("The folder 'tests' does not exist for 'module1' (base directory = "
  234. + getResource(this.getClass(), "multi-module-with-explicit-unexisting-test-dir").getAbsolutePath() + File.separator + "module1)");
  235. loadProjectDefinition("multi-module-with-explicit-unexisting-test-dir");
  236. }
  237. @Test
  238. public void multiModuleProperties() {
  239. ProjectDefinition projectDefinition = loadProjectDefinition("big-multi-module-definitions-all-in-root");
  240. assertThat(projectDefinition.properties().get("module11.property")).isNull();
  241. ProjectDefinition module1 = null;
  242. ProjectDefinition module2 = null;
  243. for (ProjectDefinition prj : projectDefinition.getSubProjects()) {
  244. if (prj.getKey().equals("com.foo.project:module1")) {
  245. module1 = prj;
  246. } else if (prj.getKey().equals("com.foo.project:module2")) {
  247. module2 = prj;
  248. }
  249. }
  250. assertThat(module1.properties().get("module11.property")).isNull();
  251. assertThat(module1.properties().get("property")).isNull();
  252. assertThat(module2.properties().get("module11.property")).isNull();
  253. assertThat(module2.properties().get("property")).isNull();
  254. ProjectDefinition module11 = null;
  255. ProjectDefinition module12 = null;
  256. for (ProjectDefinition prj : module1.getSubProjects()) {
  257. if (prj.getKey().equals("com.foo.project:module1:module11")) {
  258. module11 = prj;
  259. } else if (prj.getKey().equals("com.foo.project:module1:module12")) {
  260. module12 = prj;
  261. }
  262. }
  263. assertThat(module11.properties().get("module1.module11.property")).isNull();
  264. assertThat(module11.properties().get("module11.property")).isNull();
  265. assertThat(module11.properties().get("property")).isEqualTo("My module11 property");
  266. assertThat(module12.properties().get("module11.property")).isNull();
  267. assertThat(module12.properties().get("property")).isNull();
  268. }
  269. @Test
  270. public void shouldFailIfMandatoryPropertiesAreNotPresent() {
  271. Map<String, String> props = new HashMap<>();
  272. props.put("foo1", "bla");
  273. props.put("foo4", "bla");
  274. thrown.expect(MessageException.class);
  275. thrown.expectMessage("You must define the following mandatory properties for 'Unknown': foo2, foo3");
  276. ProjectReactorBuilder.checkMandatoryProperties(props, new String[] {"foo1", "foo2", "foo3"});
  277. }
  278. @Test
  279. public void shouldFailIfMandatoryPropertiesAreNotPresentButWithProjectKey() {
  280. Map<String, String> props = new HashMap<>();
  281. props.put("foo1", "bla");
  282. props.put("sonar.projectKey", "my-project");
  283. thrown.expect(MessageException.class);
  284. thrown.expectMessage("You must define the following mandatory properties for 'my-project': foo2, foo3");
  285. ProjectReactorBuilder.checkMandatoryProperties(props, new String[] {"foo1", "foo2", "foo3"});
  286. }
  287. @Test
  288. public void shouldNotFailIfMandatoryPropertiesArePresent() {
  289. Map<String, String> props = new HashMap<>();
  290. props.put("foo1", "bla");
  291. props.put("foo4", "bla");
  292. ProjectReactorBuilder.checkMandatoryProperties(props, new String[] {"foo1"});
  293. // No exception should be thrown
  294. }
  295. @Test
  296. public void shouldGetRelativeFile() {
  297. assertThat(ProjectReactorBuilder.resolvePath(getResource(this.getClass(), "/"), "shouldGetFile/foo.properties"))
  298. .isEqualTo(getResource(this.getClass(), "shouldGetFile/foo.properties"));
  299. }
  300. @Test
  301. public void shouldGetAbsoluteFile() {
  302. File file = getResource(this.getClass(), "shouldGetFile/foo.properties");
  303. assertThat(ProjectReactorBuilder.resolvePath(getResource(this.getClass(), "/"), file.getAbsolutePath()))
  304. .isEqualTo(file);
  305. }
  306. @Test
  307. public void shouldMergeParentProperties() {
  308. // Use a random value to avoid VM optimization that would create constant String and make s1 and s2 the same object
  309. int i = (int) Math.random() * 10;
  310. String s1 = "value" + i;
  311. String s2 = "value" + i;
  312. Map<String, String> parentProps = new HashMap<>();
  313. parentProps.put("toBeMergeProps", "fooParent");
  314. parentProps.put("existingChildProp", "barParent");
  315. parentProps.put("duplicatedProp", s1);
  316. parentProps.put("sonar.projectDescription", "Desc from Parent");
  317. Map<String, String> childProps = new HashMap<>();
  318. childProps.put("existingChildProp", "barChild");
  319. childProps.put("otherProp", "tutuChild");
  320. childProps.put("duplicatedProp", s2);
  321. ProjectReactorBuilder.mergeParentProperties(childProps, parentProps);
  322. assertThat(childProps).hasSize(4);
  323. assertThat(childProps.get("toBeMergeProps")).isEqualTo("fooParent");
  324. assertThat(childProps.get("existingChildProp")).isEqualTo("barChild");
  325. assertThat(childProps.get("otherProp")).isEqualTo("tutuChild");
  326. assertThat(childProps.get("sonar.projectDescription")).isNull();
  327. assertThat(childProps.get("duplicatedProp")).isSameAs(parentProps.get("duplicatedProp"));
  328. }
  329. @Test
  330. public void shouldInitRootWorkDir() {
  331. ProjectReactorBuilder builder = new ProjectReactorBuilder(new ProcessedScannerProperties(
  332. new RawScannerProperties(emptyMap()), new EmptyExternalProjectKeyAndOrganization()),
  333. mock(AnalysisWarnings.class));
  334. File baseDir = new File("target/tmp/baseDir");
  335. File workDir = builder.initRootProjectWorkDir(baseDir, emptyMap());
  336. assertThat(workDir).isEqualTo(new File(baseDir, ".sonar"));
  337. }
  338. @Test
  339. public void shouldInitWorkDirWithCustomRelativeFolder() {
  340. Map<String, String> props = singletonMap("sonar.working.directory", ".foo");
  341. ProjectReactorBuilder builder = new ProjectReactorBuilder(new ProcessedScannerProperties(
  342. new RawScannerProperties(props),
  343. new EmptyExternalProjectKeyAndOrganization()),
  344. mock(AnalysisWarnings.class));
  345. File baseDir = new File("target/tmp/baseDir");
  346. File workDir = builder.initRootProjectWorkDir(baseDir, props);
  347. assertThat(workDir).isEqualTo(new File(baseDir, ".foo"));
  348. }
  349. @Test
  350. public void shouldInitRootWorkDirWithCustomAbsoluteFolder() {
  351. Map<String, String> props = singletonMap("sonar.working.directory", new File("src").getAbsolutePath());
  352. ProjectReactorBuilder builder = new ProjectReactorBuilder(new ProcessedScannerProperties(
  353. new RawScannerProperties(props), new EmptyExternalProjectKeyAndOrganization()),
  354. mock(AnalysisWarnings.class));
  355. File baseDir = new File("target/tmp/baseDir");
  356. File workDir = builder.initRootProjectWorkDir(baseDir, props);
  357. assertThat(workDir).isEqualTo(new File("src").getAbsoluteFile());
  358. }
  359. @Test
  360. public void shouldFailIf2ModulesWithSameKey() {
  361. Map<String, String> props = singletonMap("sonar.projectKey", "root");
  362. ProjectDefinition root = ProjectDefinition.create().setProperties(props);
  363. Map<String, String> props1 = singletonMap("sonar.projectKey", "mod1");
  364. root.addSubProject(ProjectDefinition.create().setProperties(props1));
  365. // Check uniqueness of a new module: OK
  366. Map<String, String> props2 = singletonMap("sonar.projectKey", "mod2");
  367. ProjectDefinition mod2 = ProjectDefinition.create().setProperties(props2);
  368. ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root);
  369. // Now, add it and check again
  370. root.addSubProject(mod2);
  371. thrown.expect(MessageException.class);
  372. thrown.expectMessage("Project 'root' can't have 2 modules with the following key: mod2");
  373. ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root);
  374. }
  375. @Test
  376. public void shouldAcceptNoProjectName() {
  377. ProjectDefinition rootProject = loadProjectDefinition("simple-project-with-missing-project-name");
  378. assertThat(rootProject.getOriginalName()).isNull();
  379. assertThat(rootProject.getName()).isEqualTo("com.foo.project");
  380. }
  381. @Test
  382. public void shouldSetModuleKeyIfNotPresent() {
  383. Map<String, String> props = new HashMap<>();
  384. props.put("sonar.projectVersion", "1.0");
  385. // should be set
  386. ProjectReactorBuilder.setModuleKeyAndNameIfNotDefined(props, "foo", "parent");
  387. assertThat(props.get("sonar.moduleKey")).isEqualTo("parent:foo");
  388. assertThat(props.get("sonar.projectName")).isEqualTo("foo");
  389. // but not this 2nd time
  390. ProjectReactorBuilder.setModuleKeyAndNameIfNotDefined(props, "bar", "parent");
  391. assertThat(props.get("sonar.moduleKey")).isEqualTo("parent:foo");
  392. assertThat(props.get("sonar.projectName")).isEqualTo("foo");
  393. }
  394. private ProjectDefinition loadProjectDefinition(String projectFolder) {
  395. Map<String, String> props = loadProps(projectFolder);
  396. ProcessedScannerProperties bootstrapProps = new ProcessedScannerProperties(
  397. new RawScannerProperties(props),
  398. new EmptyExternalProjectKeyAndOrganization());
  399. ProjectReactor projectReactor = new ProjectReactorBuilder(bootstrapProps, mock(AnalysisWarnings.class)).execute();
  400. return projectReactor.getRoot();
  401. }
  402. protected static Properties toProperties(File propertyFile) {
  403. Properties propsFromFile = new Properties();
  404. try (FileInputStream fileInputStream = new FileInputStream(propertyFile)) {
  405. propsFromFile.load(fileInputStream);
  406. } catch (IOException e) {
  407. throw new IllegalStateException("Impossible to read the property file: " + propertyFile.getAbsolutePath(), e);
  408. }
  409. // Trim properties
  410. for (String propKey : propsFromFile.stringPropertyNames()) {
  411. propsFromFile.setProperty(propKey, StringUtils.trim(propsFromFile.getProperty(propKey)));
  412. }
  413. return propsFromFile;
  414. }
  415. private Map<String, String> loadProps(String projectFolder) {
  416. Map<String, String> props = new HashMap<>();
  417. Properties runnerProps = toProperties(getResource(this.getClass(), projectFolder + "/sonar-project.properties"));
  418. for (final String name : runnerProps.stringPropertyNames()) {
  419. props.put(name, runnerProps.getProperty(name));
  420. }
  421. props.put("sonar.projectBaseDir", getResource(this.getClass(), projectFolder).getAbsolutePath());
  422. return props;
  423. }
  424. @Test
  425. public void shouldGetList() {
  426. Map<String, String> props = new HashMap<>();
  427. props.put("prop", " foo ,, bar , toto,tutu");
  428. assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu");
  429. }
  430. @Test
  431. public void shouldGetListWithComma() {
  432. Map<String, String> props = new HashMap<>();
  433. props.put("prop", "\"foo,bar\", toto,tutu");
  434. assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).containsOnly("foo,bar", "toto", "tutu");
  435. }
  436. @Test
  437. public void shouldGetEmptyList() {
  438. Map<String, String> props = new HashMap<>();
  439. props.put("prop", "");
  440. assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).isEmpty();
  441. }
  442. @Test
  443. public void shouldGetListFromFile() throws IOException {
  444. String filePath = "shouldGetList/foo.properties";
  445. Map<String, String> props = loadPropsFromFile(filePath);
  446. assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu");
  447. }
  448. @Test
  449. public void doNotMixPropertiesWhenModuleKeyIsPrefixOfAnother() throws IOException {
  450. ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-same-prefix");
  451. // CHECK ROOT
  452. assertThat(rootProject.getKey()).isEqualTo("com.foo.project");
  453. assertThat(rootProject.getName()).isEqualTo("Foo Project");
  454. assertThat(rootProject.getVersion()).isEqualTo("1.0-SNAPSHOT");
  455. assertThat(rootProject.getDescription()).isEqualTo("Description of Foo Project");
  456. assertThat(rootProject.sources().contains("sources")).isTrue();
  457. assertThat(rootProject.tests().contains("tests")).isTrue();
  458. // Module properties must have been cleaned
  459. assertThat(rootProject.properties().get("module1.sonar.projectKey")).isNull();
  460. assertThat(rootProject.properties().get("module2.sonar.projectKey")).isNull();
  461. // Check baseDir and workDir
  462. assertThat(rootProject.getBaseDir().getCanonicalFile())
  463. .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix"));
  464. assertThat(rootProject.getWorkDir().getCanonicalFile())
  465. .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"), ".sonar"));
  466. // CHECK MODULES
  467. List<ProjectDefinition> modules = rootProject.getSubProjects();
  468. assertThat(modules.size()).isEqualTo(2);
  469. // Module 1
  470. ProjectDefinition module1 = modules.get(0);
  471. assertThat(module1.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1"));
  472. assertThat(module1.getKey()).isEqualTo("com.foo.project:module1");
  473. assertThat(module1.getName()).isEqualTo("module1");
  474. assertThat(module1.getVersion()).isEqualTo("1.0-SNAPSHOT");
  475. // Description should not be inherited from parent if not set
  476. assertThat(module1.getDescription()).isNull();
  477. assertThat(module1.sources()).contains("sources");
  478. assertThat(module1.tests()).contains("tests");
  479. // and module properties must have been cleaned
  480. assertThat(module1.properties().get("module1.sonar.projectKey")).isNull();
  481. assertThat(module1.properties().get("module2.sonar.projectKey")).isNull();
  482. // Check baseDir and workDir
  483. assertThat(module1.getBaseDir().getCanonicalFile())
  484. .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1"));
  485. assertThat(module1.getWorkDir().getCanonicalFile())
  486. .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"), ".sonar/com.foo.project_module1"));
  487. // Module 1 Feature
  488. ProjectDefinition module1Feature = modules.get(1);
  489. assertThat(module1Feature.getBaseDir().getCanonicalFile()).isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1.feature"));
  490. assertThat(module1Feature.getKey()).isEqualTo("com.foo.project:com.foo.project.module1.feature");
  491. assertThat(module1Feature.getName()).isEqualTo("Foo Module 1 Feature");
  492. assertThat(module1Feature.getVersion()).isEqualTo("1.0-SNAPSHOT");
  493. assertThat(module1Feature.getDescription()).isEqualTo("Description of Module 1 Feature");
  494. assertThat(module1Feature.sources()).contains("src");
  495. assertThat(module1Feature.tests()).contains("tests");
  496. // and module properties must have been cleaned
  497. assertThat(module1Feature.properties().get("module1.sonar.projectKey")).isNull();
  498. assertThat(module1Feature.properties().get("module2.sonar.projectKey")).isNull();
  499. // Check baseDir and workDir
  500. assertThat(module1Feature.getBaseDir().getCanonicalFile())
  501. .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1.feature"));
  502. assertThat(module1Feature.getWorkDir().getCanonicalFile())
  503. .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"), ".sonar/com.foo.project_com.foo.project.module1.feature"));
  504. }
  505. private Map<String, String> loadPropsFromFile(String filePath) throws IOException {
  506. Properties props = new Properties();
  507. try (FileInputStream fileInputStream = new FileInputStream(getResource(this.getClass(), filePath))) {
  508. props.load(fileInputStream);
  509. }
  510. Map<String, String> result = new HashMap<>();
  511. for (Map.Entry<Object, Object> entry : props.entrySet()) {
  512. result.put(entry.getKey().toString(), entry.getValue().toString());
  513. }
  514. return result;
  515. }
  516. /**
  517. * Search for a test resource in the classpath. For example getResource("org/sonar/MyClass/foo.txt");
  518. *
  519. * @param path the starting slash is optional
  520. * @return the resource. Null if resource not found
  521. */
  522. public static File getResource(String path) {
  523. String resourcePath = path;
  524. if (!resourcePath.startsWith("/")) {
  525. resourcePath = "/" + resourcePath;
  526. }
  527. URL url = ProjectReactorBuilderTest.class.getResource(resourcePath);
  528. if (url != null) {
  529. return FileUtils.toFile(url);
  530. }
  531. return null;
  532. }
  533. /**
  534. * Search for a resource in the classpath. For example calling the method getResource(getClass(), "myTestName/foo.txt") from
  535. * the class org.sonar.Foo loads the file $basedir/src/test/resources/org/sonar/Foo/myTestName/foo.txt
  536. *
  537. * @return the resource. Null if resource not found
  538. */
  539. public static File getResource(Class baseClass, String path) {
  540. String resourcePath = StringUtils.replaceChars(baseClass.getCanonicalName(), '.', '/');
  541. if (!path.startsWith("/")) {
  542. resourcePath += "/";
  543. }
  544. resourcePath += path;
  545. return getResource(resourcePath);
  546. }
  547. }