org.sonar.batch.scan.ProjectReactorBuilderTest.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.batch.scan.ProjectReactorBuilderTest.java

Source

/*
 * SonarQube
 * Copyright (C) 2009-2016 SonarSource SA
 * mailto:contact AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.batch.scan;

import com.google.common.collect.Maps;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.batch.AnalysisMode;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.batch.analysis.AnalysisProperties;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ProjectReactorBuilderTest {

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Rule
    public LogTester logTester = new LogTester();

    private AnalysisMode mode;

    @Before
    public void setUp() {
        mode = mock(AnalysisMode.class);
    }

    @Test
    public void shouldDefineSimpleProject() {
        ProjectDefinition projectDefinition = loadProjectDefinition("simple-project");

        assertThat(projectDefinition.getKey()).isEqualTo("com.foo.project");
        assertThat(projectDefinition.getName()).isEqualTo("Foo Project");
        assertThat(projectDefinition.getVersion()).isEqualTo("1.0-SNAPSHOT");
        assertThat(projectDefinition.getDescription()).isEqualTo("Description of Foo Project");
        assertThat(projectDefinition.getSourceDirs()).contains("sources");
    }

    @Test
    public void shouldFailIfUnexistingSourceDirectory() {
        thrown.expect(IllegalStateException.class);
        thrown.expectMessage(
                "The folder 'unexisting-source-dir' does not exist for 'com.foo.project' (base directory = "
                        + getResource(this.getClass(), "simple-project-with-unexisting-source-dir") + ")");

        loadProjectDefinition("simple-project-with-unexisting-source-dir");
    }

    @Test
    public void fail_if_sources_not_set() {
        thrown.expect(IllegalStateException.class);
        thrown.expectMessage(
                "You must define the following mandatory properties for 'com.foo.project': sonar.sources");
        loadProjectDefinition("simple-project-with-missing-source-dir");
    }

    @Test
    public void shouldNotFailIfBlankSourceDirectory() {
        loadProjectDefinition("simple-project-with-blank-source-dir");
    }

    @Test
    public void modulesDuplicateIds() {
        thrown.expect(IllegalStateException.class);
        thrown.expectMessage("Two modules have the same id: 'module1'. Each module must have a unique id.");

        loadProjectDefinition("multi-module-duplicate-id");
    }

    @Test
    public void modulesRepeatedIds() {
        ProjectDefinition rootProject = loadProjectDefinition("multi-module-repeated-id");

        List<ProjectDefinition> modules = rootProject.getSubProjects();
        assertThat(modules.size()).isEqualTo(1);
        // Module 1
        ProjectDefinition module1 = modules.get(0);
        assertThat(module1.getKey()).isEqualTo("com.foo.project:module1");
        assertThat(module1.getName()).isEqualTo("Foo Module 1");

        // Module 1 -> Module 1
        ProjectDefinition module1_module1 = module1.getSubProjects().get(0);
        assertThat(module1_module1.getKey()).isEqualTo("com.foo.project:module1:module1");
        assertThat(module1_module1.getName()).isEqualTo("Foo Sub Module 1");
    }

    @Test
    public void shouldDefineMultiModuleProjectWithDefinitionsAllInRootProject() throws IOException {
        ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-all-in-root");

        // CHECK ROOT
        assertThat(rootProject.getKey()).isEqualTo("com.foo.project");
        assertThat(rootProject.getName()).isEqualTo("Foo Project");
        assertThat(rootProject.getVersion()).isEqualTo("1.0-SNAPSHOT");
        assertThat(rootProject.getDescription()).isEqualTo("Description of Foo Project");
        // root project must not contain some properties - even if they are defined in the root properties file
        assertThat(rootProject.getSourceDirs().contains("sources")).isFalse();
        assertThat(rootProject.getTestDirs().contains("tests")).isFalse();
        // and module properties must have been cleaned
        assertThat(rootProject.properties().get("module1.sonar.projectKey")).isNull();
        assertThat(rootProject.properties().get("module2.sonar.projectKey")).isNull();
        // Check baseDir and workDir
        assertThat(rootProject.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root"));
        assertThat(rootProject.getWorkDir().getCanonicalFile()).isEqualTo(
                new File(getResource(this.getClass(), "multi-module-definitions-all-in-root"), ".sonar"));

        // CHECK MODULES
        List<ProjectDefinition> modules = rootProject.getSubProjects();
        assertThat(modules.size()).isEqualTo(2);

        // Module 1
        ProjectDefinition module1 = modules.get(0);
        assertThat(module1.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module1"));
        assertThat(module1.getKey()).isEqualTo("com.foo.project:module1");
        assertThat(module1.getName()).isEqualTo("module1");
        assertThat(module1.getVersion()).isEqualTo("1.0-SNAPSHOT");
        // Description should not be inherited from parent if not set
        assertThat(module1.getDescription()).isNull();
        assertThat(module1.getSourceDirs()).contains("sources");
        assertThat(module1.getTestDirs()).contains("tests");
        assertThat(module1.getBinaries()).contains("target/classes");
        // and module properties must have been cleaned
        assertThat(module1.properties().get("module1.sonar.projectKey")).isNull();
        assertThat(module1.properties().get("module2.sonar.projectKey")).isNull();
        // Check baseDir and workDir
        assertThat(module1.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module1"));
        assertThat(module1.getWorkDir().getCanonicalFile())
                .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-all-in-root"),
                        ".sonar/com.foo.project_module1"));

        // Module 2
        ProjectDefinition module2 = modules.get(1);
        assertThat(module2.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module2"));
        assertThat(module2.getKey()).isEqualTo("com.foo.project:com.foo.project.module2");
        assertThat(module2.getName()).isEqualTo("Foo Module 2");
        assertThat(module2.getVersion()).isEqualTo("1.0-SNAPSHOT");
        assertThat(module2.getDescription()).isEqualTo("Description of Module 2");
        assertThat(module2.getSourceDirs()).contains("src");
        assertThat(module2.getTestDirs()).contains("tests");
        assertThat(module2.getBinaries()).contains("target/classes");
        // and module properties must have been cleaned
        assertThat(module2.properties().get("module1.sonar.projectKey")).isNull();
        assertThat(module2.properties().get("module2.sonar.projectKey")).isNull();
        // Check baseDir and workDir
        assertThat(module2.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-module-definitions-all-in-root/module2"));
        assertThat(module2.getWorkDir().getCanonicalFile())
                .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-all-in-root"),
                        ".sonar/com.foo.project_com.foo.project.module2"));
    }

    // SONAR-4876
    @Test
    public void shouldDefineMultiModuleProjectWithModuleKey() {
        ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-moduleKey");

        // CHECK ROOT
        // module properties must have been cleaned
        assertThat(rootProject.properties().get("module1.sonar.moduleKey")).isNull();
        assertThat(rootProject.properties().get("module2.sonar.moduleKey")).isNull();

        // CHECK MODULES
        List<ProjectDefinition> modules = rootProject.getSubProjects();
        assertThat(modules.size()).isEqualTo(2);

        // Module 2
        ProjectDefinition module2 = modules.get(1);
        assertThat(module2.getKey()).isEqualTo("com.foo.project.module2");
    }

    // SONARPLUGINS-2421
    @Test
    public void shouldDefineMultiLanguageProjectWithDefinitionsAllInRootProject() throws IOException {
        ProjectDefinition rootProject = loadProjectDefinition("multi-language-definitions-all-in-root");

        // CHECK ROOT
        assertThat(rootProject.getKey()).isEqualTo("example");
        assertThat(rootProject.getName()).isEqualTo("Example");
        assertThat(rootProject.getVersion()).isEqualTo("1.0");

        // CHECK MODULES
        List<ProjectDefinition> modules = rootProject.getSubProjects();
        assertThat(modules.size()).isEqualTo(2);

        // Module 1
        ProjectDefinition module1 = modules.get(0);
        assertThat(module1.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-language-definitions-all-in-root"));
        assertThat(module1.getSourceDirs()).contains("src/main/java");
        // and module properties must have been cleaned
        assertThat(module1.getWorkDir().getCanonicalFile())
                .isEqualTo(new File(getResource(this.getClass(), "multi-language-definitions-all-in-root"),
                        ".sonar/example_java-module"));

        // Module 2
        ProjectDefinition module2 = modules.get(1);
        assertThat(module2.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-language-definitions-all-in-root"));
        assertThat(module2.getSourceDirs()).contains("src/main/groovy");
        // and module properties must have been cleaned
        assertThat(module2.getWorkDir().getCanonicalFile())
                .isEqualTo(new File(getResource(this.getClass(), "multi-language-definitions-all-in-root"),
                        ".sonar/example_groovy-module"));
    }

    @Test
    public void shouldDefineMultiModuleProjectWithBaseDir() {
        ProjectDefinition rootProject = loadProjectDefinition("multi-module-with-basedir");
        List<ProjectDefinition> modules = rootProject.getSubProjects();
        assertThat(modules.size()).isEqualTo(1);
        assertThat(modules.get(0).getKey()).isEqualTo("com.foo.project:com.foo.project.module1");
    }

    @Test
    public void shouldFailIfUnexistingModuleBaseDir() {
        thrown.expect(IllegalStateException.class);
        thrown.expectMessage("The base directory of the module 'module1' does not exist: "
                + getResource(this.getClass(), "multi-module-with-unexisting-basedir").getAbsolutePath()
                + File.separator + "module1");

        loadProjectDefinition("multi-module-with-unexisting-basedir");
    }

    @Test
    public void shouldFailIfUnexistingSourceFolderInheritedInMultimodule() {
        thrown.expect(IllegalStateException.class);
        thrown.expectMessage(
                "The folder 'unexisting-source-dir' does not exist for 'com.foo.project:module1' (base directory = "
                        + getResource(this.getClass(), "multi-module-with-unexisting-source-dir").getAbsolutePath()
                        + File.separator + "module1)");

        loadProjectDefinition("multi-module-with-unexisting-source-dir");
    }

    @Test
    public void shouldFailIfExplicitUnexistingTestFolder() {
        thrown.expect(IllegalStateException.class);
        thrown.expectMessage("The folder 'tests' does not exist for 'com.foo.project' (base directory = "
                + getResource(this.getClass(), "simple-project-with-unexisting-test-dir").getAbsolutePath());

        loadProjectDefinition("simple-project-with-unexisting-test-dir");
    }

    @Test
    public void shouldFailIfExplicitUnexistingTestFolderOnModule() {
        thrown.expect(IllegalStateException.class);
        thrown.expectMessage("The folder 'tests' does not exist for 'module1' (base directory = "
                + getResource(this.getClass(), "multi-module-with-explicit-unexisting-test-dir").getAbsolutePath()
                + File.separator + "module1)");

        loadProjectDefinition("multi-module-with-explicit-unexisting-test-dir");
    }

    @Test
    public void multiModuleProperties() {
        ProjectDefinition projectDefinition = loadProjectDefinition("big-multi-module-definitions-all-in-root");

        assertThat(projectDefinition.properties().get("module11.property")).isNull();
        assertThat(projectDefinition.properties().get("sonar.profile")).isEqualTo("Foo");
        ProjectDefinition module1 = null;
        ProjectDefinition module2 = null;
        for (ProjectDefinition prj : projectDefinition.getSubProjects()) {
            if (prj.getKey().equals("com.foo.project:module1")) {
                module1 = prj;
            } else if (prj.getKey().equals("com.foo.project:module2")) {
                module2 = prj;
            }
        }
        assertThat(module1.properties().get("module11.property")).isNull();
        assertThat(module1.properties().get("property")).isNull();
        assertThat(module1.properties().get("sonar.profile")).isEqualTo("Foo");
        assertThat(module2.properties().get("module11.property")).isNull();
        assertThat(module2.properties().get("property")).isNull();
        assertThat(module2.properties().get("sonar.profile")).isEqualTo("Foo");

        ProjectDefinition module11 = null;
        ProjectDefinition module12 = null;
        for (ProjectDefinition prj : module1.getSubProjects()) {
            if (prj.getKey().equals("com.foo.project:module1:module11")) {
                module11 = prj;
            } else if (prj.getKey().equals("com.foo.project:module1:module12")) {
                module12 = prj;
            }
        }
        assertThat(module11.properties().get("module1.module11.property")).isNull();
        assertThat(module11.properties().get("module11.property")).isNull();
        assertThat(module11.properties().get("property")).isEqualTo("My module11 property");
        assertThat(module11.properties().get("sonar.profile")).isEqualTo("Foo");
        assertThat(module12.properties().get("module11.property")).isNull();
        assertThat(module12.properties().get("property")).isNull();
        assertThat(module12.properties().get("sonar.profile")).isEqualTo("Foo");
    }

    @Test
    public void shouldRemoveModulePropertiesFromTaskProperties() {
        Map<String, String> props = loadProps("big-multi-module-definitions-all-in-root");

        AnalysisProperties taskProperties = new AnalysisProperties(props, null);
        assertThat(taskProperties.property("module1.module11.property")).isEqualTo("My module11 property");

        new ProjectReactorBuilder(taskProperties, mode).execute();

        assertThat(taskProperties.property("module1.module11.property")).isNull();
    }

    @Test
    public void shouldFailIfMandatoryPropertiesAreNotPresent() {
        Map<String, String> props = new HashMap<>();
        props.put("foo1", "bla");
        props.put("foo4", "bla");

        thrown.expect(IllegalStateException.class);
        thrown.expectMessage("You must define the following mandatory properties for 'Unknown': foo2, foo3");

        ProjectReactorBuilder.checkMandatoryProperties(props, new String[] { "foo1", "foo2", "foo3" });
    }

    @Test
    public void shouldFailIfMandatoryPropertiesAreNotPresentButWithProjectKey() {
        Map<String, String> props = new HashMap<>();
        props.put("foo1", "bla");
        props.put("sonar.projectKey", "my-project");

        thrown.expect(IllegalStateException.class);
        thrown.expectMessage("You must define the following mandatory properties for 'my-project': foo2, foo3");

        ProjectReactorBuilder.checkMandatoryProperties(props, new String[] { "foo1", "foo2", "foo3" });
    }

    @Test
    public void shouldNotFailIfMandatoryPropertiesArePresent() {
        Map<String, String> props = new HashMap<>();
        props.put("foo1", "bla");
        props.put("foo4", "bla");

        ProjectReactorBuilder.checkMandatoryProperties(props, new String[] { "foo1" });

        // No exception should be thrown
    }

    @Test
    public void shouldGetRelativeFile() {
        assertThat(ProjectReactorBuilder.resolvePath(getResource(this.getClass(), "/"),
                "shouldGetFile/foo.properties"))
                        .isEqualTo(getResource(this.getClass(), "shouldGetFile/foo.properties"));
    }

    @Test
    public void shouldGetAbsoluteFile() {
        File file = getResource(this.getClass(), "shouldGetFile/foo.properties");

        assertThat(ProjectReactorBuilder.resolvePath(getResource(this.getClass(), "/"), file.getAbsolutePath()))
                .isEqualTo(file);
    }

    @Test
    public void shouldMergeParentProperties() {
        // Use a random value to avoid VM optimization that would create constant String and make s1 and s2 the same object
        int i = (int) Math.random() * 10;
        String s1 = "value" + i;
        String s2 = "value" + i;
        Map<String, String> parentProps = new HashMap<>();
        parentProps.put("toBeMergeProps", "fooParent");
        parentProps.put("existingChildProp", "barParent");
        parentProps.put("duplicatedProp", s1);
        parentProps.put("sonar.projectDescription", "Desc from Parent");

        Map<String, String> childProps = new HashMap<>();
        childProps.put("existingChildProp", "barChild");
        childProps.put("otherProp", "tutuChild");
        childProps.put("duplicatedProp", s2);

        ProjectReactorBuilder.mergeParentProperties(childProps, parentProps);

        assertThat(childProps).hasSize(4);
        assertThat(childProps.get("toBeMergeProps")).isEqualTo("fooParent");
        assertThat(childProps.get("existingChildProp")).isEqualTo("barChild");
        assertThat(childProps.get("otherProp")).isEqualTo("tutuChild");
        assertThat(childProps.get("sonar.projectDescription")).isNull();
        assertThat(childProps.get("duplicatedProp")).isSameAs(parentProps.get("duplicatedProp"));
    }

    @Test
    public void shouldInitRootWorkDir() {
        ProjectReactorBuilder builder = new ProjectReactorBuilder(
                new AnalysisProperties(Maps.<String, String>newHashMap(), null), mode);
        File baseDir = new File("target/tmp/baseDir");

        File workDir = builder.initRootProjectWorkDir(baseDir, Maps.<String, String>newHashMap());

        assertThat(workDir).isEqualTo(new File(baseDir, ".sonar"));
    }

    @Test
    public void nonAssociatedMode() {
        when(mode.isIssues()).thenReturn(true);
        ProjectDefinition project = loadProjectDefinition("multi-module-with-basedir-not-associated");

        assertThat(project.getKey()).isEqualTo("project");
    }

    @Test
    public void shouldInitWorkDirWithCustomRelativeFolder() {
        Map<String, String> props = Maps.<String, String>newHashMap();
        props.put("sonar.working.directory", ".foo");
        ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null), mode);
        File baseDir = new File("target/tmp/baseDir");

        File workDir = builder.initRootProjectWorkDir(baseDir, props);

        assertThat(workDir).isEqualTo(new File(baseDir, ".foo"));
    }

    @Test
    public void shouldInitRootWorkDirWithCustomAbsoluteFolder() {
        Map<String, String> props = Maps.<String, String>newHashMap();
        props.put("sonar.working.directory", new File("src").getAbsolutePath());
        ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null), mode);
        File baseDir = new File("target/tmp/baseDir");

        File workDir = builder.initRootProjectWorkDir(baseDir, props);

        assertThat(workDir).isEqualTo(new File("src").getAbsoluteFile());
    }

    @Test
    public void shouldFailIf2ModulesWithSameKey() {
        Map<String, String> props = new HashMap<>();
        props.put("sonar.projectKey", "root");
        ProjectDefinition root = ProjectDefinition.create().setProperties(props);

        Map<String, String> props1 = new HashMap<>();
        props1.put("sonar.projectKey", "mod1");
        root.addSubProject(ProjectDefinition.create().setProperties(props1));

        // Check uniqueness of a new module: OK
        Map<String, String> props2 = new HashMap<>();
        props2.put("sonar.projectKey", "mod2");
        ProjectDefinition mod2 = ProjectDefinition.create().setProperties(props2);
        ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root);

        // Now, add it and check again
        root.addSubProject(mod2);

        thrown.expect(IllegalStateException.class);
        thrown.expectMessage("Project 'root' can't have 2 modules with the following key: mod2");

        ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root);
    }

    @Test
    public void shouldSetModuleKeyIfNotPresent() {
        Map<String, String> props = new HashMap<>();
        props.put("sonar.projectVersion", "1.0");

        // should be set
        ProjectReactorBuilder.setModuleKeyAndNameIfNotDefined(props, "foo", "parent");
        assertThat(props.get("sonar.moduleKey")).isEqualTo("parent:foo");
        assertThat(props.get("sonar.projectName")).isEqualTo("foo");

        // but not this 2nd time
        ProjectReactorBuilder.setModuleKeyAndNameIfNotDefined(props, "bar", "parent");
        assertThat(props.get("sonar.moduleKey")).isEqualTo("parent:foo");
        assertThat(props.get("sonar.projectName")).isEqualTo("foo");
    }

    private ProjectDefinition loadProjectDefinition(String projectFolder) {
        Map<String, String> props = loadProps(projectFolder);
        AnalysisProperties bootstrapProps = new AnalysisProperties(props, null);
        ProjectReactor projectReactor = new ProjectReactorBuilder(bootstrapProps, mode).execute();
        return projectReactor.getRoot();
    }

    protected static Properties toProperties(File propertyFile) {
        Properties propsFromFile = new Properties();
        try (FileInputStream fileInputStream = new FileInputStream(propertyFile)) {
            propsFromFile.load(fileInputStream);
        } catch (IOException e) {
            throw new IllegalStateException(
                    "Impossible to read the property file: " + propertyFile.getAbsolutePath(), e);
        }
        // Trim properties
        for (String propKey : propsFromFile.stringPropertyNames()) {
            propsFromFile.setProperty(propKey, StringUtils.trim(propsFromFile.getProperty(propKey)));
        }
        return propsFromFile;
    }

    private Map<String, String> loadProps(String projectFolder) {
        Map<String, String> props = Maps.<String, String>newHashMap();
        Properties runnerProps = toProperties(
                getResource(this.getClass(), projectFolder + "/sonar-project.properties"));
        for (final String name : runnerProps.stringPropertyNames()) {
            props.put(name, runnerProps.getProperty(name));
        }
        props.put("sonar.projectBaseDir", getResource(this.getClass(), projectFolder).getAbsolutePath());
        return props;
    }

    public Map<String, String> toMap(Properties props) {
        Map<String, String> result = new HashMap<>();
        for (Map.Entry<Object, Object> entry : props.entrySet()) {
            result.put(entry.getKey().toString(), entry.getValue().toString());
        }
        return result;
    }

    @Test
    public void shouldGetList() {
        Map<String, String> props = new HashMap<>();

        props.put("prop", "  foo  ,,  bar  , \n\ntoto,tutu");
        assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto",
                "tutu");
    }

    @Test
    public void shouldGetEmptyList() {
        Map<String, String> props = new HashMap<>();

        props.put("prop", "");
        assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).isEmpty();
    }

    @Test
    public void shouldGetListFromFile() throws IOException {
        String filePath = "shouldGetList/foo.properties";
        Map<String, String> props = loadPropsFromFile(filePath);

        assertThat(ProjectReactorBuilder.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto",
                "tutu");
    }

    @Test
    public void shouldDefineProjectWithBuildDir() {
        ProjectDefinition rootProject = loadProjectDefinition("simple-project-with-build-dir");
        File buildDir = rootProject.getBuildDir();
        assertThat(buildDir).isDirectory().exists();
        assertThat(new File(buildDir, "report.txt")).isFile().exists();
        assertThat(buildDir.getName()).isEqualTo("build");
    }

    @Test
    public void doNotMixPropertiesWhenModuleKeyIsPrefixOfAnother() throws IOException {
        ProjectDefinition rootProject = loadProjectDefinition("multi-module-definitions-same-prefix");

        // CHECK ROOT
        assertThat(rootProject.getKey()).isEqualTo("com.foo.project");
        assertThat(rootProject.getName()).isEqualTo("Foo Project");
        assertThat(rootProject.getVersion()).isEqualTo("1.0-SNAPSHOT");
        assertThat(rootProject.getDescription()).isEqualTo("Description of Foo Project");
        // root project must not contain some properties - even if they are defined in the root properties file
        assertThat(rootProject.getSourceDirs().contains("sources")).isFalse();
        assertThat(rootProject.getTestDirs().contains("tests")).isFalse();
        // and module properties must have been cleaned
        assertThat(rootProject.properties().get("module1.sonar.projectKey")).isNull();
        assertThat(rootProject.properties().get("module2.sonar.projectKey")).isNull();
        // Check baseDir and workDir
        assertThat(rootProject.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix"));
        assertThat(rootProject.getWorkDir().getCanonicalFile()).isEqualTo(
                new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"), ".sonar"));

        // CHECK MODULES
        List<ProjectDefinition> modules = rootProject.getSubProjects();
        assertThat(modules.size()).isEqualTo(2);

        // Module 1
        ProjectDefinition module1 = modules.get(0);
        assertThat(module1.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1"));
        assertThat(module1.getKey()).isEqualTo("com.foo.project:module1");
        assertThat(module1.getName()).isEqualTo("module1");
        assertThat(module1.getVersion()).isEqualTo("1.0-SNAPSHOT");
        // Description should not be inherited from parent if not set
        assertThat(module1.getDescription()).isNull();
        assertThat(module1.getSourceDirs()).contains("sources");
        assertThat(module1.getTestDirs()).contains("tests");
        assertThat(module1.getBinaries()).contains("target/classes");
        // and module properties must have been cleaned
        assertThat(module1.properties().get("module1.sonar.projectKey")).isNull();
        assertThat(module1.properties().get("module2.sonar.projectKey")).isNull();
        // Check baseDir and workDir
        assertThat(module1.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1"));
        assertThat(module1.getWorkDir().getCanonicalFile())
                .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"),
                        ".sonar/com.foo.project_module1"));

        // Module 1 Feature
        ProjectDefinition module1Feature = modules.get(1);
        assertThat(module1Feature.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1.feature"));
        assertThat(module1Feature.getKey()).isEqualTo("com.foo.project:com.foo.project.module1.feature");
        assertThat(module1Feature.getName()).isEqualTo("Foo Module 1 Feature");
        assertThat(module1Feature.getVersion()).isEqualTo("1.0-SNAPSHOT");
        assertThat(module1Feature.getDescription()).isEqualTo("Description of Module 1 Feature");
        assertThat(module1Feature.getSourceDirs()).contains("src");
        assertThat(module1Feature.getTestDirs()).contains("tests");
        assertThat(module1Feature.getBinaries()).contains("target/classes");
        // and module properties must have been cleaned
        assertThat(module1Feature.properties().get("module1.sonar.projectKey")).isNull();
        assertThat(module1Feature.properties().get("module2.sonar.projectKey")).isNull();
        // Check baseDir and workDir
        assertThat(module1Feature.getBaseDir().getCanonicalFile())
                .isEqualTo(getResource(this.getClass(), "multi-module-definitions-same-prefix/module1.feature"));
        assertThat(module1Feature.getWorkDir().getCanonicalFile())
                .isEqualTo(new File(getResource(this.getClass(), "multi-module-definitions-same-prefix"),
                        ".sonar/com.foo.project_com.foo.project.module1.feature"));
    }

    @Test
    public void should_log_a_warning_when_a_dropped_property_is_present() {
        Map<String, String> props = loadProps("simple-project");
        props.put("sonar.qualitygate", "somevalue");
        AnalysisProperties bootstrapProps = new AnalysisProperties(props, null);
        new ProjectReactorBuilder(bootstrapProps, mode).execute();

        assertThat(logTester.logs(LoggerLevel.WARN))
                .containsOnly("Property 'sonar.qualitygate' is not supported any more. It will be ignored.");
    }

    private Map<String, String> loadPropsFromFile(String filePath) throws IOException {
        Properties props = new Properties();
        try (FileInputStream fileInputStream = new FileInputStream(getResource(this.getClass(), filePath))) {
            props.load(fileInputStream);
        }
        Map<String, String> result = new HashMap<>();
        for (Map.Entry<Object, Object> entry : props.entrySet()) {
            result.put(entry.getKey().toString(), entry.getValue().toString());
        }
        return result;
    }

    /**
     * Search for a test resource in the classpath. For example getResource("org/sonar/MyClass/foo.txt");
     *
     * @param path the starting slash is optional
     * @return the resource. Null if resource not found
     */
    public static File getResource(String path) {
        String resourcePath = path;
        if (!resourcePath.startsWith("/")) {
            resourcePath = "/" + resourcePath;
        }
        URL url = ProjectReactorBuilderTest.class.getResource(resourcePath);
        if (url != null) {
            return FileUtils.toFile(url);
        }
        return null;
    }

    /**
     * Search for a resource in the classpath. For example calling the method getResource(getClass(), "myTestName/foo.txt") from
     * the class org.sonar.Foo loads the file $basedir/src/test/resources/org/sonar/Foo/myTestName/foo.txt
     *
     * @return the resource. Null if resource not found
     */
    public static File getResource(Class baseClass, String path) {
        String resourcePath = StringUtils.replaceChars(baseClass.getCanonicalName(), '.', '/');
        if (!path.startsWith("/")) {
            resourcePath += "/";
        }
        resourcePath += path;
        return getResource(resourcePath);
    }

}