aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabrice Bellingard <bellingard@gmail.com>2012-09-05 09:04:33 +0000
committerFabrice Bellingard <bellingard@gmail.com>2012-09-05 09:04:33 +0000
commitc7bcceb0933561fbf90ccc39225e24b47822c5d4 (patch)
tree1205b78293e9955a2bd2272943caaf5372dcb769
parentb76a02be6822c6757463057b17f051b76a150e45 (diff)
downloadsonar-scanner-cli-c7bcceb0933561fbf90ccc39225e24b47822c5d4.tar.gz
sonar-scanner-cli-c7bcceb0933561fbf90ccc39225e24b47822c5d4.zip
SONARPLUGINS-2202 Add multi-module support
-rw-r--r--src/main/java/org/sonar/runner/Launcher.java71
-rw-r--r--src/main/java/org/sonar/runner/Runner.java4
-rw-r--r--src/main/java/org/sonar/runner/model/SonarProjectBuilder.java274
-rw-r--r--src/test/java/org/sonar/runner/LauncherTest.java49
-rw-r--r--src/test/java/org/sonar/runner/model/SonarProjectBuilderTest.java316
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/any-folder/any-file.properties2
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/any-folder/sources/Fake.java1
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/sonar-project.properties14
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-path/modules/module1/sources/Fake.java1
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-path/sonar-project.properties14
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-unexisting-basedir/sonar-project.properties13
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-unexisting-file/sonar-project.properties14
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/module1/sources/Fake.java1
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/module2/src/Fake.java1
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/sonar-project.properties20
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldFilterFiles/exclude.txt (renamed from src/test/resources/org/sonar/runner/LauncherTest/shouldFilterFiles/exclude.txt)0
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldFilterFiles/include.txt (renamed from src/test/resources/org/sonar/runner/LauncherTest/shouldFilterFiles/include.txt)0
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldGetFile/foo.properties4
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldGetList/foo.properties4
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/libs/lib1.txt1
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/libs/lib2.txt1
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/sonar-project.properties9
-rw-r--r--src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/sources/Fake.java1
23 files changed, 697 insertions, 118 deletions
diff --git a/src/main/java/org/sonar/runner/Launcher.java b/src/main/java/org/sonar/runner/Launcher.java
index 797ba39..614662d 100644
--- a/src/main/java/org/sonar/runner/Launcher.java
+++ b/src/main/java/org/sonar/runner/Launcher.java
@@ -23,29 +23,21 @@ package org.sonar.runner;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
-import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.EnvironmentConfiguration;
import org.apache.commons.configuration.MapConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.filefilter.AndFileFilter;
-import org.apache.commons.io.filefilter.FileFileFilter;
-import org.apache.commons.io.filefilter.WildcardFileFilter;
-import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.Batch;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonar.runner.model.SonarProjectBuilder;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
import java.io.InputStream;
-import java.util.Properties;
/**
* Contrary to {@link org.sonar.runner.Runner}, this class is executed within the classloader
@@ -67,32 +59,12 @@ public class Launcher {
* This method invoked from {@link Main}. Do not rename it.
*/
public void execute() {
- ProjectDefinition project = defineProject();
+ ProjectDefinition project = SonarProjectBuilder.create(runner.getProjectDir(), runner.getProperties()).generateProjectDefinition();
Configuration initialConfiguration = getInitialConfiguration(project);
initLogging(initialConfiguration);
executeBatch(project, initialConfiguration);
}
- @VisibleForTesting
- protected ProjectDefinition defineProject() {
- File baseDir = runner.getProjectDir();
- Properties properties = runner.getProperties();
- ProjectDefinition definition = ProjectDefinition.create(properties)
- .setBaseDir(baseDir)
- .setWorkDir(runner.getWorkDir())
- .addSourceDirs(getList(properties, "sources"))
- .addTestDirs(getList(properties, "tests"));
- for (String dir : getList(properties, "binaries")) {
- definition.addBinaryDir(dir);
- }
- for (String pattern : getList(properties, "libraries")) {
- for (File file : getLibraries(baseDir, pattern)) {
- definition.addLibrary(file.getAbsolutePath());
- }
- }
- return definition;
- }
-
private void executeBatch(ProjectDefinition project, Configuration initialConfiguration) {
ProjectReactor reactor = new ProjectReactor(project);
Batch batch = Batch.create(reactor, initialConfiguration, new EnvironmentInformation("Runner", runner.getRunnerVersion()));
@@ -129,45 +101,6 @@ public class Launcher {
return showSql ? "DEBUG" : "WARN";
}
- /**
- * Returns files matching specified pattern.
- * Visibility has been relaxed to make code testable.
- */
- static File[] getLibraries(File baseDir, String pattern) {
- final int i = Math.max(pattern.lastIndexOf('/'), pattern.lastIndexOf('\\'));
- final String dirPath, filePattern;
- if (i == -1) {
- dirPath = ".";
- filePattern = pattern;
- } else {
- dirPath = pattern.substring(0, i);
- filePattern = pattern.substring(i + 1);
- }
- FileFilter fileFilter = new AndFileFilter(FileFileFilter.FILE, new WildcardFileFilter(filePattern));
- File dir = resolvePath(baseDir, dirPath);
- File[] files = dir.listFiles(fileFilter);
- if (files == null || files.length == 0) {
- throw new RunnerException("No files matching pattern \"" + filePattern + "\" in directory \"" + dir + "\"");
- }
- return files;
- }
-
- private static File resolvePath(File baseDir, String path) {
- File file = new File(path);
- if (!file.isAbsolute()) {
- try {
- file = new File(baseDir, path).getCanonicalFile();
- } catch (IOException e) {
- throw new RunnerException("Unable to resolve path \"" + path + "\"", e);
- }
- }
- return file;
- }
-
- private String[] getList(Properties properties, String key) {
- return StringUtils.split(properties.getProperty(key, ""), ',');
- }
-
private Configuration getInitialConfiguration(ProjectDefinition project) {
CompositeConfiguration configuration = new CompositeConfiguration();
configuration.addConfiguration(new SystemConfiguration());
diff --git a/src/main/java/org/sonar/runner/Runner.java b/src/main/java/org/sonar/runner/Runner.java
index 60784df..4224689 100644
--- a/src/main/java/org/sonar/runner/Runner.java
+++ b/src/main/java/org/sonar/runner/Runner.java
@@ -201,6 +201,10 @@ public final class Runner {
try {
Thread.currentThread().setContextClassLoader(sonarClassLoader);
Class<?> launcherClass = sonarClassLoader.findClass("org.sonar.runner.Launcher");
+ // TODO: hack to instantiate SonarProjectBuilder in this classloader otherwise it will be found in the parent one, where no deps are
+ // available
+ sonarClassLoader.findClass("org.sonar.runner.model.SonarProjectBuilder");
+ // END of hack
Constructor<?> constructor = launcherClass.getConstructor(Runner.class);
Object launcher = constructor.newInstance(this);
Method method = launcherClass.getMethod("execute");
diff --git a/src/main/java/org/sonar/runner/model/SonarProjectBuilder.java b/src/main/java/org/sonar/runner/model/SonarProjectBuilder.java
new file mode 100644
index 0000000..1fd4e70
--- /dev/null
+++ b/src/main/java/org/sonar/runner/model/SonarProjectBuilder.java
@@ -0,0 +1,274 @@
+/*
+ * Sonar Standalone Runner
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * 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 02
+ */
+
+package org.sonar.runner.model;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.filefilter.AndFileFilter;
+import org.apache.commons.io.filefilter.FileFileFilter;
+import org.apache.commons.io.filefilter.WildcardFileFilter;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.runner.RunnerException;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Class that creates a Sonar project definition based on a set of properties.
+ */
+public class SonarProjectBuilder {
+
+ private static final String PROPERTY_SONAR_MODULES = "sonar.modules";
+ private static final String PROPERTY_MODULE_FILE = "file";
+ private static final String PROPERTY_MODULE_PATH = "path";
+ /**
+ * @since 1.4
+ */
+ private static final String PROPERTY_WORK_DIRECTORY = "sonar.working.directory";
+ private static final String DEF_VALUE_WORK_DIRECTORY = ".sonar";
+
+ /**
+ * Array of all mandatory properties required for a root project.
+ */
+ private static final String[] MANDATORY_PROPERTIES_FOR_ROOT = {"sonar.projectKey", "sonar.projectName", "sonar.projectVersion", "sources"};
+
+ /**
+ * Array of all mandatory properties required for a child project.
+ */
+ private static final String[] MANDATORY_PROPERTIES_FOR_CHILD = {"sonar.projectKey", "sonar.projectName"};
+
+ /**
+ * Properties that must not be passed from the parent project to its children.
+ */
+ private static final List<String> NON_HERITED_PROPERTIES_FOR_CHILD = Lists.newArrayList("sonar.modules", "sonar.projectDescription");
+
+ private File rootBaseDir;
+ private Properties properties;
+
+ private SonarProjectBuilder(File baseDir, Properties properties) {
+ this.rootBaseDir = baseDir;
+ this.properties = properties;
+ }
+
+ public static SonarProjectBuilder create(File baseDir, Properties properties) {
+ SonarProjectBuilder builder = new SonarProjectBuilder(baseDir, properties);
+ return builder;
+ }
+
+ public ProjectDefinition generateProjectDefinition() {
+ checkMandatoryProperties("root project", properties, MANDATORY_PROPERTIES_FOR_ROOT);
+ ProjectDefinition rootProject = defineProject(rootBaseDir, properties);
+ defineChildren(rootProject, properties);
+ return rootProject;
+ }
+
+ private void defineChildren(ProjectDefinition rootProject, Properties properties) {
+ if (properties.containsKey(PROPERTY_SONAR_MODULES)) {
+ for (String module : getListFromProperty(properties, PROPERTY_SONAR_MODULES)) {
+ Properties moduleProps = extractModuleProperties(module, properties);
+ ProjectDefinition childProject = null;
+ if (moduleProps.containsKey(PROPERTY_MODULE_FILE)) {
+ childProject = loadChildProjectFromPropertyFile(rootProject, moduleProps, module);
+ } else {
+ childProject = loadChildProjectFromProperties(rootProject, moduleProps, module);
+ }
+ // the child project may have children as well
+ defineChildren(childProject, moduleProps);
+ // and finally add this child project to its parent
+ rootProject.addSubProject(childProject);
+ }
+ }
+ }
+
+ private ProjectDefinition loadChildProjectFromProperties(ProjectDefinition rootProject, Properties childProps, String moduleId) {
+ checkMandatoryProperties(moduleId, childProps, MANDATORY_PROPERTIES_FOR_CHILD);
+ mergeParentProperties(childProps, rootProject.getProperties());
+ File baseDir = null;
+ if (childProps.containsKey(PROPERTY_MODULE_PATH)) {
+ baseDir = getFileFromProperty(childProps, PROPERTY_MODULE_PATH, rootProject.getBaseDir());
+ } else {
+ baseDir = new File(rootProject.getBaseDir(), moduleId);
+ }
+ if (!baseDir.isDirectory()) {
+ throw new RunnerException("The base directory of the module '" + moduleId + "' does not exist: " + baseDir.getAbsolutePath());
+ }
+
+ return defineProject(baseDir, childProps);
+ }
+
+ private ProjectDefinition loadChildProjectFromPropertyFile(ProjectDefinition rootProject, Properties moduleProps, String moduleId) {
+ File propertyFile = getFileFromProperty(moduleProps, PROPERTY_MODULE_FILE, rootProject.getBaseDir());
+ if (!propertyFile.isFile()) {
+ throw new RunnerException("The properties file of the module '" + moduleId + "' does not exist: " + propertyFile.getAbsolutePath());
+ }
+ Properties childProps = new Properties();
+ FileInputStream fileInputStream = null;
+ try {
+ fileInputStream = new FileInputStream(propertyFile);
+ childProps.load(fileInputStream);
+ } catch (IOException e) {
+ throw new RunnerException("Impossible to read the property file: " + propertyFile.getAbsolutePath(), e);
+ } finally {
+ IOUtils.closeQuietly(fileInputStream);
+ }
+ checkMandatoryProperties(moduleId, childProps, MANDATORY_PROPERTIES_FOR_CHILD);
+ mergeParentProperties(childProps, rootProject.getProperties());
+
+ return defineProject(propertyFile.getParentFile(), childProps);
+ }
+
+ @VisibleForTesting
+ protected static void checkMandatoryProperties(String moduleId, Properties props, String[] mandatoryProps) {
+ StringBuilder missing = new StringBuilder();
+ for (String mandatoryProperty : mandatoryProps) {
+ if (!props.containsKey(mandatoryProperty)) {
+ if (missing.length() > 0) {
+ missing.append(", ");
+ }
+ missing.append(mandatoryProperty);
+ }
+ }
+ if (missing.length() != 0) {
+ throw new RunnerException("You must define the following mandatory properties for '" + moduleId + "': " + missing);
+ }
+ }
+
+ @VisibleForTesting
+ protected static void mergeParentProperties(Properties childProps, Properties parentProps) {
+ for (Map.Entry<Object, Object> entry : parentProps.entrySet()) {
+ String key = (String) entry.getKey();
+ if (!childProps.containsKey(key) && !NON_HERITED_PROPERTIES_FOR_CHILD.contains(key)) {
+ childProps.put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ @VisibleForTesting
+ protected static Properties extractModuleProperties(String module, Properties properties) {
+ Properties moduleProps = new Properties();
+ String propertyPrefix = module + ".";
+ int prefixLength = propertyPrefix.length();
+ for (Map.Entry<Object, Object> entry : properties.entrySet()) {
+ String key = (String) entry.getKey();
+ if (key.startsWith(propertyPrefix)) {
+ moduleProps.put(key.substring(prefixLength), entry.getValue());
+ }
+ }
+ return moduleProps;
+ }
+
+ private ProjectDefinition defineProject(File baseDir, Properties properties) {
+ ProjectDefinition definition = ProjectDefinition.create((Properties) properties.clone())
+ .setBaseDir(baseDir)
+ .setWorkDir(initWorkDir(baseDir));
+ if (!properties.containsKey(PROPERTY_SONAR_MODULES)) {
+ // this is not a "aggregator" project, so let's specify its sources, tests, ...
+ definition.addSourceDirs(getListFromProperty(properties, "sources"));
+ definition.addTestDirs(getListFromProperty(properties, "tests"));
+ for (String dir : getListFromProperty(properties, "binaries")) {
+ definition.addBinaryDir(dir);
+ }
+ for (String pattern : getListFromProperty(properties, "libraries")) {
+ for (File file : getLibraries(baseDir, pattern)) {
+ definition.addLibrary(file.getAbsolutePath());
+ }
+ }
+ }
+ return definition;
+ }
+
+ @VisibleForTesting
+ protected File initWorkDir(File baseDir) {
+ String workDir = properties.getProperty(PROPERTY_WORK_DIRECTORY);
+ if (StringUtils.isBlank(workDir)) {
+ return new File(baseDir, DEF_VALUE_WORK_DIRECTORY);
+ }
+
+ File customWorkDir = new File(workDir);
+ if (customWorkDir.isAbsolute()) {
+ return customWorkDir;
+ }
+ return new File(baseDir, customWorkDir.getPath());
+ }
+
+ /**
+ * Returns files matching specified pattern.
+ */
+ @VisibleForTesting
+ protected static File[] getLibraries(File baseDir, String pattern) {
+ final int i = Math.max(pattern.lastIndexOf('/'), pattern.lastIndexOf('\\'));
+ final String dirPath, filePattern;
+ if (i == -1) {
+ dirPath = ".";
+ filePattern = pattern;
+ } else {
+ dirPath = pattern.substring(0, i);
+ filePattern = pattern.substring(i + 1);
+ }
+ FileFilter fileFilter = new AndFileFilter(FileFileFilter.FILE, new WildcardFileFilter(filePattern));
+ File dir = resolvePath(baseDir, dirPath);
+ File[] files = dir.listFiles(fileFilter);
+ if (files == null || files.length == 0) {
+ throw new RunnerException("No files matching pattern \"" + filePattern + "\" in directory \"" + dir + "\"");
+ }
+ return files;
+ }
+
+ private static File resolvePath(File baseDir, String path) {
+ File file = new File(path);
+ if (!file.isAbsolute()) {
+ try {
+ file = new File(baseDir, path).getCanonicalFile();
+ } catch (IOException e) {
+ throw new RunnerException("Unable to resolve path \"" + path + "\"", e);
+ }
+ }
+ return file;
+ }
+
+ /**
+ * Returns a list of comma-separated values, even if they are separated by whitespace characters (space char, EOL, ...)
+ */
+ @VisibleForTesting
+ protected static String[] getListFromProperty(Properties properties, String key) {
+ return StringUtils.stripAll(StringUtils.split(properties.getProperty(key, ""), ','));
+ }
+
+ /**
+ * Returns the file denoted by the given property, may it be relative to "baseDir" or absolute.
+ */
+ @VisibleForTesting
+ protected static File getFileFromProperty(Properties properties, String key, File baseDir) {
+ File propertyFile = new File(properties.getProperty(key).trim());
+ if (!propertyFile.isAbsolute()) {
+ propertyFile = new File(baseDir, propertyFile.getPath());
+ }
+ return propertyFile;
+ }
+
+}
diff --git a/src/test/java/org/sonar/runner/LauncherTest.java b/src/test/java/org/sonar/runner/LauncherTest.java
index 515daa1..0cec5f5 100644
--- a/src/test/java/org/sonar/runner/LauncherTest.java
+++ b/src/test/java/org/sonar/runner/LauncherTest.java
@@ -22,61 +22,12 @@ package org.sonar.runner;
import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.junit.Test;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-
-import java.io.File;
-import java.util.Properties;
import static org.fest.assertions.Assertions.assertThat;
-import static org.junit.Assert.fail;
public class LauncherTest {
@Test
- public void shouldFilterFiles() throws Exception {
- File baseDir = new File(getClass().getResource("/org/sonar/runner/LauncherTest/shouldFilterFiles/").toURI());
- assertThat(Launcher.getLibraries(baseDir, "in*.txt").length).isEqualTo(1);
- assertThat(Launcher.getLibraries(baseDir, "*.txt").length).isEqualTo(2);
- assertThat(Launcher.getLibraries(baseDir.getParentFile(), "shouldFilterFiles/in*.txt").length).isEqualTo(1);
- assertThat(Launcher.getLibraries(baseDir.getParentFile(), "shouldFilterFiles/*.txt").length).isEqualTo(2);
- }
-
- @Test
- public void shouldWorkWithAbsolutePath() throws Exception {
- File baseDir = new File("not-exists");
- String absolutePattern = new File(getClass().getResource("/org/sonar/runner/LauncherTest/shouldFilterFiles/").toURI()).getAbsolutePath() + "/in*.txt";
- assertThat(Launcher.getLibraries(baseDir.getParentFile(), absolutePattern).length).isEqualTo(1);
- }
-
- @Test
- public void shouldThrowExceptionWhenNoFilesMatchingPattern() throws Exception {
- File baseDir = new File(getClass().getResource("/org/sonar/runner/LauncherTest/shouldFilterFiles/").toURI());
- try {
- Launcher.getLibraries(baseDir, "*.jar");
- fail("Exception expected");
- } catch (RunnerException e) {
- assertThat(e.getMessage()).contains("No files matching pattern \"*.jar\" in directory");
- }
- }
-
- @Test
- public void shouldDefineProject() {
- Properties conf = new Properties();
- conf.setProperty("sources", "src/main/java");
- conf.setProperty("tests", "src/test/java");
- conf.setProperty("binaries", "target/classes");
- conf.setProperty("libraries", "./*.xml");
- Runner runner = Runner.create(conf);
-
- Launcher launcher = new Launcher(runner);
- ProjectDefinition projectDefinition = launcher.defineProject();
- assertThat(projectDefinition.getSourceDirs()).contains("src/main/java");
- assertThat(projectDefinition.getTestDirs()).contains("src/test/java");
- assertThat(projectDefinition.getBinaries()).contains("target/classes");
- assertThat(projectDefinition.getLibraries()).contains(new File("assembly.xml").getAbsolutePath(), new File("pom.xml").getAbsolutePath());
- }
-
- @Test
public void testGetSqlLevel() throws Exception {
Configuration conf = new BaseConfiguration();
diff --git a/src/test/java/org/sonar/runner/model/SonarProjectBuilderTest.java b/src/test/java/org/sonar/runner/model/SonarProjectBuilderTest.java
new file mode 100644
index 0000000..931e535
--- /dev/null
+++ b/src/test/java/org/sonar/runner/model/SonarProjectBuilderTest.java
@@ -0,0 +1,316 @@
+/*
+ * Sonar Standalone Runner
+ * Copyright (C) 2011 SonarSource
+ * dev@sonar.codehaus.org
+ *
+ * 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 02
+ */
+package org.sonar.runner.model;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.runner.RunnerException;
+import org.sonar.test.TestUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+import java.util.Properties;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class SonarProjectBuilderTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void shouldDefineSimpleProject() throws IOException {
+ 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");
+ assertThat(projectDefinition.getTestDirs()).contains("tests");
+ assertThat(projectDefinition.getBinaries()).contains("target/classes");
+ assertThat(projectDefinition.getLibraries()).contains(TestUtils.getResource(this.getClass(), "simple-project/libs/lib2.txt").getAbsolutePath(),
+ TestUtils.getResource(this.getClass(), "simple-project/libs/lib2.txt").getAbsolutePath());
+ }
+
+ @Test
+ public void shouldDefineMultiModuleProject() throws IOException {
+ ProjectDefinition rootProject = loadProjectDefinition("multi-module");
+
+ // 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();
+ assertThat(rootProject.getBinaries().contains("target/classes")).isFalse();
+
+ // CHECK MODULES
+ List<ProjectDefinition> modules = rootProject.getSubProjects();
+ assertThat(modules.size()).isEqualTo(2);
+
+ // Module 1
+ ProjectDefinition module1 = modules.get(0);
+ assertThat(module1.getKey()).isEqualTo("com.foo.project.module1");
+ assertThat(module1.getName()).isEqualTo("Foo Module 1");
+ 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");
+
+ // Module 2
+ ProjectDefinition module2 = modules.get(1);
+ assertThat(module2.getKey()).isEqualTo("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");
+ }
+
+ @Test
+ public void shouldDefineMultiModuleProjectWithPath() throws IOException {
+ ProjectDefinition rootProject = loadProjectDefinition("multi-module-with-path");
+ List<ProjectDefinition> modules = rootProject.getSubProjects();
+ assertThat(modules.size()).isEqualTo(1);
+ assertThat(modules.get(0).getKey()).isEqualTo("com.foo.project.module1");
+ }
+
+ @Test
+ public void shouldDefineMultiModuleProjectWithFile() throws IOException {
+ ProjectDefinition rootProject = loadProjectDefinition("multi-module-with-file");
+ List<ProjectDefinition> modules = rootProject.getSubProjects();
+ assertThat(modules.size()).isEqualTo(1);
+ assertThat(modules.get(0).getKey()).isEqualTo("com.foo.project.module1");
+ }
+
+ @Test
+ public void shouldFailIfUnexistingModuleBaseDir() throws IOException {
+ thrown.expect(RunnerException.class);
+ thrown.expectMessage("The base directory of the module 'module1' does not exist: "
+ + TestUtils.getResource(this.getClass(), "multi-module-with-unexisting-basedir").getAbsolutePath() + File.separator + "module1");
+
+ loadProjectDefinition("multi-module-with-unexisting-basedir");
+ }
+
+ @Test
+ public void shouldFailIfUnexistingModuleFile() throws IOException {
+ thrown.expect(RunnerException.class);
+ thrown.expectMessage("The properties file of the module 'module1' does not exist: "
+ + TestUtils.getResource(this.getClass(), "multi-module-with-unexisting-file").getAbsolutePath() + File.separator + "any-folder"
+ + File.separator + "any-file.properties");
+
+ loadProjectDefinition("multi-module-with-unexisting-file");
+ }
+
+ @Test
+ public void shouldExtractModuleProperties() {
+ Properties props = new Properties();
+ props.setProperty("sources", "src/main/java");
+ props.setProperty("tests", "src/test/java");
+ props.setProperty("foo.sources", "src/main/java");
+ props.setProperty("foobar.tests", "src/test/java");
+ props.setProperty("foobar.binaries", "target/classes");
+
+ Properties moduleProps = SonarProjectBuilder.extractModuleProperties("bar", props);
+ assertThat(moduleProps.size()).isEqualTo(0);
+
+ moduleProps = SonarProjectBuilder.extractModuleProperties("foo", props);
+ assertThat(moduleProps.size()).isEqualTo(1);
+ assertThat(moduleProps.get("sources")).isEqualTo("src/main/java");
+
+ moduleProps = SonarProjectBuilder.extractModuleProperties("foobar", props);
+ assertThat(moduleProps.size()).isEqualTo(2);
+ assertThat(moduleProps.get("tests")).isEqualTo("src/test/java");
+ assertThat(moduleProps.get("binaries")).isEqualTo("target/classes");
+ }
+
+ @Test
+ public void shouldFailIfMandatoryPropertiesAreNotPresent() {
+ Properties props = new Properties();
+ props.setProperty("sources", "src/main/java");
+ props.setProperty("tests", "src/test/java");
+
+ thrown.expect(RunnerException.class);
+ thrown.expectMessage("You must define the following mandatory properties for 'foo': sonar.projectKey, sonar.projectName");
+
+ SonarProjectBuilder.checkMandatoryProperties("foo", props, new String[] {"sonar.projectKey", "sonar.projectName", "sources"});
+ }
+
+ @Test
+ public void shouldNotFailIfMandatoryPropertiesArePresent() {
+ Properties props = new Properties();
+ props.setProperty("sources", "src/main/java");
+ props.setProperty("tests", "src/test/java");
+
+ SonarProjectBuilder.checkMandatoryProperties("foo", props, new String[] {"sources"});
+
+ // No exception should be thrown
+ }
+
+ @Test
+ public void shouldFilterFiles() throws Exception {
+ File baseDir = TestUtils.getResource(this.getClass(), "shouldFilterFiles");
+ assertThat(SonarProjectBuilder.getLibraries(baseDir, "in*.txt").length).isEqualTo(1);
+ assertThat(SonarProjectBuilder.getLibraries(baseDir, "*.txt").length).isEqualTo(2);
+ assertThat(SonarProjectBuilder.getLibraries(baseDir.getParentFile(), "shouldFilterFiles/in*.txt").length).isEqualTo(1);
+ assertThat(SonarProjectBuilder.getLibraries(baseDir.getParentFile(), "shouldFilterFiles/*.txt").length).isEqualTo(2);
+ }
+
+ @Test
+ public void shouldWorkWithAbsolutePath() throws Exception {
+ File baseDir = new File("not-exists");
+ String absolutePattern = TestUtils.getResource(this.getClass(), "shouldFilterFiles").getAbsolutePath() + "/in*.txt";
+ assertThat(SonarProjectBuilder.getLibraries(baseDir.getParentFile(), absolutePattern).length).isEqualTo(1);
+ }
+
+ @Test
+ public void shouldThrowExceptionWhenNoFilesMatchingPattern() throws Exception {
+ File baseDir = TestUtils.getResource(this.getClass(), "shouldFilterFiles");
+
+ thrown.expect(RunnerException.class);
+ thrown.expectMessage("No files matching pattern \"*.jar\" in directory");
+
+ SonarProjectBuilder.getLibraries(baseDir, "*.jar");
+ }
+
+ @Test
+ public void shouldGetList() {
+ Properties props = new Properties();
+
+ props.put("prop", " foo , bar , \n\ntoto,tutu");
+ assertThat(SonarProjectBuilder.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu");
+ }
+
+ @Test
+ public void shouldGetListFromFile() throws IOException {
+ String filePath = "shouldGetList/foo.properties";
+ Properties props = loadPropsFromFile(filePath);
+
+ assertThat(SonarProjectBuilder.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu");
+ }
+
+ @Test
+ public void shouldGetRelativeFile() {
+ Properties props = new Properties();
+ props.put("path", "shouldGetFile/foo.properties");
+
+ assertThat(SonarProjectBuilder.getFileFromProperty(props, "path", TestUtils.getResource(this.getClass(), "/")))
+ .isEqualTo(TestUtils.getResource("org/sonar/runner/model/SonarProjectBuilderTest/shouldGetFile/foo.properties"));
+ }
+
+ @Test
+ public void shouldGetAbsoluteFile() {
+ File file = TestUtils.getResource("org/sonar/runner/model/SonarProjectBuilderTest/shouldGetFile/foo.properties");
+
+ Properties props = new Properties();
+ props.put("path", file.getAbsolutePath());
+
+ assertThat(SonarProjectBuilder.getFileFromProperty(props, "path", TestUtils.getResource(this.getClass(), "/")))
+ .isEqualTo(file);
+ }
+
+ @Test
+ public void shouldMergeParentProperties() {
+ Properties parentProps = new Properties();
+ parentProps.setProperty("toBeMergeProps", "fooParent");
+ parentProps.setProperty("existingChildProp", "barParent");
+ parentProps.setProperty("sonar.modules", "mod1,mod2");
+ parentProps.setProperty("sonar.projectDescription", "Desc fomr Parent");
+
+ Properties childProps = new Properties();
+ childProps.setProperty("existingChildProp", "barChild");
+ childProps.setProperty("otherProp", "tutuChild");
+
+ SonarProjectBuilder.mergeParentProperties(childProps, parentProps);
+
+ assertThat(childProps.size()).isEqualTo(3);
+ assertThat(childProps.getProperty("toBeMergeProps")).isEqualTo("fooParent");
+ assertThat(childProps.getProperty("existingChildProp")).isEqualTo("barChild");
+ assertThat(childProps.getProperty("otherProp")).isEqualTo("tutuChild");
+ assertThat(childProps.getProperty("sonar.modules")).isNull();
+ assertThat(childProps.getProperty("sonar.projectDescription")).isNull();
+ }
+
+ private ProjectDefinition loadProjectDefinition(String projectFolder) throws FileNotFoundException, IOException {
+ Properties props = loadPropsFromFile(projectFolder + "/sonar-project.properties");
+ ProjectDefinition projectDefinition = SonarProjectBuilder.create(TestUtils.getResource(this.getClass(), projectFolder), props)
+ .generateProjectDefinition();
+ return projectDefinition;
+ }
+
+ private Properties loadPropsFromFile(String filePath) throws FileNotFoundException, IOException {
+ Properties props = new Properties();
+ FileInputStream fileInputStream = null;
+ try {
+ fileInputStream = new FileInputStream(TestUtils.getResource(this.getClass(), filePath));
+ props.load(fileInputStream);
+ } finally {
+ IOUtils.closeQuietly(fileInputStream);
+ }
+ return props;
+ }
+
+ @Test
+ public void shouldInitWorkDir() {
+ SonarProjectBuilder builder = SonarProjectBuilder.create(null, new Properties());
+ File baseDir = new File("target/tmp/baseDir");
+
+ File workDir = builder.initWorkDir(baseDir);
+
+ assertThat(workDir).isEqualTo(new File(baseDir, ".sonar"));
+ }
+
+ @Test
+ public void shouldInitWorkDirWithCustomRelativeFolder() {
+ Properties properties = new Properties();
+ properties.put("sonar.working.directory", ".foo");
+ SonarProjectBuilder builder = SonarProjectBuilder.create(null, properties);
+ File baseDir = new File("target/tmp/baseDir");
+
+ File workDir = builder.initWorkDir(baseDir);
+
+ assertThat(workDir).isEqualTo(new File(baseDir, ".foo"));
+ }
+
+ @Test
+ public void shouldInitWorkDirWithCustomAbsoluteFolder() {
+ Properties properties = new Properties();
+ properties.put("sonar.working.directory", new File("src").getAbsolutePath());
+ SonarProjectBuilder builder = SonarProjectBuilder.create(null, properties);
+ File baseDir = new File("target/tmp/baseDir");
+
+ File workDir = builder.initWorkDir(baseDir);
+
+ assertThat(workDir).isEqualTo(new File("src").getAbsoluteFile());
+ }
+
+}
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/any-folder/any-file.properties b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/any-folder/any-file.properties
new file mode 100644
index 0000000..460d349
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/any-folder/any-file.properties
@@ -0,0 +1,2 @@
+sonar.projectKey=com.foo.project.module1
+sonar.projectName=Foo Module 1
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/any-folder/sources/Fake.java b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/any-folder/sources/Fake.java
new file mode 100644
index 0000000..5967658
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/any-folder/sources/Fake.java
@@ -0,0 +1 @@
+Fake \ No newline at end of file
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/sonar-project.properties b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/sonar-project.properties
new file mode 100644
index 0000000..4c655a7
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-file/sonar-project.properties
@@ -0,0 +1,14 @@
+sonar.projectKey=com.foo.project
+sonar.projectName=Foo Project
+sonar.projectVersion=1.0-SNAPSHOT
+sonar.projectDescription=Description of Foo Project
+
+sources=sources
+tests=tests
+binaries=target/classes
+
+sonar.modules=module1
+
+module1.file=any-folder/any-file.properties
+module1.sonar.projectKey=com.foo.project.module1
+module1.sonar.projectName=Foo Module 1
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-path/modules/module1/sources/Fake.java b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-path/modules/module1/sources/Fake.java
new file mode 100644
index 0000000..5967658
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-path/modules/module1/sources/Fake.java
@@ -0,0 +1 @@
+Fake \ No newline at end of file
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-path/sonar-project.properties b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-path/sonar-project.properties
new file mode 100644
index 0000000..202de2e
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-path/sonar-project.properties
@@ -0,0 +1,14 @@
+sonar.projectKey=com.foo.project
+sonar.projectName=Foo Project
+sonar.projectVersion=1.0-SNAPSHOT
+sonar.projectDescription=Description of Foo Project
+
+sources=sources
+tests=tests
+binaries=target/classes
+
+sonar.modules=module1
+
+module1.path=modules/module1
+module1.sonar.projectKey=com.foo.project.module1
+module1.sonar.projectName=Foo Module 1
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-unexisting-basedir/sonar-project.properties b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-unexisting-basedir/sonar-project.properties
new file mode 100644
index 0000000..7249f0a
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-unexisting-basedir/sonar-project.properties
@@ -0,0 +1,13 @@
+sonar.projectKey=com.foo.project
+sonar.projectName=Foo Project
+sonar.projectVersion=1.0-SNAPSHOT
+sonar.projectDescription=Description of Foo Project
+
+sources=sources
+tests=tests
+binaries=target/classes
+
+sonar.modules=module1
+
+module1.sonar.projectKey=com.foo.project.module1
+module1.sonar.projectName=Foo Module 1
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-unexisting-file/sonar-project.properties b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-unexisting-file/sonar-project.properties
new file mode 100644
index 0000000..4c655a7
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module-with-unexisting-file/sonar-project.properties
@@ -0,0 +1,14 @@
+sonar.projectKey=com.foo.project
+sonar.projectName=Foo Project
+sonar.projectVersion=1.0-SNAPSHOT
+sonar.projectDescription=Description of Foo Project
+
+sources=sources
+tests=tests
+binaries=target/classes
+
+sonar.modules=module1
+
+module1.file=any-folder/any-file.properties
+module1.sonar.projectKey=com.foo.project.module1
+module1.sonar.projectName=Foo Module 1
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/module1/sources/Fake.java b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/module1/sources/Fake.java
new file mode 100644
index 0000000..5967658
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/module1/sources/Fake.java
@@ -0,0 +1 @@
+Fake \ No newline at end of file
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/module2/src/Fake.java b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/module2/src/Fake.java
new file mode 100644
index 0000000..5967658
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/module2/src/Fake.java
@@ -0,0 +1 @@
+Fake \ No newline at end of file
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/sonar-project.properties b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/sonar-project.properties
new file mode 100644
index 0000000..96eb03d
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/multi-module/sonar-project.properties
@@ -0,0 +1,20 @@
+sonar.projectKey=com.foo.project
+sonar.projectName=Foo Project
+sonar.projectVersion=1.0-SNAPSHOT
+sonar.projectDescription=Description of Foo Project
+
+sources=sources
+tests=tests
+binaries=target/classes
+
+sonar.modules=module1,\
+ module2
+
+module1.sonar.projectKey=com.foo.project.module1
+module1.sonar.projectName=Foo Module 1
+
+module2.sonar.projectKey=com.foo.project.module2
+module2.sonar.projectName=Foo Module 2
+# redefine some properties
+module2.sonar.projectDescription=Description of Module 2
+module2.sources=src
diff --git a/src/test/resources/org/sonar/runner/LauncherTest/shouldFilterFiles/exclude.txt b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldFilterFiles/exclude.txt
index e69de29..e69de29 100644
--- a/src/test/resources/org/sonar/runner/LauncherTest/shouldFilterFiles/exclude.txt
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldFilterFiles/exclude.txt
diff --git a/src/test/resources/org/sonar/runner/LauncherTest/shouldFilterFiles/include.txt b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldFilterFiles/include.txt
index e69de29..e69de29 100644
--- a/src/test/resources/org/sonar/runner/LauncherTest/shouldFilterFiles/include.txt
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldFilterFiles/include.txt
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldGetFile/foo.properties b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldGetFile/foo.properties
new file mode 100644
index 0000000..8fbb104
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldGetFile/foo.properties
@@ -0,0 +1,4 @@
+prop= foo, bar, \
+toto,\
+\
+tutu, \ No newline at end of file
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldGetList/foo.properties b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldGetList/foo.properties
new file mode 100644
index 0000000..8fbb104
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/shouldGetList/foo.properties
@@ -0,0 +1,4 @@
+prop= foo, bar, \
+toto,\
+\
+tutu, \ No newline at end of file
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/libs/lib1.txt b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/libs/lib1.txt
new file mode 100644
index 0000000..81d4e95
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/libs/lib1.txt
@@ -0,0 +1 @@
+lib1 \ No newline at end of file
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/libs/lib2.txt b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/libs/lib2.txt
new file mode 100644
index 0000000..7dacac0
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/libs/lib2.txt
@@ -0,0 +1 @@
+lib2 \ No newline at end of file
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/sonar-project.properties b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/sonar-project.properties
new file mode 100644
index 0000000..81bdf16
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/sonar-project.properties
@@ -0,0 +1,9 @@
+sonar.projectKey=com.foo.project
+sonar.projectName=Foo Project
+sonar.projectVersion=1.0-SNAPSHOT
+sonar.projectDescription=Description of Foo Project
+
+sources=sources
+tests=tests
+binaries=target/classes
+libraries=libs/*.txt
diff --git a/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/sources/Fake.java b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/sources/Fake.java
new file mode 100644
index 0000000..5967658
--- /dev/null
+++ b/src/test/resources/org/sonar/runner/model/SonarProjectBuilderTest/simple-project/sources/Fake.java
@@ -0,0 +1 @@
+Fake \ No newline at end of file