Browse Source

SONARPLUGINS-2574 Fork API

tags/2.5-rc1
SimonBrandhof 11 years ago
parent
commit
9b44cc3c58
100 changed files with 1913 additions and 2007 deletions
  1. 46
    4
      pom.xml
  2. 71
    22
      sonar-runner-api/pom.xml
  3. 0
    264
      sonar-runner-api/src/main/java/org/sonar/runner/Bootstrapper.java
  4. 0
    324
      sonar-runner-api/src/main/java/org/sonar/runner/Main.java
  5. 0
    397
      sonar-runner-api/src/main/java/org/sonar/runner/Runner.java
  6. 0
    201
      sonar-runner-api/src/main/java/org/sonar/runner/SonarCache.java
  7. 123
    0
      sonar-runner-api/src/main/java/org/sonar/runner/api/Command.java
  8. 30
    0
      sonar-runner-api/src/main/java/org/sonar/runner/api/CommandException.java
  9. 170
    0
      sonar-runner-api/src/main/java/org/sonar/runner/api/CommandExecutor.java
  10. 73
    0
      sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java
  11. 169
    0
      sonar-runner-api/src/main/java/org/sonar/runner/api/ForkedRunner.java
  12. 40
    0
      sonar-runner-api/src/main/java/org/sonar/runner/api/Os.java
  13. 13
    18
      sonar-runner-api/src/main/java/org/sonar/runner/api/PrintStreamConsumer.java
  14. 108
    0
      sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java
  15. 12
    20
      sonar-runner-api/src/main/java/org/sonar/runner/api/RunnerVersion.java
  16. 6
    2
      sonar-runner-api/src/main/java/org/sonar/runner/api/StreamConsumer.java
  17. 41
    0
      sonar-runner-api/src/main/java/org/sonar/runner/api/Utils.java
  18. 23
    0
      sonar-runner-api/src/main/java/org/sonar/runner/api/package-info.java
  19. 1
    0
      sonar-runner-api/src/main/resources/org/sonar/runner/api/version.txt
  20. 0
    1
      sonar-runner-api/src/main/resources/org/sonar/runner/version.txt
  21. 0
    178
      sonar-runner-api/src/test/java/org/sonar/runner/BootstrapperTest.java
  22. 0
    83
      sonar-runner-api/src/test/java/org/sonar/runner/LogsTest.java
  23. 0
    131
      sonar-runner-api/src/test/java/org/sonar/runner/MainTest.java
  24. 0
    227
      sonar-runner-api/src/test/java/org/sonar/runner/RunnerTest.java
  25. 161
    0
      sonar-runner-api/src/test/java/org/sonar/runner/api/CommandExecutorTest.java
  26. 75
    0
      sonar-runner-api/src/test/java/org/sonar/runner/api/CommandTest.java
  27. 106
    0
      sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java
  28. 134
    0
      sonar-runner-api/src/test/java/org/sonar/runner/api/ForkedRunnerTest.java
  29. 47
    0
      sonar-runner-api/src/test/java/org/sonar/runner/api/OsTest.java
  30. 42
    0
      sonar-runner-api/src/test/java/org/sonar/runner/api/PrintStreamConsumerTest.java
  31. 5
    7
      sonar-runner-api/src/test/java/org/sonar/runner/api/RunnerVersionTest.java
  32. 33
    0
      sonar-runner-api/src/test/java/org/sonar/runner/api/UtilsTest.java
  33. 4
    0
      sonar-runner-api/src/test/scripts/echo.bat
  34. 6
    0
      sonar-runner-api/src/test/scripts/echo.sh
  35. 6
    0
      sonar-runner-api/src/test/scripts/forever.bat
  36. 6
    0
      sonar-runner-api/src/test/scripts/forever.sh
  37. 5
    0
      sonar-runner-api/src/test/scripts/output.bat
  38. 6
    0
      sonar-runner-api/src/test/scripts/output.sh
  39. 67
    0
      sonar-runner-batch/pom.xml
  40. 99
    0
      sonar-runner-batch/src/main/java/org/sonar/runner/batch/IsolatedLauncher.java
  41. 32
    60
      sonar-runner-batch/src/main/java/org/sonar/runner/batch/ProjectReactorBuilder.java
  42. 4
    4
      sonar-runner-batch/src/main/java/org/sonar/runner/batch/Utils.java
  43. 5
    2
      sonar-runner-batch/src/main/java/org/sonar/runner/batch/package-info.java
  44. 74
    0
      sonar-runner-batch/src/test/java/org/sonar/runner/batch/LauncherTest.java
  45. 56
    57
      sonar-runner-batch/src/test/java/org/sonar/runner/batch/ProjectReactorBuilderTest.java
  46. 14
    5
      sonar-runner-batch/src/test/java/org/sonar/runner/batch/UtilsTest.java
  47. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-language-definitions-all-in-root/sonar-project.properties
  48. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-language-definitions-all-in-root/src/main/groovy/Fake.groovy
  49. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-language-definitions-all-in-root/src/main/java/Fake.java
  50. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-all-in-root/module1/sources/Fake.java
  51. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-all-in-root/module2/src/Fake.java
  52. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-all-in-root/sonar-project.properties
  53. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module-inherited/module1/sonar-project.properties
  54. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module-inherited/module1/sources/Fake.java
  55. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module-inherited/module2/newBaseDir/src/Fake.java
  56. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module-inherited/module2/sonar-project.properties
  57. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module-inherited/sonar-project.properties
  58. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module/module1/sonar-project.properties
  59. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module/module1/sources/Fake.java
  60. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module/module2/newBaseDir/src/Fake.java
  61. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module/module2/sonar-project.properties
  62. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module/sonar-project.properties
  63. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-basedir/modules/module1/sources/Fake.java
  64. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-basedir/sonar-project.properties
  65. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile-and-overwritten-basedir/any-folder/generated/any-file.properties
  66. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile-and-overwritten-basedir/any-folder/sources/Fake.java
  67. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile-and-overwritten-basedir/sonar-project.properties
  68. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile/any-folder/any-file.properties
  69. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile/any-folder/sources/Fake.java
  70. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile/sonar-project.properties
  71. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-binary-dir/module1/src/Fake.java
  72. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-binary-dir/sonar-project.properties
  73. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-lib/module1/src/Fake.java
  74. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-lib/sonar-project.properties
  75. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-test-dir/module1/src/Fake.java
  76. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-test-dir/sonar-project.properties
  77. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-basedir/sonar-project.properties
  78. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-file/sonar-project.properties
  79. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-source-dir/module1/src/Fake.java
  80. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-source-dir/sonar-project.properties
  81. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-test-bin-lib-dir/module1/src/Fake.java
  82. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-test-bin-lib-dir/sonar-project.properties
  83. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/shouldFilterFiles/exclude.txt
  84. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/shouldFilterFiles/include.txt
  85. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/shouldGetFile/foo.properties
  86. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-deprecated-props/libs/lib1.txt
  87. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-deprecated-props/libs/lib2.txt
  88. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-deprecated-props/sonar-project.properties
  89. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-deprecated-props/sources/Fake.java
  90. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-binary/sonar-project.properties
  91. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-binary/sources/Fake.java
  92. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-lib/sonar-project.properties
  93. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-lib/sources/Fake.java
  94. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-source-dir/sonar-project.properties
  95. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-test-dir/sonar-project.properties
  96. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-test-dir/sources/Fake.java
  97. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project/libs/lib1.txt
  98. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project/libs/lib2.txt
  99. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project/sonar-project.properties
  100. 0
    0
      sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project/sources/Fake.java

+ 46
- 4
pom.xml View File

@@ -1,4 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.codehaus.sonar-plugins</groupId>
@@ -25,8 +26,9 @@

<modules>
<module>sonar-runner-api</module>
<module>sonar-runner-impl</module>
<module>sonar-runner-batch</module>
<module>sonar-runner-dist</module>
<module>sonar-runner-impl</module>
</modules>

<organization>
@@ -46,7 +48,6 @@
<connection>scm:git:git@github.com:SonarSource/sonar-runner.git</connection>
<developerConnection>scm:git:git@github.com:SonarSource/sonar-runner.git</developerConnection>
<url>https://github.com/SonarSource/sonar-runner</url>
<tag>HEAD</tag>
</scm>

<issueManagement>
@@ -55,7 +56,7 @@
</issueManagement>

<properties>
<sonar.buildVersion>3.5-RC3</sonar.buildVersion>
<sonar.buildVersion>3.5</sonar.buildVersion>
<maven.test.redirectTestOutputToFile>true</maven.test.redirectTestOutputToFile>
</properties>

@@ -94,4 +95,45 @@
</profile>
</profiles>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.7</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.7.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

+ 71
- 22
sonar-runner-api/pom.xml View File

@@ -1,4 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.codehaus.sonar.runner</groupId>
@@ -10,39 +11,38 @@
<name>Sonar Runner - API</name>

<dependencies>
<!-- Dependencies will be shaded -->
<!-- Dependencies with scope "compile" are shaded and removed from transitive dependencies-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.2</version>
<groupId>${pom.groupId}</groupId>
<artifactId>sonar-runner-impl</artifactId>
<version>${pom.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.4</version>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>

<!-- Unit tests -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
<version>2.6</version>
<scope>test</scope>
</dependency>
<!-- Unit tests -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -54,7 +54,6 @@
<filtering>true</filtering>
</resource>
</resources>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -67,6 +66,34 @@
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>process-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${pom.groupId}</groupId>
<artifactId>sonar-runner-impl</artifactId>
<version>${pom.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
<destFileName>sonar-runner-impl.jar</destFileName>
</artifactItem>
</artifactItems>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
@@ -80,14 +107,6 @@
<createDependencyReducedPom>true</createDependencyReducedPom>
<minimizeJar>true</minimizeJar>
<relocations>
<relocation>
<pattern>org.apache.commons.io</pattern>
<shadedPattern>org.sonar.runner.commonsio</shadedPattern>
</relocation>
<relocation>
<pattern>org.apache.commons.codec</pattern>
<shadedPattern>org.sonar.runner.commonscodec</shadedPattern>
</relocation>
<relocation>
<pattern>org.apache.commons.lang</pattern>
<shadedPattern>org.sonar.runner.commonslang</shadedPattern>
@@ -97,6 +116,36 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.7</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>!*</Import-Package>
<Export-Package>!.,org.sonar.runner.api</Export-Package>
<Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
</instructions>
</configuration>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>

+ 0
- 264
sonar-runner-api/src/main/java/org/sonar/runner/Bootstrapper.java View File

@@ -1,264 +0,0 @@
/*
* Sonar Runner - API
* 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;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Bootstrapper used to download everything from the server and create the correct classloader required to execute a Sonar analysis in isolation.
*/
class Bootstrapper {

static final String VERSION_PATH = "/api/server/version";
static final String BATCH_PATH = "/batch/";
static final String BOOTSTRAP_INDEX_PATH = "/batch_bootstrap/index";
static final int CONNECT_TIMEOUT_MILLISECONDS = 30000;
static final int READ_TIMEOUT_MILLISECONDS = 60000;
private static final Pattern CHARSET_PATTERN = Pattern.compile("(?i)\\bcharset=\\s*\"?([^\\s;\"]*)");

private static final String[] UNSUPPORTED_VERSIONS_FOR_CACHE = {"1", "2", "3.0", "3.1", "3.2", "3.3", "3.4"};

private File bootDir;
private String serverUrl;
private String productToken;
private String serverVersion;
private SonarCache cache;

/**
* @param productToken part of User-Agent request-header field - see http://tools.ietf.org/html/rfc1945#section-10.15
*/
Bootstrapper(String productToken, String serverUrl, File workDir, SonarCache cache) {
this.productToken = productToken;
this.cache = cache;
bootDir = new File(workDir, "batch");
bootDir.mkdirs();
if (serverUrl.endsWith("/")) {
this.serverUrl = serverUrl.substring(0, serverUrl.length() - 1);
} else {
this.serverUrl = serverUrl;
}
}

/**
* @return server url
*/
String getServerUrl() {
return serverUrl;
}

/**
* @return server version
*/
String getServerVersion() {
if (serverVersion == null) {
try {
serverVersion = remoteContent(VERSION_PATH);
} catch (ConnectException e) {
Logs.error("Sonar server '" + serverUrl + "' can not be reached");
throw new RunnerException("Fail to request server version", e);
} catch (UnknownHostException e) {
Logs.error("Sonar server '" + serverUrl + "' can not be reached");
throw new RunnerException("Fail to request server version", e);
} catch (IOException e) {
throw new RunnerException("Fail to request server version", e);
}
}
return serverVersion;
}

/**
* Download batch files from server and creates {@link BootstrapClassLoader}.
* To use this method version of Sonar should be at least 2.6.
*
* @param urls additional URLs for loading classes and resources
* @param parent parent ClassLoader
* @param unmaskedPackages only classes and resources from those packages would be available for loading from parent
*/
BootstrapClassLoader createClassLoader(URL[] urls, ClassLoader parent, String... unmaskedPackages) {
BootstrapClassLoader classLoader = new BootstrapClassLoader(parent, unmaskedPackages);
List<File> files = downloadBatchFiles();
for (URL url : urls) {
classLoader.addURL(url);
}
for (File file : files) {
try {
classLoader.addURL(file.toURI().toURL());
} catch (MalformedURLException e) {
throw new IllegalStateException("Fail to create classloader", e);
}
}
return classLoader;
}

private void remoteContentToFile(String path, File toFile) {
InputStream input = null;
FileOutputStream output = null;
String fullUrl = serverUrl + path;
if (Logs.isDebugEnabled()) {
Logs.debug("Downloading " + fullUrl + " to " + toFile.getAbsolutePath());
}
// Don't log for old versions without cache to not pollute logs
else if (!isUnsupportedVersionForCache(getServerVersion())) {
Logs.info("Downloading " + path.substring(path.lastIndexOf('/') + 1));
}
try {
HttpURLConnection connection = newHttpConnection(new URL(fullUrl));
output = new FileOutputStream(toFile, false);
input = connection.getInputStream();
IOUtils.copyLarge(input, output);
} catch (IOException e) {
IOUtils.closeQuietly(output);
FileUtils.deleteQuietly(toFile);
throw new IllegalStateException("Fail to download the file: " + fullUrl, e);
} finally {
IOUtils.closeQuietly(input);
IOUtils.closeQuietly(output);
}
}

String remoteContent(String path) throws IOException {
String fullUrl = serverUrl + path;
HttpURLConnection conn = newHttpConnection(new URL(fullUrl));
String charset = getCharsetFromContentType(conn.getContentType());
if (charset == null || "".equals(charset)) {
charset = "UTF-8";
}
Reader reader = new InputStreamReader(conn.getInputStream(), charset);
try {
int statusCode = conn.getResponseCode();
if (statusCode != HttpURLConnection.HTTP_OK) {
throw new IOException("Status returned by url : '" + fullUrl + "' is invalid : " + statusCode);
}
return IOUtils.toString(reader);
} finally {
IOUtils.closeQuietly(reader);
conn.disconnect();
}
}

/**
* By convention, the product tokens are listed in order of their significance for identifying the application.
*/
String getUserAgent() {
return "sonar-bootstrapper/" + Version.getVersion() + " " + productToken;
}

HttpURLConnection newHttpConnection(URL url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(CONNECT_TIMEOUT_MILLISECONDS);
connection.setReadTimeout(READ_TIMEOUT_MILLISECONDS);
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod("GET");
connection.setRequestProperty("User-Agent", getUserAgent());
return connection;
}

private List<File> downloadBatchFiles() {
try {
List<File> files = new ArrayList<File>();
if (isUnsupportedVersionForCache(getServerVersion())) {
getBootstrapFilesFromOldURL(files);
}
else {
getBootstrapFiles(files);
}
return files;
} catch (Exception e) {
throw new IllegalStateException("Fail to download libraries from server", e);
}
}

private void getBootstrapFilesFromOldURL(List<File> files) throws IOException {
String libs = remoteContent(BATCH_PATH);
for (String lib : libs.split(",")) {
File file = new File(bootDir, lib);
remoteContentToFile(BATCH_PATH + lib, file);
files.add(file);
}
}

private void getBootstrapFiles(List<File> files) throws IOException {
String libs = remoteContent(BOOTSTRAP_INDEX_PATH);
String[] lines = libs.split("[\r\n]+");
for (String line : lines) {
line = line.trim();
if ("".equals(line)) {
continue;
}
String[] libAndMd5 = line.split("\\|");
String libName = libAndMd5[0];
String remoteMd5 = libAndMd5.length > 0 ? libAndMd5[1] : null;
File libInCache = null;
if (remoteMd5 != null && !"".equals(remoteMd5)) {
libInCache = cache.getFileFromCache(libName, remoteMd5);
}
if (libInCache == null) {
File tmpLocation = cache.getTemporaryFile();
remoteContentToFile(BATCH_PATH + libName, tmpLocation);
String md5 = cache.cacheFile(tmpLocation, libName);
libInCache = cache.getFileFromCache(libName, md5);
if (!md5.equals(remoteMd5)) {
throw new RunnerException("INVALID CHECKSUM: File " + libInCache.getAbsolutePath() + " was expected to have checksum " + remoteMd5
+ " but was downloaded with checksum " + md5);
}
}
files.add(libInCache);
}
}

static boolean isUnsupportedVersionForCache(String version) {
return VersionUtils.isUnsupportedVersion(version, UNSUPPORTED_VERSIONS_FOR_CACHE);
}

/**
* Parse out a charset from a content type header.
*
* @param contentType e.g. "text/html; charset=EUC-JP"
* @return "EUC-JP", or null if not found. Charset is trimmed and uppercased.
*/
static String getCharsetFromContentType(String contentType) {
if (contentType == null) {
return null;
}

Matcher m = CHARSET_PATTERN.matcher(contentType);
if (m.find()) {
return m.group(1).trim().toUpperCase();
}
return null;
}
}

+ 0
- 324
sonar-runner-api/src/main/java/org/sonar/runner/Main.java View File

@@ -1,324 +0,0 @@
/*
* Sonar Runner - API
* 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;

import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.Properties;

/**
* Arguments :
* <ul>
* <li>runner.home: optional path to runner home (root directory with sub-directories bin, lib and conf)</li>
* <li>runner.settings: optional path to runner global settings, usually ${runner.home}/conf/sonar-runner.properties.
* This property is used only if ${runner.home} is not defined</li>
* <li>project.home: path to project root directory. If not set, then it's supposed to be the directory where the runner is executed</li>
* <li>project.settings: optional path to project settings. Default value is ${project.home}/sonar-project.properties.</li>
* </ul>
*
* @since 1.0
*/
public final class Main {

private static final String RUNNER_HOME = "runner.home";
private static final String RUNNER_SETTINGS = "runner.settings";
private static final String PROJECT_HOME = "project.home";
private static final String PROJECT_SETTINGS = "project.settings";
private static final String TASK_COMMAND = "sonar.task";

boolean debugMode = false;
boolean displayVersionOnly = false;
boolean displayStackTrace = false;
String command;
Properties globalProperties;
Properties projectProperties;

/**
* Entry point of the program.
*/
public static void main(String[] args) {
new Main().execute(args);
}

Main() {
}

private void execute(String[] args) {
Properties argsProperties = parseArguments(args);
System.out.println("Runner version: " + Version.getVersion());
System.out.println("Java version: " + System.getProperty("java.version", "<unknown>")
+ ", vendor: " + System.getProperty("java.vendor", "<unknown>"));
System.out
.println("OS name: \"" + System.getProperty("os.name") + "\", version: \"" + System.getProperty("os.version") + "\", arch: \"" + System.getProperty("os.arch") + "\"");
if (!displayVersionOnly) {
int result = execute(argsProperties);
System.exit(result);
}
}

private int execute(Properties argsProperties) {
if (displayStackTrace) {
Logs.info("Error stacktraces are turned on.");
}
Stats stats = new Stats().start();
try {
loadProperties(argsProperties);
Runner runner = Runner.create(command, globalProperties, projectProperties);
Logs.info("Default locale: \"" + Locale.getDefault() + "\", source code encoding: \"" + runner.getSourceCodeEncoding() + "\""
+ (runner.isEncodingPlatformDependant() ? " (analysis is platform dependent)" : ""));
Logs.debug("Other system properties:");
Logs.debug(" - sun.arch.data.model: \"" + System.getProperty("sun.arch.data.model") + "\"");
Logs.info("Server: " + runner.getSonarServerURL());
try {
Logs.info("Work directory: " + runner.getWorkDir().getCanonicalPath());
Logs.info("Cache directory: " + runner.getCache().getCacheLocation());
} catch (IOException e) {
throw new RunnerException("Unable to resolve directory", e);
}
runner.execute();
} catch (Exception e) {
displayExecutionResult(stats, "FAILURE");
showError("Error during Sonar runner execution", e, displayStackTrace);
return 1;
}
displayExecutionResult(stats, "SUCCESS");
return 0;
}

private void displayExecutionResult(Stats stats, String resultMsg) {
Logs.info("------------------------------------------------------------------------");
Logs.info("EXECUTION " + resultMsg);
Logs.info("------------------------------------------------------------------------");
stats.stop();
Logs.info("------------------------------------------------------------------------");
}

public void showError(String message, Throwable e, boolean showStackTrace) {
if (showStackTrace) {
Logs.error(message, e);
if (!debugMode) {
Logs.error("");
suggestDebugMode();
}
}
else {
Logs.error(message);
if (e != null) {
Logs.error(e.getMessage());
String previousMsg = "";
for (Throwable cause = e.getCause(); cause != null
&& cause.getMessage() != null
&& !cause.getMessage().equals(previousMsg); cause = cause.getCause()) {
Logs.error("Caused by: " + cause.getMessage());
previousMsg = cause.getMessage();
}
}
Logs.error("");
Logs.error("To see the full stack trace of the errors, re-run Sonar Runner with the -e switch.");
if (!debugMode) {
suggestDebugMode();
}
}
}

private void suggestDebugMode() {
Logs.error("Re-run Sonar Runner using the -X switch to enable full debug logging.");
}

void loadProperties(Properties argsProperties) {
globalProperties = loadGlobalProperties(argsProperties);
projectProperties = loadProjectProperties(argsProperties);
}

Properties loadGlobalProperties(Properties argsProperties) {
Properties commandLineProps = new Properties();
commandLineProps.putAll(System.getProperties());
commandLineProps.putAll(argsProperties);

Properties result = new Properties();
result.putAll(loadRunnerConfiguration(commandLineProps));
result.putAll(commandLineProps);

return result;
}

Properties loadProjectProperties(Properties argsProperties) {
Properties commandLineProps = new Properties();
commandLineProps.putAll(System.getProperties());
commandLineProps.putAll(argsProperties);

Properties result = new Properties();
result.putAll(loadProjectConfiguration(commandLineProps));
result.putAll(commandLineProps);

if (result.containsKey(PROJECT_HOME)) {
// the real property of the Sonar Runner is "sonar.projectDir"
String baseDir = result.getProperty(PROJECT_HOME);
result.remove(PROJECT_HOME);
result.put(Runner.PROPERTY_SONAR_PROJECT_BASEDIR, baseDir);
}

return result;
}

Properties loadRunnerConfiguration(Properties props) {
File settingsFile = locatePropertiesFile(props, RUNNER_HOME, "conf/sonar-runner.properties", RUNNER_SETTINGS);
if (settingsFile != null && settingsFile.isFile() && settingsFile.exists()) {
Logs.info("Runner configuration file: " + settingsFile.getAbsolutePath());
return toProperties(settingsFile);
}
Logs.info("Runner configuration file: NONE");
return new Properties();
}

private Properties loadProjectConfiguration(Properties props) {
File settingsFile = locatePropertiesFile(props, PROJECT_HOME, "sonar-project.properties", PROJECT_SETTINGS);
if (settingsFile != null && settingsFile.isFile() && settingsFile.exists()) {
Logs.info("Project configuration file: " + settingsFile.getAbsolutePath());
return toProperties(settingsFile);
}
Logs.info("Project configuration file: NONE");
return new Properties();
}

private File locatePropertiesFile(Properties props, String homeKey, String relativePathFromHome, String settingsKey) {
File settingsFile = null;
String runnerHome = props.getProperty(homeKey);
if (runnerHome != null && !"".equals(runnerHome)) {
settingsFile = new File(runnerHome, relativePathFromHome);
}

if (settingsFile == null || !settingsFile.exists()) {
String settingsPath = props.getProperty(settingsKey);
if (settingsPath != null && !"".equals(settingsPath)) {
settingsFile = new File(settingsPath);
}
}
return settingsFile;
}

private Properties toProperties(File file) {
InputStream in = null;
Properties properties = new Properties();
try {
in = new FileInputStream(file);
properties.load(in);
return properties;

} catch (Exception e) {
throw new IllegalStateException("Fail to load file: " + file.getAbsolutePath(), e);

} finally {
IOUtils.closeQuietly(in);
}
}

Properties parseArguments(String[] args) {
int i = 0;
if (args.length > 0 && !args[0].startsWith("-")) {
command = args[0];
i++;
}
else {
command = null;
}
Properties props = new Properties();
for (; i < args.length; i++) {
String arg = args[i];
if ("-h".equals(arg) || "--help".equals(arg)) {
printUsage();
}
else if ("-v".equals(arg) || "--version".equals(arg)) {
displayVersionOnly = true;
}
else if ("-e".equals(arg) || "--errors".equals(arg)) {
displayStackTrace = true;
}
else if ("-X".equals(arg) || "--debug".equals(arg)) {
props.setProperty(Runner.PROPERTY_VERBOSE, "true");
displayStackTrace = true;
debugMode = true;
Logs.setDebugEnabled(true);
}
else if ("-D".equals(arg) || "--define".equals(arg)) {
i++;
if (i >= args.length) {
printError("Missing argument for option --define");
}
arg = args[i];
appendPropertyTo(arg, props);

}
else if (arg.startsWith("-D")) {
arg = arg.substring(2);
appendPropertyTo(arg, props);

}
else {
printError("Unrecognized option: " + arg);
}
}
return props;
}

private void appendPropertyTo(String arg, Properties props) {
final String key, value;
int j = arg.indexOf('=');
if (j == -1) {
key = arg;
value = "true";
} else {
key = arg.substring(0, j);
value = arg.substring(j + 1);
}
if (TASK_COMMAND.equals(key)) {
command = value;
}
else {
props.setProperty(key, value);
}
}

private void printError(String message) {
Logs.error(message);
printUsage();
}

private void printUsage() {
Logs.info("");
Logs.info("usage: sonar-runner [command] [options]");
Logs.info("");
Logs.info("Command:");
Logs.info(" analyse-project Run Sonar analysis task on the current project (default)");
Logs.info(" list-tasks Display all tasks available");
Logs.info("Options:");
Logs.info(" -D,--define <arg> Define property");
Logs.info(" -e,--errors Produce execution error messages");
Logs.info(" -h,--help Display help information");
Logs.info(" -v,--version Display version information");
Logs.info(" -X,--debug Produce execution debug output");
System.exit(0);
}
}

+ 0
- 397
sonar-runner-api/src/main/java/org/sonar/runner/Runner.java View File

@@ -1,397 +0,0 @@
/*
* Sonar Runner - API
* 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;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
* <p>
* Sonar Runner class that can be used to launch Sonar analyses.
* </p>
* <p>
* Configuration is all done through properties:
* </p>
* <ul>
* <li>"sonar.projectDir": the base directory of the project to analyse (this can also be passed via the {@link #create(Properties, File)} constructor)</li>
* <li>"sonar.working.directory": the working directory, which is "${sonar.projectDir}/.sonar" by default.</li>
* <li>"sonar.verbose": if set to "true", more information is displayed in the log</li>
* <li>"sonar.environment.information.key" and "sonar.environment.information.version": can be used to overwrite environment information (can also be
* set via {@link #setEnvironmentInformation(String, String)} method)</li>
* <li>... plus all the other Sonar and Sonar plugins properties.</li>
* </ul>
*
* @since 1.1
*/
public final class Runner {

/**
* Old property used to activate debug level for logging.
*
* @deprecated Replaced by sonar.verbose since 1.2
*/
@Deprecated
public static final String PROPERTY_OLD_DEBUG_MODE = "runner.debug";

/**
* Property used to increase logging information.
*
* @since 1.2
*/
public static final String PROPERTY_VERBOSE = "sonar.verbose";

/**
* Property used to specify the working directory for the runner. May be a relative or absolute path.
*
* @since 1.4
*/
public static final String PROPERTY_WORK_DIRECTORY = "sonar.working.directory";

/**
* Default value of the working directory.
*/
public static final String DEF_VALUE_WORK_DIRECTORY = ".sonar";

/**
* Property used to specify the base directory of the project to analyse.
*
* @since 1.5
*/
public static final String PROPERTY_SONAR_PROJECT_BASEDIR = "sonar.projectBaseDir";

/**
* Property used to specify the name of the tool that will run a Sonar analysis.
*
* @since 1.5
*/
public static final String PROPERTY_ENVIRONMENT_INFORMATION_KEY = "sonar.environment.information.key";

/**
* Property used to specify the version of the tool that will run a Sonar analysis.
*
* @since 1.5
*/
public static final String PROPERTY_ENVIRONMENT_INFORMATION_VERSION = "sonar.environment.information.version";

/**
* Property used to define cache location (default to ~/.sonar/cache).
*
* @since 2.1
*/
public static final String ENV_SONAR_USER_HOME = "SONAR_USER_HOME";
public static final String PROPERTY_SONAR_USER_HOME = "sonar.userHome";

/**
* Array of prefixes of versions of Sonar without support of this runner.
*/
private static final String[] UNSUPPORTED_VERSIONS = {"1", "2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8", "2.9", "2.10"};
private static final String[] UNSUPPORTED_VERSIONS_FOR_TASKS = {"1", "2", "3.0", "3.1", "3.2", "3.3", "3.4"};

private static final String PROPERTY_SOURCE_ENCODING = "sonar.sourceEncoding";

private String command;
private File projectDir;
private File sonarUserHomeDir;
private File workDir;
private String[] unmaskedPackages;
private List<Object> containerExtensions = new ArrayList<Object>();
private Properties globalProperties;
private Properties projectProperties;
private boolean isEncodingPlatformDependant;
private SonarCache cache;

private Runner(String command, Properties globalProperties, Properties projectProperties) {
this.command = command;
this.globalProperties = globalProperties;
this.projectProperties = projectProperties;
this.unmaskedPackages = new String[0];
// set the default values for the Sonar Runner - they can be overriden with #setEnvironmentInformation
this.globalProperties.put(PROPERTY_ENVIRONMENT_INFORMATION_KEY, "Runner");
this.globalProperties.put(PROPERTY_ENVIRONMENT_INFORMATION_VERSION, Version.getVersion());
// sets the encoding if not forced
if (!globalProperties.containsKey(PROPERTY_SOURCE_ENCODING) && !projectProperties.containsKey(PROPERTY_SOURCE_ENCODING)) {
isEncodingPlatformDependant = true;
globalProperties.setProperty(PROPERTY_SOURCE_ENCODING, Charset.defaultCharset().name());
}
// and init the directories
initDirs();
// init the cache
// Try to get Sonar user home from property
cache = SonarCache.create(getSonarUserHomeDir()).build();
}

/**
* Creates a Runner based only on the given properties.
* @deprecated Use {@link Runner#create(String, Properties, Properties)}
*/
@Deprecated
public static Runner create(Properties props) {
return create(null, new Properties(), props);
}

/**
* Creates a Runner based only on the given properties.
*/
public static Runner create(String command, Properties globalProperties, Properties projectProperties) {
return new Runner(command, globalProperties, projectProperties);
}

/**
* Creates a Runner based only on the properties and with the given base directory.
* @deprecated Use {@link Runner#create(String, Properties, Properties, File)}
*/
@Deprecated
public static Runner create(Properties props, File basedir) {
return create(null, new Properties(), props, basedir);
}

/**
* Creates a Runner based only on the properties and with the given base directory.
*/
public static Runner create(String command, Properties globalProperties, Properties projectProperties, File basedir) {
projectProperties.put(PROPERTY_SONAR_PROJECT_BASEDIR, basedir.getAbsolutePath());
return new Runner(command, globalProperties, projectProperties);
}

/**
* Runs a Sonar analysis.
*/
public void execute() {
Bootstrapper bootstrapper = new Bootstrapper("SonarRunner/" + Version.getVersion(), getSonarServerURL(), getWorkDir(), getCache());
checkSonarVersion(bootstrapper);
delegateExecution(createClassLoader(bootstrapper));
}

public String getSonarServerURL() {
return projectProperties.getProperty("sonar.host.url", globalProperties.getProperty("sonar.host.url", "http://localhost:9000"));
}

public SonarCache getCache() {
return cache;
}

private void initDirs() {
projectDir = initProjectDir();
// project home exists: add its absolute path as "sonar.projectBaseDir" property
projectProperties.put(PROPERTY_SONAR_PROJECT_BASEDIR, projectDir.getAbsolutePath());
workDir = initWorkDir();
sonarUserHomeDir = initSonarUserHomeDir();
}

private File initSonarUserHomeDir() {
String sonarUserHome = globalProperties.getProperty(PROPERTY_SONAR_USER_HOME);
if (StringUtils.isBlank(sonarUserHome)) {
// Try to get Sonar user home from environment variable
sonarUserHome = System.getenv(ENV_SONAR_USER_HOME);
}
if (StringUtils.isBlank(sonarUserHome)) {
// Default Sonar user home
sonarUserHome = System.getProperty("user.home") + File.separator + ".sonar";
}
return new File(sonarUserHome);
}

private File initProjectDir() {
String path = projectProperties.getProperty(PROPERTY_SONAR_PROJECT_BASEDIR, ".");
File dir = new File(path);
if (!dir.isDirectory()) {
throw new RunnerException("Project home must be an existing directory: " + path);
}
return dir;
}

private File initWorkDir() {
File newWorkDir;
String customWorkDir = projectProperties.getProperty(PROPERTY_WORK_DIRECTORY, globalProperties.getProperty(PROPERTY_WORK_DIRECTORY));
if (customWorkDir == null || "".equals(customWorkDir.trim())) {
newWorkDir = new File(getProjectDir(), DEF_VALUE_WORK_DIRECTORY);
}
else {
newWorkDir = defineCustomizedWorkDir(new File(customWorkDir));
}
FileUtils.deleteQuietly(newWorkDir);
return newWorkDir;
}

private File defineCustomizedWorkDir(File customWorkDir) {
if (customWorkDir.isAbsolute()) {
return customWorkDir;
}
return new File(getProjectDir(), customWorkDir.getPath());
}

/**
* @return the project base directory
*/
public File getProjectDir() {
return projectDir;
}

/**
* @return work directory, default is ".sonar" in project directory
*/
public File getWorkDir() {
return workDir;
}

/**
* @return sonar user home directory
*/
public File getSonarUserHomeDir() {
return sonarUserHomeDir;
}

/**
* @return the source code encoding that will be used by Sonar
*/
public String getSourceCodeEncoding() {
return projectProperties.getProperty(PROPERTY_SOURCE_ENCODING, globalProperties.getProperty(PROPERTY_SOURCE_ENCODING));
}

/**
* @return true if the property "sonar.sourceEncoding" hasn't been forced
*/
public boolean isEncodingPlatformDependant() {
return isEncodingPlatformDependant;
}

public String getCommand() {
return command;
}

/**
* @return global properties, project properties and command-line properties
*/
public Properties getProperties() {
Properties props = new Properties();
props.putAll(globalProperties);
props.putAll(projectProperties);
return props;
}

protected void checkSonarVersion(Bootstrapper bootstrapper) {
String serverVersion = bootstrapper.getServerVersion();
if (isUnsupportedVersion(serverVersion)) {
throw new RunnerException("Sonar " + serverVersion
+ " is not supported. Please upgrade Sonar to version 2.11 or more.");
}
if (command != null && isUnsupportedVersionForTasks(serverVersion)) {
throw new RunnerException("Sonar " + serverVersion
+ " doesn't support tasks. Please upgrade Sonar to version 3.5 or more.");
}
}

private BootstrapClassLoader createClassLoader(Bootstrapper bootstrapper) {
URL url = getJarPath();
return bootstrapper.createClassLoader(
// Add JAR with Sonar Runner - it's a Jar which contains this class
new URL[] {url},
getClass().getClassLoader(),
unmaskedPackages);
}

/**
* For unknown reasons <code>getClass().getProtectionDomain().getCodeSource().getLocation()</code> doesn't work under Ant 1.7.0.
* So this is a workaround.
*
* @return Jar which contains this class
*/
public static URL getJarPath() {
String pathToClass = "/" + Runner.class.getName().replace('.', '/') + ".class";
URL url = Runner.class.getResource(pathToClass);
if (url != null) {
String path = url.toString();
String uri = null;
if (path.startsWith("jar:file:")) {
int bang = path.indexOf('!');
uri = path.substring(4, bang);
} else if (path.startsWith("file:")) {
int tail = path.indexOf(pathToClass);
uri = path.substring(0, tail);
}
if (uri != null) {
try {
return new URL(uri);
} catch (MalformedURLException e) {
}
}
}
return null;
}

static boolean isUnsupportedVersion(String version) {
return VersionUtils.isUnsupportedVersion(version, UNSUPPORTED_VERSIONS);
}

static boolean isUnsupportedVersionForTasks(String version) {
return VersionUtils.isUnsupportedVersion(version, UNSUPPORTED_VERSIONS_FOR_TASKS);
}

private void delegateExecution(BootstrapClassLoader sonarClassLoader) {
ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(sonarClassLoader);
Class<?> launcherClass = sonarClassLoader.findClass("org.sonar.runner.internal.batch.Launcher");
Constructor<?> constructor = launcherClass.getConstructor(String.class, Properties.class, Properties.class, List.class);
Object launcher = constructor.newInstance(getCommand(), globalProperties, projectProperties, containerExtensions);
Method method = launcherClass.getMethod("execute");
method.invoke(launcher);
} catch (InvocationTargetException e) {
// Unwrap original exception
throw new RunnerException("Unable to execute Sonar", e.getTargetException());
} catch (Exception e) {
// Catch all other exceptions, which relates to reflection
throw new RunnerException("Unable to execute Sonar", e);
} finally {
Thread.currentThread().setContextClassLoader(oldContextClassLoader);
}
}

/**
* Allows to overwrite the environment information when Sonar Runner is embedded in a specific tool (for instance, with the Sonar Ant Task).
*
* @param key the key of the tool that embeds Sonar Runner
* @param version the version of this tool
*/
public void setEnvironmentInformation(String key, String version) {
this.globalProperties.put(PROPERTY_ENVIRONMENT_INFORMATION_KEY, key);
this.globalProperties.put(PROPERTY_ENVIRONMENT_INFORMATION_VERSION, version);
}

public void setUnmaskedPackages(String... unmaskedPackages) {
this.unmaskedPackages = unmaskedPackages;
}

public void addContainerExtension(Object extension) {
containerExtensions.add(extension);
}

}

+ 0
- 201
sonar-runner-api/src/main/java/org/sonar/runner/SonarCache.java View File

@@ -1,201 +0,0 @@
/*
* Sonar Runner - API
* 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;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
* This class is responsible for managing Sonar batch file cache. You can put file into cache and
* later try to retrieve them. MD5 is used to differentiate files (name is not secure as files may come
* from different Sonar servers and have same name but be actually different, and same for SNAPSHOTs).
* Default location of cache is
* @author Julien HENRY
*
*/
public class SonarCache {

private static final int TEMP_FILE_ATTEMPTS = 10000;

private File cacheLocation;
/**
* Temporary directory where files should be stored before be inserted in the cache.
* Having a temporary close to the final location (read on same FS) will assure
* the move will be atomic.
*/
private File tmpDir;

private SonarCache(File cacheLocation) {
this.cacheLocation = cacheLocation;
tmpDir = new File(cacheLocation, "tmp");
if (!cacheLocation.exists()) {
Logs.debug("Creating cache directory: " + cacheLocation.getAbsolutePath());
try {
FileUtils.forceMkdir(cacheLocation);
} catch (IOException e) {
throw new RunnerException("Unable to create cache directory " + cacheLocation.getAbsolutePath(), e);
}
}
}

public static class Builder {

private File sonarUserHomeLocation;
private File cacheLocation;

public Builder(File sonarUserHomeLocation) {
this.sonarUserHomeLocation = sonarUserHomeLocation;
}

public Builder setCacheLocation(File cacheLocation) {
this.cacheLocation = cacheLocation;
return this;
}

public SonarCache build() {
if (cacheLocation == null) {
return new SonarCache(new File(sonarUserHomeLocation, "cache"));
}
else {
return new SonarCache(cacheLocation);
}
}
}

public static Builder create(File sonarUserHomeLocation) {
if (sonarUserHomeLocation == null) {
throw new RunnerException("Sonar user home directory should not be null");
}
return new Builder(sonarUserHomeLocation);
}

/**
* Move the given file inside the cache. Return the MD5 of the cached file.
* @param sourceFile
* @throws IOException
*/
public String cacheFile(File sourceFile, String filename) throws IOException {
Logs.debug("Trying to cache file " + sourceFile.getAbsolutePath() + " with filename " + filename);
File tmpFileName = null;
try {
if (!sourceFile.getParentFile().equals(getTmpDir())) {
// Provided file is not close to the cache so we will move it first in a temporary file (could be non atomic)
tmpFileName = getTemporaryFile();
FileUtils.moveFile(sourceFile, tmpFileName);
}
else {
tmpFileName = sourceFile;
}
// Now compute the md5 to find the final destination
String md5;
FileInputStream fis = null;
try {
fis = new FileInputStream(tmpFileName);
md5 = DigestUtils.md5Hex(fis);
} finally {
IOUtils.closeQuietly(fis);
}
File finalDir = new File(cacheLocation, md5);
File finalFileName = new File(finalDir, filename);
// Try to create final destination folder
FileUtils.forceMkdir(finalDir);
// Now try to move the file from temporary folder to final location
boolean rename = tmpFileName.renameTo(finalFileName);
if (!rename
// Check if the file was already in cache
&& !finalFileName.exists()) {
Logs.warn("Unable to rename " + tmpFileName.getAbsolutePath() + " to " + finalFileName.getAbsolutePath());
Logs.warn("A copy/delete will be tempted but with no garantee of atomicity");
FileUtils.moveFile(tmpFileName, finalFileName);
}
Logs.debug("File cached at " + finalFileName.getAbsolutePath());
return md5;
} finally {
FileUtils.deleteQuietly(tmpFileName);
}

}

/**
* Look for a file in the cache by its filename and md5 checksum. If the file is not
* present then return null.
*/
public File getFileFromCache(String filename, String md5) {
File location = new File(new File(cacheLocation, md5), filename);
Logs.debug("Looking for " + location.getAbsolutePath());
if (location.exists()) {
return location;
}
Logs.debug("No file found in the cache with name " + filename + " and checksum " + md5);
return null;
}

/**
* Return a temporary file that caller can use to store file content before
* asking for caching it with {@link #cacheFile(File)}.
* This is to avoid extra copy.
* @return
* @throws IOException
*/
public File getTemporaryFile() throws IOException {
return createTempFile(getTmpDir());
}

/**
* Create a temporary file in the given directory.
* @param baseDir
* @return
* @throws IOException
*/
private static File createTempFile(File baseDir) throws IOException {
String baseName = System.currentTimeMillis() + "-";

for (int counter = 0; counter < TEMP_FILE_ATTEMPTS; counter++) {
File tempFile = new File(baseDir, baseName + counter);
if (tempFile.createNewFile()) {
return tempFile;
}
}
throw new IOException("Failed to create temporary file in " + baseDir.getAbsolutePath() + " within "
+ TEMP_FILE_ATTEMPTS + " attempts (tried "
+ baseName + "0 to " + baseName + (TEMP_FILE_ATTEMPTS - 1) + ')');
}

public File getTmpDir() {
if (!tmpDir.exists()) {
Logs.debug("Creating temporary cache directory: " + tmpDir.getAbsolutePath());
try {
FileUtils.forceMkdir(tmpDir);
} catch (IOException e) {
throw new RunnerException("Unable to create temporary cache directory " + tmpDir.getAbsolutePath(), e);
}
}
return tmpDir;
}

public File getCacheLocation() {
return cacheLocation;
}
}

+ 123
- 0
sonar-runner-api/src/main/java/org/sonar/runner/api/Command.java View File

@@ -0,0 +1,123 @@
/*
* Sonar Runner - API
* 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.api;

import java.io.File;
import java.util.*;

class Command {
private final String executable;
private final List<String> arguments;
private final Map<String, String> env;
private final File directory;

private Command(Builder builder) {
this.executable = builder.executable;
this.arguments = Collections.unmodifiableList(builder.arguments);
this.env = Collections.unmodifiableMap(builder.env);
this.directory = builder.directory;
}

File directory() {
return directory;
}

String executable() {
return executable;
}

List<String> arguments() {
return arguments;
}

/**
* Environment variables that are propagated during command execution.
*
* @return a non-null and immutable map of variables
*/
Map<String, String> envVariables() {
return env;
}

String[] toStrings() {
String[] strings = new String[1 + arguments.size()];
strings[0] = executable;
for (int index = 0; index < arguments.size(); index++) {
strings[index + 1] = arguments.get(index);
}
return strings;
}

@Override
public String toString() {
return Utils.join(toStrings(), " ");
}

static Builder builder() {
return new Builder();
}

static class Builder {
private String executable;
private final List<String> arguments = new ArrayList<String>();
private final Map<String, String> env = new HashMap<String, String>();
private File directory;

private Builder() {
}

Builder setExecutable(String s) {
this.executable = s;
return this;
}

Builder addArguments(String... args) {
arguments.addAll(Arrays.asList(args));
return this;
}

Builder addArguments(List<String> args) {
arguments.addAll(args);
return this;
}

Builder setEnvVariable(String key, String value) {
env.put(key, value);
return this;
}

Builder addEnvVariables(Map<String, String> map) {
env.putAll(map);
return this;
}

Builder setDirectory(File d) {
this.directory = d;
return this;
}

Command build() {
if (executable == null) {
throw new IllegalArgumentException("Command executable is not defined");
}
return new Command(this);
}
}
}

+ 30
- 0
sonar-runner-api/src/main/java/org/sonar/runner/api/CommandException.java View File

@@ -0,0 +1,30 @@
/*
* Sonar Runner - API
* 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.api;

import javax.annotation.Nullable;

class CommandException extends RuntimeException {

CommandException(String message, Command command, @Nullable Throwable throwable) {
super(message + " [command: " + command + "]", throwable);
}

}

+ 170
- 0
sonar-runner-api/src/main/java/org/sonar/runner/api/CommandExecutor.java View File

@@ -0,0 +1,170 @@
/*
* Sonar Runner - API
* 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.api;

import org.apache.commons.io.IOUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.*;

/**
* Synchronously execute a native command line. It's much more limited than the Apache Commons Exec library.
* For example it does not allow to run asynchronously or to automatically quote command-line arguments.
*
* @since 2.7
*/
class CommandExecutor {

private static final CommandExecutor INSTANCE = new CommandExecutor();

private CommandExecutor() {
}

static CommandExecutor create() {
// stateless object, so a single singleton can be shared
return INSTANCE;
}

int execute(Command command, StreamConsumer stdOut, StreamConsumer stdErr, long timeoutMilliseconds) {
ExecutorService executorService = null;
Process process = null;
StreamGobbler outputGobbler = null;
StreamGobbler errorGobbler = null;
try {
ProcessBuilder builder = new ProcessBuilder(command.toStrings());
if (command.directory() != null) {
builder.directory(command.directory());
}
builder.environment().putAll(command.envVariables());
process = builder.start();

outputGobbler = new StreamGobbler(process.getInputStream(), stdOut);
errorGobbler = new StreamGobbler(process.getErrorStream(), stdErr);
outputGobbler.start();
errorGobbler.start();

final Process finalProcess = process;
executorService = Executors.newSingleThreadExecutor();
Future<Integer> ft = executorService.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return finalProcess.waitFor();
}
});
int exitCode = ft.get(timeoutMilliseconds, TimeUnit.MILLISECONDS);
waitUntilFinish(outputGobbler);
waitUntilFinish(errorGobbler);
verifyGobbler(command, outputGobbler, "stdOut");
verifyGobbler(command, errorGobbler, "stdErr");
return exitCode;

} catch (TimeoutException te) {
process.destroy();
throw new CommandException("Timeout exceeded: " + timeoutMilliseconds + " ms", command, te);

} catch (CommandException e) {
throw e;

} catch (Exception e) {
throw new CommandException("Fail to execute command", command, e);

} finally {
waitUntilFinish(outputGobbler);
waitUntilFinish(errorGobbler);
closeStreams(process);

if (executorService != null) {
executorService.shutdown();
}
}
}

private void verifyGobbler(Command command, StreamGobbler gobbler, String type) {
if (gobbler.getException() != null) {
throw new CommandException("Error inside " + type + " stream", command, gobbler.getException());
}
}

private void closeStreams(Process process) {
if (process != null) {
IOUtils.closeQuietly(process.getInputStream());
IOUtils.closeQuietly(process.getInputStream());
IOUtils.closeQuietly(process.getOutputStream());
IOUtils.closeQuietly(process.getErrorStream());
}
}

private void waitUntilFinish(StreamGobbler thread) {
if (thread != null) {
try {
thread.join();
} catch (InterruptedException e) {
System.err.println("InterruptedException while waiting finish of " + thread.toString());
e.printStackTrace();
}
}
}

private static class StreamGobbler extends Thread {
private final InputStream is;
private final StreamConsumer consumer;
private volatile Exception exception;

StreamGobbler(InputStream is, StreamConsumer consumer) {
super("ProcessStreamGobbler");
this.is = is;
this.consumer = consumer;
}

@Override
public void run() {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
try {
String line;
while ((line = br.readLine()) != null) {
consumeLine(line);
}
} catch (IOException ioe) {
exception = ioe;

} finally {
IOUtils.closeQuietly(br);
IOUtils.closeQuietly(isr);
}
}

private void consumeLine(String line) {
if (exception == null) {
try {
consumer.consumeLine(line);
} catch (Exception e) {
exception = e;
}
}
}

public Exception getException() {
return exception;
}
}
}

+ 73
- 0
sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java View File

@@ -0,0 +1,73 @@
/*
* Sonar Runner - API
* 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.api;

import org.sonar.runner.impl.BatchLauncher;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* Implementation of {@link Runner} that is executed in the same JVM. The application can inject
* some extensions into Sonar IoC container (see {@link #addExtensions(Object...)}. It can be
* used for example in the Maven Sonar plugin to register Maven components like MavenProject
* or MavenPluginExecutor.
* @since 2.2
*/
public class EmbeddedRunner extends Runner<EmbeddedRunner> {

private final BatchLauncher batchLauncher;
private final List<Object> extensions = new ArrayList<Object>();

EmbeddedRunner(BatchLauncher bl) {
this.batchLauncher = bl;
}

/**
* Create a new instance.
*/
public static EmbeddedRunner create() {
return new EmbeddedRunner(new BatchLauncher());
}

/**
* Sonar is executed in an almost fully isolated classloader. The unmasked packages
* define the classes of the client application that are visible from Sonar classloader. They
* relate to the extensions provided by {@link #setUnmaskedPackages(String...)}.
*/
public EmbeddedRunner setUnmaskedPackages(String... packages) {
return setProperty("sonarRunner.unmaskedPackages", Utils.join(packages, ","));
}

public EmbeddedRunner addExtensions(Object... objects) {
extensions.addAll(Arrays.asList(objects));
return this;
}

List<Object> extensions() {
return extensions;
}

@Override
protected void doExecute() {
batchLauncher.execute(properties(), extensions);
}
}

+ 169
- 0
sonar-runner-api/src/main/java/org/sonar/runner/api/ForkedRunner.java View File

@@ -0,0 +1,169 @@
/*
* Sonar Runner - API
* 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.api;

import org.apache.commons.io.IOUtils;
import org.sonar.runner.impl.BatchLauncherMain;
import org.sonar.runner.impl.JarExtractor;

import javax.annotation.Nullable;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Runner executed in a new JVM.
*
* @since 2.2
*/
public class ForkedRunner extends Runner<ForkedRunner> {

private static final int ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;

private final Map<String, String> jvmEnvVariables = new HashMap<String, String>();
private final List<String> jvmArguments = new ArrayList<String>();
private String javaExecutable;
private StreamConsumer stdOut = null, stdErr = null;
private final JarExtractor jarExtractor;
private final CommandExecutor commandExecutor;

ForkedRunner(JarExtractor jarExtractor, CommandExecutor commandExecutor) {
this.jarExtractor = jarExtractor;
this.commandExecutor = commandExecutor;
}

/**
* Create new instance. Never return null.
*/
public static ForkedRunner create() {
return new ForkedRunner(new JarExtractor(), CommandExecutor.create());
}

/**
* Path to the java executable. The JVM of the client app is used by default
* (see the system property java.home)
*/
public ForkedRunner setJavaExecutable(@Nullable String s) {
this.javaExecutable = s;
return this;
}

/**
* See {@link #addJvmArguments(java.util.List)}
*/
public ForkedRunner addJvmArguments(String... s) {
return addJvmArguments(Arrays.asList(s));
}

/**
* JVM arguments, for example "-Xmx512m"
*/
public ForkedRunner addJvmArguments(List<String> args) {
jvmArguments.addAll(args);
return this;
}

/**
* Set a JVM environment variable. By default no variables are set.
*/
public ForkedRunner setJvmEnvVariable(String key, String value) {
jvmEnvVariables.put(key, value);
return this;
}

/**
* Add some JVM environment variables. By default no variables are set.
*/
public ForkedRunner addJvmEnvVariables(Map<String, String> map) {
jvmEnvVariables.putAll(map);
return this;
}

/**
* Subscribe to the standard output. By default output is {@link System.out}
*/
public ForkedRunner setStdOut(@Nullable StreamConsumer stream) {
this.stdOut = stream;
return this;
}

/**
* Subscribe to the error output. By default output is {@link System.err}
*/
public ForkedRunner setStdErr(@Nullable StreamConsumer stream) {
this.stdErr = stream;
return this;
}

@Override
protected void doExecute() {
fork(createCommand());
}

private Command createCommand() {
File propertiesFile = writeProperties();
File jarFile = jarExtractor.extract("sonar-runner-impl");
if (javaExecutable == null) {
javaExecutable = new Os().thisJavaExe().getAbsolutePath();
}
return Command.builder()
.setExecutable(javaExecutable)
.addEnvVariables(jvmEnvVariables)
.addArguments(jvmArguments)
.addArguments("-cp", jarFile.getAbsolutePath(), BatchLauncherMain.class.getName(), propertiesFile.getAbsolutePath())
.build();
}

private File writeProperties() {
OutputStream output = null;
try {
File file = File.createTempFile("sonar-project", ".properties");
output = new FileOutputStream(file);
properties().store(output, "Generated by sonar-runner");
return file;

} catch (Exception e) {
throw new IllegalStateException("Fail to export sonar-runner properties", e);

} finally {
IOUtils.closeQuietly(output);
}
}

private void fork(Command command) {
if (stdOut == null) {
stdOut = new PrintStreamConsumer(System.out);
}
if (stdErr == null) {
stdErr = new PrintStreamConsumer(System.err);
}
int status = commandExecutor.execute(command, stdOut, stdErr, ONE_DAY_IN_MILLISECONDS);
if (status != 0) {
throw new IllegalStateException("Error status: " + status);
}
}

}

+ 40
- 0
sonar-runner-api/src/main/java/org/sonar/runner/api/Os.java View File

@@ -0,0 +1,40 @@
/*
* Sonar Runner - API
* 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.api;

import java.io.File;

class Os {
boolean isWindows() {
return System.getProperty("os.name").contains("Windows");
}

File thisJavaHome() {
return new File(System.getProperty("java.home"));
}

/**
* Path to the java executable used by this VM
*/
File thisJavaExe() {
File bin = new File(thisJavaHome(), "bin");
return new File(bin, isWindows() ? "java.exe" : "java");
}
}

sonar-runner-api/src/main/java/org/sonar/runner/VersionUtils.java → sonar-runner-api/src/main/java/org/sonar/runner/api/PrintStreamConsumer.java View File

@@ -17,29 +17,24 @@
* 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;
package org.sonar.runner.api;

import java.io.PrintStream;

/**
* Internal class used only by the Runner.
* This class should not be used by Sonar Runner consumers.
* Implementation of StreamConsumer that prints lines to {@link java.io.PrintStream}. Generally used
* to forward to {@link System.out} or {@link System.err}.
*
* @since 2.2
*/
final class VersionUtils {
public class PrintStreamConsumer implements StreamConsumer {
final PrintStream output;

private VersionUtils() {
// only static methods
public PrintStreamConsumer(PrintStream output) {
this.output = output;
}

static boolean isUnsupportedVersion(String version, String[] unsuportedVersions) {
for (String unsupportedVersion : unsuportedVersions) {
if (isVersion(version, unsupportedVersion)) {
return true;
}
}
return false;
public void consumeLine(String line) {
output.println(line);
}

private static boolean isVersion(String version, String prefix) {
return version.startsWith(prefix + ".") || version.equals(prefix);
}

}

+ 108
- 0
sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java View File

@@ -0,0 +1,108 @@
/*
* Sonar Runner - API
* 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.api;

import org.sonar.runner.impl.Constants;
import org.sonar.runner.impl.Logs;

import javax.annotation.Nullable;

import java.nio.charset.Charset;
import java.util.Locale;
import java.util.Properties;

/**
* @since 2.2
*/
public abstract class Runner<T extends Runner> {

private final Properties properties = new Properties();

protected Runner() {
initProperties();
}

private void initProperties() {
// default values
properties.put(Constants.HOST_URL, "http://localhost:9000");
properties.put(Constants.TASK, "scan");
properties.put(Constants.PROP_APP, "SonarRunner");
properties.put(Constants.PROP_APP_VERSION, RunnerVersion.version());
}

public Properties properties() {
Properties clone = new Properties();
clone.putAll(properties);
return clone;
}

/**
* Declare Sonar properties, for example sonar.projectKey=>foo.
*/
public T addProperties(Properties p) {
properties.putAll(p);
return (T) this;
}

public T setProperty(String key, String value) {
properties.setProperty(key, value);
return (T) this;
}

public String property(String key, @Nullable String defaultValue) {
return properties.getProperty(key, defaultValue);
}

/**
* User-agent used in the HTTP requests to the Sonar server
*/
public T setApp(String app, String version) {
setProperty(Constants.PROP_APP, app);
setProperty(Constants.PROP_APP_VERSION, version);
return (T) this;
}

public String app() {
return property(Constants.PROP_APP, null);
}

public String appVersion() {
return property(Constants.PROP_APP_VERSION, null);
}

public void execute() {
initSourceEncoding();
doExecute();
}

private void initSourceEncoding() {
String sourceEncoding = property(Constants.SOURCE_ENCODING, null);
boolean platformDependent = false;
if (sourceEncoding == null || sourceEncoding.equals("")) {
sourceEncoding = Charset.defaultCharset().name();
platformDependent = true;
setProperty(Constants.SOURCE_ENCODING, sourceEncoding);
}
Logs.info("Default locale: \"" + Locale.getDefault() + "\", source code encoding: \"" + sourceEncoding + "\""
+ (platformDependent ? " (analysis is platform dependent)" : ""));
}

protected abstract void doExecute();
}

sonar-runner-api/src/main/java/org/sonar/runner/Version.java → sonar-runner-api/src/main/java/org/sonar/runner/api/RunnerVersion.java View File

@@ -17,38 +17,30 @@
* 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;
package org.sonar.runner.api;

import org.apache.commons.io.IOUtils;
import java.util.Scanner;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public enum Version {
/**
* Version of this sonar-runner API.
* @since 2.2
*/
public enum RunnerVersion {

INSTANCE;

private static final String PROPERTIES_PATH = "/org/sonar/runner/version.txt";
private String version;

public static String getVersion() {
public static String version() {
return INSTANCE.version;
}

private Version() {
InputStream input = getClass().getResourceAsStream(PROPERTIES_PATH);
private RunnerVersion() {
Scanner scanner = new Scanner(getClass().getResourceAsStream("/org/sonar/runner/api/version.txt"), "UTF-8");
try {
Properties properties = new Properties();
properties.load(input);
this.version = properties.getProperty("version");

} catch (IOException e) {
// Can not load the version
this.version = "";

this.version = scanner.next();
} finally {
IOUtils.closeQuietly(input);
scanner.close();
}
}
}

sonar-runner-api/src/main/java/org/sonar/runner/package-info.java → sonar-runner-api/src/main/java/org/sonar/runner/api/StreamConsumer.java View File

@@ -17,7 +17,11 @@
* 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.api;

/**
* API package of the Sonar Runner.
* @since 2.2
*/
package org.sonar.runner;
public interface StreamConsumer {
void consumeLine(String line);
}

+ 41
- 0
sonar-runner-api/src/main/java/org/sonar/runner/api/Utils.java View File

@@ -0,0 +1,41 @@
/*
* Sonar Runner - API
* 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.api;

import java.util.Arrays;
import java.util.Iterator;

class Utils {
/**
* Similar to org.apache.commons.lang.StringUtils#join()
*/
static String join(String[] array, String delimiter) {
StringBuilder sb = new StringBuilder();
Iterator it = Arrays.asList(array).iterator();
while (it.hasNext()) {
sb.append(it.next());
if (!it.hasNext()) {
break;
}
sb.append(delimiter);
}
return sb.toString();
}
}

+ 23
- 0
sonar-runner-api/src/main/java/org/sonar/runner/api/package-info.java View File

@@ -0,0 +1,23 @@
/*
* Sonar Runner - API
* 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
*/
@ParametersAreNonnullByDefault
package org.sonar.runner.api;

import javax.annotation.ParametersAreNonnullByDefault;

+ 1
- 0
sonar-runner-api/src/main/resources/org/sonar/runner/api/version.txt View File

@@ -0,0 +1 @@
${project.version}

+ 0
- 1
sonar-runner-api/src/main/resources/org/sonar/runner/version.txt View File

@@ -1 +0,0 @@
version=${project.version}

+ 0
- 178
sonar-runner-api/src/test/java/org/sonar/runner/BootstrapperTest.java View File

@@ -1,178 +0,0 @@
/*
* Sonar Runner - API
* 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;

import org.apache.commons.io.IOUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class BootstrapperTest {

@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();

@Test
public void shouldRemoveLastUrlSlash() {
Bootstrapper bootstrapper = new Bootstrapper("", "http://test/", new File("target/tmp"), null);
assertThat(bootstrapper.getServerUrl()).isEqualTo("http://test");
}

@Test(expected = Exception.class)
public void shouldFailIfCanNotConnectServer() {
Bootstrapper bootstrapper = new Bootstrapper("", "http://unknown.foo", new File("target/tmp"), null);
bootstrapper.getServerVersion();
}

@Test
public void shouldReturnUserAgent() {
Bootstrapper bootstrapper = new Bootstrapper("test/0.1", "http://unknown.foo", new File("target/tmp"), null);
String userAgent = bootstrapper.getUserAgent();

assertThat(userAgent.length()).isGreaterThan(0);
assertThat(userAgent).startsWith("sonar-bootstrapper/");
assertThat(userAgent).endsWith(" test/0.1");
}

@Test
public void shouldReturnValidVersion() {
Bootstrapper bootstrapper = new Bootstrapper("", "http://test", new File("target/tmp"), null) {
@Override
String remoteContent(String path) throws IOException {
return "2.6";
}
};
assertThat(bootstrapper.getServerVersion()).isEqualTo("2.6");
}

@Test
public void shouldParseEncodingFromContentType() {
assertThat(Bootstrapper.getCharsetFromContentType("text/html; charset=EUC-JP")).isEqualTo("EUC-JP");
assertThat(Bootstrapper.getCharsetFromContentType("text/html")).isNull();
}

@Test
public void shouldCheckVersionForCache() {
assertThat(Bootstrapper.isUnsupportedVersionForCache("1.0")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("2.0")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("2.1")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("2.2")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("2.3")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("2.4")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("2.4.1")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("2.5")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("2.11")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("3.0")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("3.1")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("3.2")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("3.3")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("3.4")).isTrue();
assertThat(Bootstrapper.isUnsupportedVersionForCache("3.5")).isFalse();
}

@Test
public void shouldCacheWhenNecessary() throws Exception {
File sonarUserHome = tempFolder.newFolder();
SonarCache cache = SonarCache.create(sonarUserHome).build();
final MockedConnectionFactory connections = new MockedConnectionFactory("http://test");
connections.register("/api/server/version", "3.5");
connections.register("/batch_bootstrap/index", "foo.jar|922afef30ca31573d7131347d01b76c4\nbar.jar|69155f65900fbabbf21e28abb33dd06a");
connections.register("/batch/foo.jar", "fakecontent1");
connections.register("/batch/bar.jar", "fakecontent2");
Bootstrapper bootstrapper = new Bootstrapper("", "http://test", new File("target/tmp"), cache) {
@Override
HttpURLConnection newHttpConnection(URL url) throws IOException {
return connections.get(url);
}
};
bootstrapper.createClassLoader(new URL[] {}, this.getClass().getClassLoader());
assertThat(new File(new File(cache.getCacheLocation(), "922afef30ca31573d7131347d01b76c4"), "foo.jar")).exists();
assertThat(new File(new File(cache.getCacheLocation(), "69155f65900fbabbf21e28abb33dd06a"), "bar.jar")).exists();

// Should not download during the second execution
final MockedConnectionFactory connections2 = new MockedConnectionFactory("http://test");
connections2.register("/api/server/version", "3.5");
connections2.register("/batch_bootstrap/index", "foo.jar|922afef30ca31573d7131347d01b76c4\nbar.jar|69155f65900fbabbf21e28abb33dd06a");
Bootstrapper bootstrapper2 = new Bootstrapper("", "http://test", new File("target/tmp"), cache) {
@Override
HttpURLConnection newHttpConnection(URL url) throws IOException {
return connections2.get(url);
}
};
bootstrapper2.createClassLoader(new URL[] {}, this.getClass().getClassLoader());
}

@Test
public void shouldDownloadFromOldURL() throws Exception {
File sonarUserHome = tempFolder.newFolder();
final MockedConnectionFactory connections = new MockedConnectionFactory("http://test");
connections.register("/api/server/version", "3.4");
connections.register("/batch/", "foo.jar,bar.jar");
connections.register("/batch/foo.jar", "fakecontent1");
connections.register("/batch/bar.jar", "fakecontent2");
Bootstrapper bootstrapper = new Bootstrapper("", "http://test", new File("target/tmp"), SonarCache.create(sonarUserHome).build()) {
@Override
HttpURLConnection newHttpConnection(URL url) throws IOException {
return connections.get(url);
}
};
bootstrapper.createClassLoader(new URL[] {}, this.getClass().getClassLoader());
verify(connections.get("/batch/foo.jar")).getInputStream();
verify(connections.get("/batch/bar.jar")).getInputStream();
}

private class MockedConnectionFactory {
private Map<URL, HttpURLConnection> mockedConnections = new HashMap<URL, HttpURLConnection>();
private String serverUrl;

public MockedConnectionFactory(String serverUrl) {
this.serverUrl = serverUrl;
}

public void register(String path, String content) throws Exception {
HttpURLConnection mockConnection = mock(HttpURLConnection.class);
when(mockConnection.getInputStream()).thenReturn(IOUtils.toInputStream(content));
when(mockConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
mockedConnections.put(new URL(serverUrl + path), mockConnection);
}

public HttpURLConnection get(URL url) {
return mockedConnections.get(url);
}

public HttpURLConnection get(String path) throws MalformedURLException {
return mockedConnections.get(new URL(serverUrl + path));
}

}
}

+ 0
- 83
sonar-runner-api/src/test/java/org/sonar/runner/LogsTest.java View File

@@ -1,83 +0,0 @@
/*
* Sonar Runner - API
* 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;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import static org.fest.assertions.Assertions.assertThat;

public class LogsTest {

private PrintStream oldSysout;
private PrintStream oldSyserr;

private ByteArrayOutputStream baosOut;
private ByteArrayOutputStream baosErr;

@Before
public void prepare() {
oldSysout = System.out;
oldSyserr = System.err;
baosOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(baosOut));
baosErr = new ByteArrayOutputStream();
System.setErr(new PrintStream(baosErr));
}

@After
public void restore() {
System.setOut(oldSysout);
System.setErr(oldSyserr);
}

@Test
public void shouldLogInfo() {
Logs.info("info");
assertThat(baosOut.toString()).contains("INFO: info");
assertThat(baosErr.toString()).isEmpty();
}

@Test
public void shouldLogError() {
Logs.error("error");
assertThat(baosOut.toString()).isEmpty();
assertThat(baosErr.toString()).contains("ERROR: error");
}

@Test
public void shouldLogErrorWithoutThrowable() {
Logs.error("error", null);
assertThat(baosOut.toString()).isEmpty();
assertThat(baosErr.toString()).contains("ERROR: error");
}

@Test
public void shouldLogErrorWithThrowable() {
Logs.error("error", new RuntimeException());
assertThat(baosOut.toString()).isEmpty();
assertThat(baosErr.toString()).contains("ERROR: error").contains("RuntimeException");
}

}

+ 0
- 131
sonar-runner-api/src/test/java/org/sonar/runner/MainTest.java View File

@@ -1,131 +0,0 @@
/*
* Sonar Runner - API
* 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;

import org.junit.Before;
import org.junit.Test;

import java.io.File;
import java.util.Properties;

import static org.fest.assertions.Assertions.assertThat;

public class MainTest {

private Main main;

@Before
public void prepare() {
main = new Main();
}

@Test
public void shouldParseEmptyArguments() {
Properties props = main.parseArguments(new String[] {});
assertThat(props).isEmpty();
}

@Test
public void shouldParseArguments() {
Properties props = main.parseArguments(new String[] {"-D", "foo=bar", "--define", "hello=world", "-Dboolean"});
assertThat(props).hasSize(3);
assertThat(props.getProperty("foo")).isEqualTo("bar");
assertThat(props.getProperty("hello")).isEqualTo("world");
assertThat(props.getProperty("boolean")).isEqualTo("true");
}

@Test
public void shouldParseCommand() {

Properties props = main.parseArguments(new String[] {"cmd", "-D", "foo=bar", "--define", "hello=world", "-Dboolean"});
assertThat(main.command).isEqualTo("cmd");
assertThat(props).hasSize(3);
}

@Test
public void shouldEnableDebugMode() {
Properties props = main.parseArguments(new String[] {"-X"});
assertThat(main.debugMode).isTrue();
assertThat(main.displayStackTrace).isTrue();
assertThat(props.getProperty(Runner.PROPERTY_VERBOSE)).isEqualTo("true");
}

@Test
public void shouldEnableError() {
Properties props = main.parseArguments(new String[] {"-e"});
assertThat(main.debugMode).isFalse();
assertThat(main.displayStackTrace).isTrue();
assertThat(props.getProperty(Runner.PROPERTY_VERBOSE)).isNull();
}

@Test
public void shouldDisableDebugModeAndStackByDefault() {
Properties props = main.parseArguments(new String[] {});
assertThat(main.debugMode).isFalse();
assertThat(main.displayStackTrace).isFalse();
assertThat(props.getProperty(Runner.PROPERTY_VERBOSE)).isNull();
}

@Test
public void shouldLoadRunnerSettingsByHome() throws Exception {
File home = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByHome/").toURI());
Properties args = new Properties();
args.setProperty("runner.home", home.getCanonicalPath());

Properties props = new Main().loadRunnerConfiguration(args);

assertThat(props.getProperty("sonar.host.url")).isEqualTo("http://moon/sonar");
}

@Test
public void shouldNotFailIfNoHome() throws Exception {
Properties args = new Properties();
Properties props = new Main().loadRunnerConfiguration(args);

assertThat(props).isEmpty();
}

@Test
public void shouldLoadRunnerSettingsByDirectPath() throws Exception {
File settings = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadRunnerSettingsByDirectPath/other-conf.properties").toURI());
Properties args = new Properties();
args.setProperty("runner.settings", settings.getCanonicalPath());
Properties props = new Main().loadRunnerConfiguration(args);

assertThat(props.getProperty("sonar.host.url")).isEqualTo("http://other/sonar");
}

@Test
public void shouldLoadCompleteConfiguration() throws Exception {
File runnerHome = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/runner").toURI());
File projectHome = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/project").toURI());
Main main = new Main();
Properties args = main.parseArguments(new String[] {
"-D", "runner.home=" + runnerHome.getCanonicalPath(),
"-D", "project.home=" + projectHome.getCanonicalPath()
});
main.loadProperties(args);

assertThat(main.projectProperties.getProperty("project.prop")).isEqualTo("foo");
assertThat(main.projectProperties.getProperty("overridden.prop")).isEqualTo("project scope");
assertThat(main.globalProperties.getProperty("global.prop")).isEqualTo("jdbc:mysql:localhost/sonar");
}

}

+ 0
- 227
sonar-runner-api/src/test/java/org/sonar/runner/RunnerTest.java View File

@@ -1,227 +0,0 @@
/*
* Sonar Runner - API
* 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;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.nio.charset.Charset;
import java.util.Properties;

import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class RunnerTest {

@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();

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

@Test
public void shouldHaveDefaultEncodingIfNotForce() {
Runner runner = Runner.create(new Properties());
assertThat(runner.getSourceCodeEncoding()).isEqualTo(Charset.defaultCharset().name());
assertThat(runner.isEncodingPlatformDependant()).isTrue();
}

@Test
public void shouldKeepEncodingIfSpecified() {
Properties props = new Properties();
// Yeah, windows charset!
props.setProperty("sonar.sourceEncoding", "cp1252");
Runner runner = Runner.create(props);
assertThat(runner.getSourceCodeEncoding()).isEqualTo("cp1252");
assertThat(runner.isEncodingPlatformDependant()).isFalse();
}

@Test
public void shouldHaveDefaultEnvironmentInformationValues() {
Runner runner = Runner.create(new Properties());
assertThat(runner.getProperties().getProperty(Runner.PROPERTY_ENVIRONMENT_INFORMATION_KEY)).isEqualTo("Runner");
assertThat(runner.getProperties().getProperty(Runner.PROPERTY_ENVIRONMENT_INFORMATION_VERSION)).contains(".");
assertThat(runner.getProperties().getProperty(Runner.PROPERTY_ENVIRONMENT_INFORMATION_VERSION)).doesNotContain("$");
}

@Test
public void shouldOverwriteDefaultEnvironmentInformationValues() {
Runner runner = Runner.create(new Properties());
runner.setEnvironmentInformation("Ant", "1.2.3");
assertThat(runner.getProperties().getProperty(Runner.PROPERTY_ENVIRONMENT_INFORMATION_KEY)).isEqualTo("Ant");
assertThat(runner.getProperties().getProperty(Runner.PROPERTY_ENVIRONMENT_INFORMATION_VERSION)).isEqualTo("1.2.3");
}

@Test
public void shouldCheckVersion() {
assertThat(Runner.isUnsupportedVersion("1.0")).isTrue();
assertThat(Runner.isUnsupportedVersion("2.0")).isTrue();
assertThat(Runner.isUnsupportedVersion("2.1")).isTrue();
assertThat(Runner.isUnsupportedVersion("2.2")).isTrue();
assertThat(Runner.isUnsupportedVersion("2.3")).isTrue();
assertThat(Runner.isUnsupportedVersion("2.4")).isTrue();
assertThat(Runner.isUnsupportedVersion("2.4.1")).isTrue();
assertThat(Runner.isUnsupportedVersion("2.5")).isTrue();
assertThat(Runner.isUnsupportedVersion("2.11")).isFalse();
assertThat(Runner.isUnsupportedVersion("3.0")).isFalse();
assertThat(Runner.isUnsupportedVersion("3.1")).isFalse();
assertThat(Runner.isUnsupportedVersion("3.2")).isFalse();
assertThat(Runner.isUnsupportedVersion("3.3")).isFalse();
assertThat(Runner.isUnsupportedVersion("3.4")).isFalse();
assertThat(Runner.isUnsupportedVersion("3.5")).isFalse();
}

@Test
public void shouldCheckVersionForTasks() {
assertThat(Runner.isUnsupportedVersionForTasks("1.0")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("2.0")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("2.1")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("2.2")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("2.3")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("2.4")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("2.4.1")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("2.5")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("2.11")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("3.0")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("3.1")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("3.2")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("3.3")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("3.4")).isTrue();
assertThat(Runner.isUnsupportedVersionForTasks("3.5")).isFalse();
}

@Test
public void shouldGetServerUrl() {
Properties properties = new Properties();
Runner runner = Runner.create(properties);
assertThat(runner.getSonarServerURL()).isEqualTo("http://localhost:9000");
properties.setProperty("sonar.host.url", "foo");
assertThat(runner.getSonarServerURL()).isEqualTo("foo");
}

@Test
public void shouldInitDirs() throws Exception {
Properties props = new Properties();
File home = tempFolder.newFolder("shouldInitDirs").getCanonicalFile();
props.setProperty(Runner.PROPERTY_SONAR_PROJECT_BASEDIR, home.getCanonicalPath());
Runner runner = Runner.create(props);
assertThat(runner.getProperties().get(Runner.PROPERTY_SONAR_PROJECT_BASEDIR)).isEqualTo(home.getCanonicalPath());

assertThat(runner.getProjectDir().getCanonicalFile()).isEqualTo(home);
assertThat(runner.getWorkDir().getCanonicalFile()).isEqualTo(new File(home, ".sonar"));
}

@Test
public void shouldInitProjectDirWithCurrentDir() throws Exception {
Runner runner = Runner.create(new Properties());

assertThat(runner.getProjectDir().isDirectory()).isTrue();
assertThat(runner.getProjectDir().exists()).isTrue();
}

@Test
public void shouldSetValidBaseDirOnConstructor() {
File baseDir = tempFolder.newFolder("shouldInitDirs");
Runner runner = Runner.create(new Properties(), baseDir);
assertThat(runner.getProjectDir()).isEqualTo(baseDir);
}

@Test
public void shouldFailIfBaseDirDoesNotExist() {
File fakeBasedir = new File("fake");

thrown.expect(RunnerException.class);
thrown.expectMessage("Project home must be an existing directory: " + fakeBasedir.getAbsolutePath());

Runner.create(new Properties(), fakeBasedir);
}

@Test
public void shouldSpecifyWorkingDirectory() {
Properties properties = new Properties();
Runner runner = Runner.create(properties);
assertThat(runner.getWorkDir()).isEqualTo(new File(".", ".sonar"));

// empty string
properties.setProperty(Runner.PROPERTY_WORK_DIRECTORY, " ");
runner = Runner.create(properties);
assertThat(runner.getWorkDir()).isEqualTo(new File(".", ".sonar").getAbsoluteFile());

// real relative path
properties.setProperty(Runner.PROPERTY_WORK_DIRECTORY, "temp-dir");
runner = Runner.create(properties);
assertThat(runner.getWorkDir()).isEqualTo(new File(".", "temp-dir").getAbsoluteFile());

// real absolute path
properties.setProperty(Runner.PROPERTY_WORK_DIRECTORY, new File("target", "temp-dir2").getAbsolutePath());
runner = Runner.create(properties);
assertThat(runner.getWorkDir()).isEqualTo(new File("target", "temp-dir2").getAbsoluteFile());
}

@Test
public void shouldDeleteWorkingDirectory() {
Properties properties = new Properties();
File workDir = new File("target", "temp-dir-should-be-deleted");
workDir.mkdirs();
assertThat(workDir.exists()).isTrue();
// real absolute path
properties.setProperty(Runner.PROPERTY_WORK_DIRECTORY, workDir.getAbsolutePath());
Runner.create(properties);
assertThat(workDir.exists()).isFalse();
}

@Test
public void shouldCheckSonarVersion() {
Properties properties = new Properties();
Runner runner = Runner.create(properties);
Bootstrapper bootstrapper = mock(Bootstrapper.class);

// nothing happens, OK
when(bootstrapper.getServerVersion()).thenReturn("3.0");
runner.checkSonarVersion(bootstrapper);

// but fails with older versions
when(bootstrapper.getServerVersion()).thenReturn("2.1");
thrown.expect(RunnerException.class);
thrown.expectMessage("Sonar 2.1 is not supported. Please upgrade Sonar to version 2.11 or more.");
runner.checkSonarVersion(bootstrapper);
}

@Test
public void shouldCheckSonarVersionForTasks() {
Properties properties = new Properties();
Runner runner = Runner.create("foo-cmd", properties, properties);
Bootstrapper bootstrapper = mock(Bootstrapper.class);

// nothing happens, OK
when(bootstrapper.getServerVersion()).thenReturn("3.5");
runner.checkSonarVersion(bootstrapper);

// but fails with older versions
when(bootstrapper.getServerVersion()).thenReturn("3.4");
thrown.expect(RunnerException.class);
thrown.expectMessage("Sonar 3.4 doesn't support tasks. Please upgrade Sonar to version 3.5 or more.");
runner.checkSonarVersion(bootstrapper);
}
}

+ 161
- 0
sonar-runner-api/src/test/java/org/sonar/runner/api/CommandExecutorTest.java View File

@@ -0,0 +1,161 @@
/*
* Sonar Runner - API
* 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.api;

import org.apache.commons.io.FileUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestName;

import java.io.File;
import java.io.IOException;

import static org.fest.assertions.Assertions.assertThat;
import static org.junit.Assert.fail;

public class CommandExecutorTest {

@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();

@Rule
public TestName testName = new TestName();

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

PrintStreamConsumer stdout = new PrintStreamConsumer(System.out);
PrintStreamConsumer stderr = new PrintStreamConsumer(System.err);
File workDir;

@Before
public void setUp() throws IOException {
workDir = tempFolder.newFolder(testName.getMethodName());
}

@Test
public void should_consume_StdOut_and_StdErr() throws Exception {
final StringBuilder stdOutBuilder = new StringBuilder();
StreamConsumer stdOutConsumer = new StreamConsumer() {
public void consumeLine(String line) {
stdOutBuilder.append(line).append(System.getProperty("line.separator"));
}
};
final StringBuilder stdErrBuilder = new StringBuilder();
StreamConsumer stdErrConsumer = new StreamConsumer() {
public void consumeLine(String line) {
stdErrBuilder.append(line).append(System.getProperty("line.separator"));
}
};
Command command = Command.builder().setExecutable(getScript("output")).setDirectory(workDir).build();
int exitCode = CommandExecutor.create().execute(command, stdOutConsumer, stdErrConsumer, 1000L);
assertThat(exitCode).isEqualTo(0);

String stdOut = stdOutBuilder.toString();
String stdErr = stdErrBuilder.toString();
assertThat(stdOut).contains("stdOut: first line");
assertThat(stdOut).contains("stdOut: second line");
assertThat(stdErr).contains("stdErr: first line");
assertThat(stdErr).contains("stdErr: second line");
}

@Test
public void stdOut_consumer_can_throw_exception() throws Exception {
Command command = Command.builder().setExecutable(getScript("output")).setDirectory(workDir).build();
thrown.expect(CommandException.class);
thrown.expectMessage("Error inside stdOut stream");
CommandExecutor.create().execute(command, BAD_CONSUMER, NOP_CONSUMER, 1000L);
}

@Test
public void stdErr_consumer_can_throw_exception() throws Exception {
Command command = Command.builder().setExecutable(getScript("output")).setDirectory(workDir).build();
thrown.expect(CommandException.class);
thrown.expectMessage("Error inside stdErr stream");
CommandExecutor.create().execute(command, NOP_CONSUMER, BAD_CONSUMER, 1000L);
}

private static final StreamConsumer NOP_CONSUMER = new StreamConsumer() {
public void consumeLine(String line) {
// nop
}
};

private static final StreamConsumer BAD_CONSUMER = new StreamConsumer() {
public void consumeLine(String line) {
throw new RuntimeException();
}
};

@Test
public void should_use_working_directory_to_store_argument_and_environment_variable() throws Exception {
Command command = Command.builder()
.setDirectory(workDir)
.setExecutable(getScript("echo"))
.addArguments("1")
.setEnvVariable("ENVVAR", "2")
.build();
int exitCode = CommandExecutor.create().execute(command, stdout, stderr, 1000L);
assertThat(exitCode).isEqualTo(0);
File logFile = new File(workDir, "echo.log");
assertThat(logFile).exists();
String log = FileUtils.readFileToString(logFile);
assertThat(log).contains(workDir.getAbsolutePath());
assertThat(log).contains("Parameter: 1");
assertThat(log).contains("Environment variable: 2");
}

@Test
public void should_stop_after_timeout() throws IOException {
String executable = getScript("forever");
long start = System.currentTimeMillis();
try {
Command command = Command.builder().setExecutable(executable).setDirectory(workDir).build();
CommandExecutor.create().execute(command, stdout, stderr, 300L);
fail();
} catch (CommandException e) {
long duration = System.currentTimeMillis() - start;
// should test >= 300 but it strangly fails during build on windows.
// The timeout is raised after 297ms (??)
assertThat(duration).as(e.getMessage()).isGreaterThan(290L);
}
}

@Test
public void should_fail_if_script_not_found() {
thrown.expect(CommandException.class);
Command command = Command.builder().setExecutable("notfound").setDirectory(workDir).build();
CommandExecutor.create().execute(command, stdout, stderr, 1000L);
}

private static String getScript(String name) throws IOException {
String filename;
if (new Os().isWindows()) {
filename = name + ".bat";
} else {
filename = name + ".sh";
}
return new File("src/test/scripts/" + filename).getCanonicalPath();
}

}

+ 75
- 0
sonar-runner-api/src/test/java/org/sonar/runner/api/CommandTest.java View File

@@ -0,0 +1,75 @@
/*
* Sonar Runner - API
* 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.api;

import org.junit.Test;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
import static org.fest.assertions.MapAssert.entry;

public class CommandTest {
@Test
public void test_simple_build() throws Exception {
Command command = Command.builder().setExecutable("java").build();
assertThat(command.executable()).isEqualTo("java");
assertThat(command.envVariables()).isEmpty();
assertThat(command.arguments()).isEmpty();
assertThat(command.toStrings()).containsOnly("java");
assertThat(command.toString()).isEqualTo("java");
}

@Test
public void test_arguments_and_env_variables() throws Exception {
Map<String, String> env = new HashMap<String, String>();
env.put("USER_HOME", "/user");

Command command = Command.builder()
.setExecutable("java")
.addArguments("-Dfoo=bar", "-Djava.io.tmpdir=/tmp")
.addArguments(Arrays.asList("-Xmx512m"))
.setEnvVariable("JAVA_HOME", "/path/to/jdk")
.addEnvVariables(env)
.build();

assertThat(command.executable()).isEqualTo("java");
assertThat(command.envVariables()).hasSize(2).includes(
entry("JAVA_HOME", "/path/to/jdk"),
entry("USER_HOME", "/user")
);
assertThat(command.arguments()).containsSequence("-Dfoo=bar", "-Djava.io.tmpdir=/tmp", "-Xmx512m");
assertThat(command.toStrings()).containsOnly("java", "-Dfoo=bar", "-Djava.io.tmpdir=/tmp", "-Xmx512m");
assertThat(command.toString()).isEqualTo("java -Dfoo=bar -Djava.io.tmpdir=/tmp -Xmx512m");
}

@Test
public void executable_should_be_required() {
try {
Command.builder().build();
fail();
} catch (IllegalArgumentException e) {
// success
}
}
}

+ 106
- 0
sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java View File

@@ -0,0 +1,106 @@
/*
* Sonar Runner - API
* 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.api;

import org.junit.Test;
import org.mockito.ArgumentMatcher;
import org.sonar.runner.impl.BatchLauncher;
import org.sonar.runner.impl.Constants;

import java.util.List;
import java.util.Properties;

import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class EmbeddedRunnerTest {
@Test
public void should_create() {
assertThat(EmbeddedRunner.create()).isNotNull().isInstanceOf(EmbeddedRunner.class);
}

@Test
public void test_app() {
EmbeddedRunner runner = EmbeddedRunner.create().setApp("Eclipse", "3.1");
assertThat(runner.app()).isEqualTo("Eclipse");
assertThat(runner.appVersion()).isEqualTo("3.1");
}

@Test
public void should_set_unmasked_packages() {
EmbeddedRunner runner = EmbeddedRunner.create();
assertThat(runner.property(Constants.UNMASKED_PACKAGES, null)).isNull();

runner = EmbeddedRunner.create().setUnmaskedPackages("org.apache.ant", "org.ant");
assertThat(runner.property(Constants.UNMASKED_PACKAGES, null)).isEqualTo("org.apache.ant,org.ant");
}

@Test
public void should_add_extensions() {
EmbeddedRunner runner = EmbeddedRunner.create();
assertThat(runner.extensions()).isEmpty();

FakeExtension fakeExtension = new FakeExtension();
runner.addExtensions(fakeExtension);
assertThat(runner.extensions()).containsExactly(fakeExtension);
}

@Test
public void should_set_properties() {
EmbeddedRunner runner = EmbeddedRunner.create();
runner.setProperty("sonar.projectKey", "foo");
runner.addProperties(new Properties() {{
setProperty("sonar.login", "admin");
setProperty("sonar.password", "gniark");
}});

assertThat(runner.property("sonar.projectKey", null)).isEqualTo("foo");
assertThat(runner.property("sonar.login", null)).isEqualTo("admin");
assertThat(runner.property("sonar.password", null)).isEqualTo("gniark");
assertThat(runner.property("not.set", "this_is_default")).isEqualTo("this_is_default");
}

@Test
public void should_launch_batch() {
BatchLauncher batchLauncher = mock(BatchLauncher.class);
EmbeddedRunner runner = new EmbeddedRunner(batchLauncher);
final FakeExtension fakeExtension = new FakeExtension();
runner.addExtensions(fakeExtension);
runner.setProperty("sonar.projectKey", "foo");
runner.execute();

verify(batchLauncher).execute(argThat(new ArgumentMatcher<Properties>() {
@Override
public boolean matches(Object o) {
return "foo".equals(((Properties) o).getProperty("sonar.projectKey"));
}
}), argThat(new ArgumentMatcher<List<Object>>() {
@Override
public boolean matches(Object o) {
return ((List) o).contains(fakeExtension);
}
}));
}

static class FakeExtension {
}
}

+ 134
- 0
sonar-runner-api/src/test/java/org/sonar/runner/api/ForkedRunnerTest.java View File

@@ -0,0 +1,134 @@
/*
* Sonar Runner - API
* 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.api;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentMatcher;
import org.sonar.runner.impl.JarExtractor;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Properties;

import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class ForkedRunnerTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Test
public void should_create() {
ForkedRunner runner = ForkedRunner.create();
assertThat(runner).isNotNull().isInstanceOf(ForkedRunner.class);
}

@Test
public void should_print_to_standard_outputs_by_default() throws IOException {
JarExtractor jarExtractor = mock(JarExtractor.class);
final File jar = temp.newFile();
when(jarExtractor.extract("sonar-runner-impl")).thenReturn(jar);

CommandExecutor commandExecutor = mock(CommandExecutor.class);
ForkedRunner runner = new ForkedRunner(jarExtractor, commandExecutor);
runner.execute();

verify(commandExecutor).execute(any(Command.class), argThat(new StdConsumerMatcher(System.out)), argThat(new StdConsumerMatcher(System.err)), anyLong());
}

static class StdConsumerMatcher extends ArgumentMatcher<StreamConsumer> {
PrintStream output;

StdConsumerMatcher(PrintStream output) {
this.output = output;
}

public boolean matches(Object o) {
return ((PrintStreamConsumer) o).output == output;
}
}

@Test
public void test_java_command() throws IOException {
JarExtractor jarExtractor = mock(JarExtractor.class);
final File jar = temp.newFile();
when(jarExtractor.extract("sonar-runner-impl")).thenReturn(jar);

CommandExecutor commandExecutor = mock(CommandExecutor.class);

ForkedRunner runner = new ForkedRunner(jarExtractor, commandExecutor);
runner.setJavaExecutable("java");
runner.setProperty("sonar.dynamicAnalysis", "false");
runner.setProperty("sonar.login", "admin");
runner.addJvmArguments("-Xmx512m");
runner.addJvmEnvVariables(System.getenv());
runner.setJvmEnvVariable("SONAR_HOME", "/path/to/sonar");
runner.setStdOut(mock(StreamConsumer.class));
runner.setStdErr(mock(StreamConsumer.class));

runner.execute();

verify(commandExecutor).execute(argThat(new ArgumentMatcher<Command>() {
public boolean matches(Object o) {
Command command = (Command) o;
assertThat(command.toStrings()).hasSize(6);
assertThat(command.toStrings()[0]).isEqualTo("java");
assertThat(command.toStrings()[1]).isEqualTo("-Xmx512m");
assertThat(command.toStrings()[2]).isEqualTo("-cp");
assertThat(command.toStrings()[3]).isEqualTo(jar.getAbsolutePath());
assertThat(command.toStrings()[4]).isEqualTo("org.sonar.runner.impl.BatchLauncherMain");

// env variables
assertThat(command.envVariables().size()).isGreaterThan(1);
assertThat(command.envVariables().get("SONAR_HOME")).isEqualTo("/path/to/sonar");

// the properties
String propsPath = command.toStrings()[5];
assertThat(propsPath).endsWith(".properties");
Properties properties = new Properties();
try {
properties.load(new FileInputStream(propsPath));
} catch (IOException e) {
throw new IllegalStateException(e);
}
assertThat(properties.size()).isGreaterThan(2);
assertThat(properties.getProperty("sonar.dynamicAnalysis")).isEqualTo("false");
assertThat(properties.getProperty("sonar.login")).isEqualTo("admin");
assertThat(properties.getProperty("-Xmx512m")).isNull();
assertThat(properties.getProperty("SONAR_HOME")).isNull();
// default values
assertThat(properties.getProperty("sonar.task")).isEqualTo("scan");
assertThat(properties.getProperty("sonar.host.url")).isEqualTo("http://localhost:9000");
return true;
}
}), any(PrintStreamConsumer.class), any(PrintStreamConsumer.class), anyLong());

}
}

+ 47
- 0
sonar-runner-api/src/test/java/org/sonar/runner/api/OsTest.java View File

@@ -0,0 +1,47 @@
/*
* Sonar Runner - API
* 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.api;

import org.apache.commons.lang.SystemUtils;
import org.junit.Test;

import java.io.File;

import static org.fest.assertions.Assertions.assertThat;

public class OsTest {
@Test
public void testIsWindows() throws Exception {
assertThat(new Os().isWindows()).isEqualTo(SystemUtils.IS_OS_WINDOWS);
}

@Test
public void testUsedJavaHome() throws Exception {
File javaHome = new Os().thisJavaHome();
assertThat(javaHome).isNotNull().exists().isDirectory();
}

@Test
public void testUsedJavaExe() throws Exception {
File javaExe = new Os().thisJavaExe();
assertThat(javaExe).isNotNull().isFile().exists();
assertThat(javaExe.getName()).contains("java");
}
}

+ 42
- 0
sonar-runner-api/src/test/java/org/sonar/runner/api/PrintStreamConsumerTest.java View File

@@ -0,0 +1,42 @@
/*
* Sonar Runner - API
* 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.api;

import org.junit.Test;

import java.io.PrintStream;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

public class PrintStreamConsumerTest {
@Test
public void consumeLine() {
PrintStream stream = mock(PrintStream.class);
PrintStreamConsumer consumer = new PrintStreamConsumer(stream);
consumer.consumeLine("foo");
consumer.consumeLine("bar");

verify(stream).println("foo");
verify(stream).println("bar");
verifyNoMoreInteractions(stream);
}
}

sonar-runner-api/src/test/java/org/sonar/runner/VersionTest.java → sonar-runner-api/src/test/java/org/sonar/runner/api/RunnerVersionTest.java View File

@@ -17,20 +17,18 @@
* 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;
package org.sonar.runner.api;

import org.junit.Test;
import org.sonar.runner.Version;

import static org.fest.assertions.Assertions.assertThat;

public class VersionTest {
public class RunnerVersionTest {

@Test
public void shouldLoadVersion() {
String version = Version.getVersion();
assertThat(version).contains(".");
assertThat(version).doesNotContain("$");
public void should_load_version() {
String version = RunnerVersion.version();
assertThat(version).isNotEmpty().contains(".").endsWith("-SNAPSHOT").doesNotContain("$");
}

}

+ 33
- 0
sonar-runner-api/src/test/java/org/sonar/runner/api/UtilsTest.java View File

@@ -0,0 +1,33 @@
/*
* Sonar Runner - API
* 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.api;

import org.junit.Test;

import static org.fest.assertions.Assertions.assertThat;

public class UtilsTest {
@Test
public void should_join_strings() {
assertThat(Utils.join(new String[]{}, ",")).isEqualTo("");
assertThat(Utils.join(new String[]{"foo"}, ",")).isEqualTo("foo");
assertThat(Utils.join(new String[]{"foo", "bar"}, ",")).isEqualTo("foo,bar");
}
}

+ 4
- 0
sonar-runner-api/src/test/scripts/echo.bat View File

@@ -0,0 +1,4 @@
@ECHO OFF
@ECHO %CD% > echo.log
@ECHO Parameter: %1 >> echo.log
@ECHO Environment variable: %ENVVAR% >> echo.log

+ 6
- 0
sonar-runner-api/src/test/scripts/echo.sh View File

@@ -0,0 +1,6 @@
#!/bin/sh

WORKING_DIR=`pwd`
echo $WORKING_DIR > echo.log
echo "Parameter: $1" >> echo.log
echo "Environment variable: $ENVVAR" >> echo.log

+ 6
- 0
sonar-runner-api/src/test/scripts/forever.bat View File

@@ -0,0 +1,6 @@
@ECHO OFF

:LOOP
@rem Next line may lead to freeze of build process on Windows 7 due to non-terminated ping-processes
@rem ping 1.1.1.1 -n 2 -w 60000 > nul
GOTO LOOP

+ 6
- 0
sonar-runner-api/src/test/scripts/forever.sh View File

@@ -0,0 +1,6 @@
#!/bin/sh

while test "notempty"
do
sleep 1
done

+ 5
- 0
sonar-runner-api/src/test/scripts/output.bat View File

@@ -0,0 +1,5 @@
@ECHO OFF
@ECHO stdOut: first line
@ECHO stdOut: second line
@ECHO stdErr: first line 1>&2
@ECHO stdErr: second line 1>&2

+ 6
- 0
sonar-runner-api/src/test/scripts/output.sh View File

@@ -0,0 +1,6 @@
#!/bin/sh

echo stdOut: first line
echo stdOut: second line
echo stdErr: first line 1>&2
echo stdErr: second line 1>&2

+ 67
- 0
sonar-runner-batch/pom.xml View File

@@ -0,0 +1,67 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.codehaus.sonar.runner</groupId>
<artifactId>sonar-runner</artifactId>
<version>2.2-SNAPSHOT</version>
</parent>

<artifactId>sonar-runner-batch</artifactId>
<name>Sonar Runner - Batch</name>

<properties>
<sonarBatchVersion>3.0</sonarBatchVersion>
</properties>

<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.15</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-batch</artifactId>
<version>${sonarBatchVersion}</version>
<scope>provided</scope>
</dependency>

<!-- Unit tests -->
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-testing-harness</artifactId>
<version>${sonarBatchVersion}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>commons-configuration</artifactId>
<groupId>commons-configuration</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

+ 99
- 0
sonar-runner-batch/src/main/java/org/sonar/runner/batch/IsolatedLauncher.java View File

@@ -0,0 +1,99 @@
/*
* Sonar Runner - Batch
* 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.batch;

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.io.IOUtils;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.bootstrapper.Batch;
import org.sonar.batch.bootstrapper.EnvironmentInformation;

import java.io.InputStream;
import java.util.List;
import java.util.Properties;

/**
* This class is executed within the classloader provided by the server. It contains the installed plugins and
* the same version of sonar-batch as the server.
*/
public class IsolatedLauncher {

public void execute(Properties properties, List<Object> extensions) {
String task = properties.getProperty("sonar.task");
ProjectReactor projectReactor = null;
if (task == null || "scan".equals(task)) {
Properties propsClone = new Properties();
propsClone.putAll(properties);
projectReactor = new ProjectReactorBuilder(propsClone).build();
}
initLogging(properties);
EnvironmentInformation env = new EnvironmentInformation(properties.getProperty("sonarRunner.userAgent"), properties.getProperty("sonarRunner.userAgentVersion"));
Batch.Builder builder = Batch.builder()
.setEnvironment(env)
.addComponents(extensions);

if (projectReactor != null) {
builder.setProjectReactor(projectReactor);
}
builder.build().execute();
}

private void initLogging(Properties props) {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator jc = new JoranConfigurator();
jc.setContext(context);
context.reset();
InputStream input = Batch.class.getResourceAsStream("/org/sonar/batch/logback.xml");
System.setProperty("ROOT_LOGGER_LEVEL", isDebug(props) ? "DEBUG" : "INFO");
context.putProperty("SQL_LOGGER_LEVEL", getSqlLevel(props));
context.putProperty("SQL_RESULTS_LOGGER_LEVEL", getSqlResultsLevel(props));
try {
jc.doConfigure(input);

} catch (JoranException e) {
throw new SonarException("can not initialize logging", e);

} finally {
IOUtils.closeQuietly(input);
}
}

@VisibleForTesting
protected boolean isDebug(Properties props) {
return Boolean.parseBoolean(props.getProperty("sonar.verbose", "false"));
}

@VisibleForTesting
protected static String getSqlLevel(Properties props) {
boolean showSql = "true".equals(props.getProperty("sonar.showSql", "false"));
return showSql ? "DEBUG" : "WARN";
}

@VisibleForTesting
protected static String getSqlResultsLevel(Properties props) {
boolean showSql = "true".equals(props.getProperty("sonar.showSqlResults", "false"));
return showSql ? "DEBUG" : "WARN";
}
}

sonar-runner-impl/src/main/java/org/sonar/runner/internal/batch/SonarProjectBuilder.java → sonar-runner-batch/src/main/java/org/sonar/runner/batch/ProjectReactorBuilder.java View File

@@ -1,5 +1,5 @@
/*
* Sonar Runner - Implementation
* Sonar Runner - Batch
* Copyright (C) 2011 SonarSource
* dev@sonar.codehaus.org
*
@@ -17,7 +17,7 @@
* 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.internal.batch;
package org.sonar.runner.batch;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
@@ -29,7 +29,7 @@ import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.runner.RunnerException;
import org.sonar.api.batch.bootstrap.ProjectReactor;

import java.io.File;
import java.io.FileFilter;
@@ -46,9 +46,9 @@ import java.util.Properties;
*
* @since 1.5
*/
public final class SonarProjectBuilder {
public final class ProjectReactorBuilder {

private static final Logger LOG = LoggerFactory.getLogger(SonarProjectBuilder.class);
private static final Logger LOG = LoggerFactory.getLogger(ProjectReactorBuilder.class);

private static final String PROPERTY_PROJECT_BASEDIR = "sonar.projectBaseDir";
private static final String PROPERTY_PROJECT_CONFIG_FILE = "sonar.projectConfigFile";
@@ -110,47 +110,19 @@ public final class SonarProjectBuilder {
*/
private static final List<String> NON_HERITED_PROPERTIES_FOR_CHILD = Lists.newArrayList(PROPERTY_PROJECT_BASEDIR, PROPERTY_MODULES, PROPERTY_PROJECT_DESCRIPTION);

private String command;
private Properties properties;
private File rootProjectWorkDir;

private SonarProjectBuilder(String command, Properties properties) {
this.command = command;
public ProjectReactorBuilder(Properties properties) {
this.properties = properties;
}

public static SonarProjectBuilder create(Properties properties) {
return create(null, properties);
}

public static SonarProjectBuilder create(String command, Properties properties) {
return new SonarProjectBuilder(command, properties);
}

public ProjectDefinition generateProjectDefinition() {
if (StringUtils.isBlank(command) || "scan".equals(command)) {
ProjectDefinition rootProject = defineProject(properties, null);
rootProjectWorkDir = rootProject.getWorkDir();
defineChildren(rootProject);
cleanAndCheckProjectDefinitions(rootProject);
return rootProject;
}
else if (properties.containsKey(PROPERTY_PROJECT_KEY)) {
return defineTaskContext();
}
else {
return null;
}
}

private ProjectDefinition defineTaskContext() {
File baseDir = new File(System.getProperty("user.home"));
File workDir = initRootProjectWorkDir(baseDir);

ProjectDefinition definition = ProjectDefinition.create().setProperties(properties)
.setBaseDir(baseDir)
.setWorkDir(workDir);
return definition;
public ProjectReactor build() {
ProjectDefinition rootProject = defineProject(properties, null);
rootProjectWorkDir = rootProject.getWorkDir();
defineChildren(rootProject);
cleanAndCheckProjectDefinitions(rootProject);
return new ProjectReactor(rootProject);
}

private ProjectDefinition defineProject(Properties properties, ProjectDefinition parent) {
@@ -161,7 +133,7 @@ public final class SonarProjectBuilder {
else {
checkMandatoryProperties(properties, MANDATORY_PROPERTIES_FOR_SIMPLE_PROJECT);
}
File workDir = null;
File workDir;
if (parent == null) {
validateDirectories(properties, baseDir, properties.getProperty(PROPERTY_PROJECT_KEY));
workDir = initRootProjectWorkDir(baseDir);
@@ -199,7 +171,7 @@ public final class SonarProjectBuilder {
private void defineChildren(ProjectDefinition parentProject) {
Properties parentProps = parentProject.getProperties();
if (parentProps.containsKey(PROPERTY_MODULES)) {
for (String module : SonarRunnerUtils.getListFromProperty(parentProps, PROPERTY_MODULES)) {
for (String module : Utils.getListFromProperty(parentProps, PROPERTY_MODULES)) {
Properties moduleProps = extractModuleProperties(module, parentProps);
ProjectDefinition childProject = loadChildProject(parentProject, moduleProps, module);
// check the uniqueness of the child key
@@ -224,7 +196,7 @@ public final class SonarProjectBuilder {
tryToFindAndLoadPropsFile(baseDir, moduleProps, moduleId);
}
} catch (IOException e) {
throw new RunnerException("Error when resolving baseDir", e);
throw new IllegalStateException("Error when resolving baseDir", e);
}
} else if (moduleProps.containsKey(PROPERTY_PROJECT_CONFIG_FILE)) {
baseDir = loadPropsFile(parentProject, moduleProps, moduleId);
@@ -264,7 +236,7 @@ public final class SonarProjectBuilder {
setProjectBaseDir(baseDir, moduleProps, moduleId);
return baseDir;
} else {
throw new RunnerException("The properties file of the module '" + moduleId + "' does not exist: " + propertyFile.getAbsolutePath());
throw new IllegalStateException("The properties file of the module '" + moduleId + "' does not exist: " + propertyFile.getAbsolutePath());
}
}

@@ -290,7 +262,7 @@ public final class SonarProjectBuilder {
fileInputStream = new FileInputStream(propertyFile);
propsFromFile.load(fileInputStream);
} catch (IOException e) {
throw new RunnerException("Impossible to read the property file: " + propertyFile.getAbsolutePath(), e);
throw new IllegalStateException("Impossible to read the property file: " + propertyFile.getAbsolutePath(), e);
} finally {
IOUtils.closeQuietly(fileInputStream);
}
@@ -311,7 +283,7 @@ public final class SonarProjectBuilder {
protected static void checkUniquenessOfChildKey(ProjectDefinition childProject, ProjectDefinition parentProject) {
for (ProjectDefinition definition : parentProject.getSubProjects()) {
if (definition.getKey().equals(childProject.getKey())) {
throw new RunnerException("Project '" + parentProject.getKey() + "' can't have 2 modules with the following key: " + childProject.getKey());
throw new IllegalStateException("Project '" + parentProject.getKey() + "' can't have 2 modules with the following key: " + childProject.getKey());
}
}
}
@@ -324,7 +296,7 @@ public final class SonarProjectBuilder {

private static void setProjectBaseDir(File baseDir, Properties childProps, String moduleId) {
if (!baseDir.isDirectory()) {
throw new RunnerException("The base directory of the module '" + moduleId + "' does not exist: " + baseDir.getAbsolutePath());
throw new IllegalStateException("The base directory of the module '" + moduleId + "' does not exist: " + baseDir.getAbsolutePath());
}
childProps.put(PROPERTY_PROJECT_BASEDIR, baseDir.getAbsolutePath());
}
@@ -343,29 +315,29 @@ public final class SonarProjectBuilder {
}
String projectKey = props.getProperty(PROPERTY_PROJECT_KEY);
if (missing.length() != 0) {
throw new RunnerException("You must define the following mandatory properties for '" + (projectKey == null ? "Unknown" : projectKey) + "': " + missing);
throw new IllegalStateException("You must define the following mandatory properties for '" + (projectKey == null ? "Unknown" : projectKey) + "': " + missing);
}
}

private static void validateDirectories(Properties props, File baseDir, String projectId) {
if (!props.containsKey(PROPERTY_MODULES)) {
// SONARPLUGINS-2285 Not an aggreator project so we can validate that paths are correct if defined
// SONARPLUGINS-2285 Not an aggregator project so we can validate that paths are correct if defined

// We need to resolve patterns that may have been used in "sonar.libraries"
for (String pattern : SonarRunnerUtils.getListFromProperty(props, PROPERTY_LIBRARIES)) {
for (String pattern : Utils.getListFromProperty(props, PROPERTY_LIBRARIES)) {
File[] files = getLibraries(baseDir, pattern);
if (files == null || files.length == 0) {
LOG.error("Invalid value of " + PROPERTY_LIBRARIES + " for " + projectId);
throw new RunnerException("No file matching pattern \"" + pattern + "\" in directory \"" + baseDir + "\"");
throw new IllegalStateException("No file matching pattern \"" + pattern + "\" in directory \"" + baseDir + "\"");
}
}

// Check sonar.tests
String[] testDirs = SonarRunnerUtils.getListFromProperty(props, PROPERTY_TESTS);
String[] testDirs = Utils.getListFromProperty(props, PROPERTY_TESTS);
checkExistenceOfDirectories(projectId, baseDir, testDirs, PROPERTY_TESTS);

// Check sonar.binaries
String[] binDirs = SonarRunnerUtils.getListFromProperty(props, PROPERTY_BINARIES);
String[] binDirs = Utils.getListFromProperty(props, PROPERTY_BINARIES);
checkExistenceOfDirectories(projectId, baseDir, binDirs, PROPERTY_BINARIES);
}
}
@@ -389,12 +361,12 @@ public final class SonarProjectBuilder {
Properties properties = project.getProperties();

// We need to check the existence of source directories
String[] sourceDirs = SonarRunnerUtils.getListFromProperty(properties, PROPERTY_SOURCES);
String[] sourceDirs = Utils.getListFromProperty(properties, PROPERTY_SOURCES);
checkExistenceOfDirectories(project.getKey(), project.getBaseDir(), sourceDirs, PROPERTY_SOURCES);

// And we need to resolve patterns that may have been used in "sonar.libraries"
List<String> libPaths = Lists.newArrayList();
for (String pattern : SonarRunnerUtils.getListFromProperty(properties, PROPERTY_LIBRARIES)) {
for (String pattern : Utils.getListFromProperty(properties, PROPERTY_LIBRARIES)) {
for (File file : getLibraries(project.getBaseDir(), pattern)) {
libPaths.add(file.getAbsolutePath());
}
@@ -408,7 +380,7 @@ public final class SonarProjectBuilder {
Properties properties = project.getProperties();

// SONARPLUGINS-2295
String[] sourceDirs = SonarRunnerUtils.getListFromProperty(properties, PROPERTY_SOURCES);
String[] sourceDirs = Utils.getListFromProperty(properties, PROPERTY_SOURCES);
for (String path : sourceDirs) {
File sourceFolder = getFileFromPath(path, project.getBaseDir());
if (sourceFolder.isDirectory()) {
@@ -426,7 +398,7 @@ public final class SonarProjectBuilder {

// and they don't need properties related to their modules either
Properties clone = (Properties) properties.clone();
List<String> moduleIds = Lists.newArrayList(SonarRunnerUtils.getListFromProperty(properties, PROPERTY_MODULES));
List<String> moduleIds = Lists.newArrayList(Utils.getListFromProperty(properties, PROPERTY_MODULES));
for (Entry<Object, Object> entry : clone.entrySet()) {
String key = (String) entry.getKey();
if (isKeyPrefixedByModuleId(key, moduleIds)) {
@@ -455,7 +427,7 @@ public final class SonarProjectBuilder {

@VisibleForTesting
protected static void mergeParentProperties(Properties childProps, Properties parentProps) {
List<String> moduleIds = Lists.newArrayList(SonarRunnerUtils.getListFromProperty(parentProps, PROPERTY_MODULES));
List<String> moduleIds = Lists.newArrayList(Utils.getListFromProperty(parentProps, PROPERTY_MODULES));
for (Map.Entry<Object, Object> entry : parentProps.entrySet()) {
String key = (String) entry.getKey();
if (!childProps.containsKey(key)
@@ -495,7 +467,7 @@ public final class SonarProjectBuilder {
File sourceFolder = getFileFromPath(path, baseDir);
if (!sourceFolder.isDirectory()) {
LOG.error("Invalid value of " + propName + " for " + moduleRef);
throw new RunnerException("The folder '" + path + "' does not exist for '" + moduleRef +
throw new IllegalStateException("The folder '" + path + "' does not exist for '" + moduleRef +
"' (base directory = " + baseDir.getAbsolutePath() + ")");
}
}
@@ -531,7 +503,7 @@ public final class SonarProjectBuilder {
try {
file = new File(baseDir, path).getCanonicalFile();
} catch (IOException e) {
throw new RunnerException("Unable to resolve path \"" + path + "\"", e);
throw new IllegalStateException("Unable to resolve path \"" + path + "\"", e);
}
}
return file;

sonar-runner-impl/src/main/java/org/sonar/runner/internal/batch/SonarRunnerUtils.java → sonar-runner-batch/src/main/java/org/sonar/runner/batch/Utils.java View File

@@ -1,5 +1,5 @@
/*
* Sonar Runner - Implementation
* Sonar Runner - Batch
* Copyright (C) 2011 SonarSource
* dev@sonar.codehaus.org
*
@@ -17,7 +17,7 @@
* 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.internal.batch;
package org.sonar.runner.batch;

import org.apache.commons.lang.StringUtils;

@@ -26,9 +26,9 @@ import java.util.Properties;
/**
* Public utility that can be used by consumers of the Sonar Runner.
*/
public final class SonarRunnerUtils {
class Utils {

private SonarRunnerUtils() {
private Utils() {
// only static methods
}


sonar-runner-impl/src/main/java/org/sonar/runner/internal/batch/package-info.java → sonar-runner-batch/src/main/java/org/sonar/runner/batch/package-info.java View File

@@ -1,5 +1,5 @@
/*
* Sonar Runner - Implementation
* Sonar Runner - Batch
* Copyright (C) 2011 SonarSource
* dev@sonar.codehaus.org
*
@@ -21,4 +21,7 @@
* Internal package that creates the project definition and launches the analyses based on it.
* Should not be used by consumers.
*/
package org.sonar.runner.internal.batch;
@ParametersAreNonnullByDefault
package org.sonar.runner.batch;

import javax.annotation.ParametersAreNonnullByDefault;

+ 74
- 0
sonar-runner-batch/src/test/java/org/sonar/runner/batch/LauncherTest.java View File

@@ -0,0 +1,74 @@
/*
* Sonar Runner - Batch
* 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.batch;
//
//import com.google.common.collect.Lists;
//import org.junit.Test;
//
//import java.util.Properties;
//
//import static org.fest.assertions.Assertions.assertThat;
//
//public class LauncherTest {
//
// @Test
// public void testGetSqlLevel() throws Exception {
// Properties conf = new Properties();
//
// assertThat(Launcher.getSqlLevel(conf)).isEqualTo("WARN");
//
// conf.setProperty("sonar.showSql", "true");
// assertThat(Launcher.getSqlLevel(conf)).isEqualTo("DEBUG");
//
// conf.setProperty("sonar.showSql", "false");
// assertThat(Launcher.getSqlLevel(conf)).isEqualTo("WARN");
// }
//
// @Test
// public void testGetSqlResultsLevel() throws Exception {
// Properties conf = new Properties();
//
// assertThat(Launcher.getSqlResultsLevel(conf)).isEqualTo("WARN");
//
// conf.setProperty("sonar.showSqlResults", "true");
// assertThat(Launcher.getSqlResultsLevel(conf)).isEqualTo("DEBUG");
//
// conf.setProperty("sonar.showSqlResults", "false");
// assertThat(Launcher.getSqlResultsLevel(conf)).isEqualTo("WARN");
// }
//
// @Test
// public void shouldDetermineVerboseMode() {
// Properties properties = new Properties();
// Launcher launcher = new Launcher(properties, Lists.newArrayList());
// assertThat(launcher.isDebug()).isFalse();
// properties.setProperty("sonar.verbose", "true");
// assertThat(launcher.isDebug()).isTrue();
// }
//
// @Test
// public void shouldSupportDeprecatedDebugProperty() {
// Properties properties = new Properties();
// Launcher launcher = new Launcher(properties, Lists.newArrayList());
// properties.setProperty("runner.debug", "true");
// assertThat(launcher.isDebug()).isTrue();
// }
//
//}

sonar-runner-impl/src/test/java/org/sonar/runner/internal/batch/SonarProjectBuilderTest.java → sonar-runner-batch/src/test/java/org/sonar/runner/batch/ProjectReactorBuilderTest.java View File

@@ -1,5 +1,5 @@
/*
* Sonar Runner - Implementation
* Sonar Runner - Batch
* Copyright (C) 2011 SonarSource
* dev@sonar.codehaus.org
*
@@ -17,13 +17,13 @@
* 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.internal.batch;
package org.sonar.runner.batch;

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.api.batch.bootstrap.ProjectReactor;
import org.sonar.test.TestUtils;

import java.io.File;
@@ -34,7 +34,7 @@ import java.util.Properties;

import static org.fest.assertions.Assertions.assertThat;

public class SonarProjectBuilderTest {
public class ProjectReactorBuilderTest {

@Rule
public ExpectedException thrown = ExpectedException.none();
@@ -64,9 +64,9 @@ public class SonarProjectBuilderTest {

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

loadProjectDefinition("simple-project-with-unexisting-source-dir");
}
@@ -302,28 +302,28 @@ public class SonarProjectBuilderTest {

@Test
public void shouldFailIfUnexistingModuleBaseDir() throws IOException {
thrown.expect(RunnerException.class);
thrown.expect(IllegalStateException.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");
+ 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.expect(IllegalStateException.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");
+ 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 shouldFailIfUnexistingSourceFolderInheritedInMultimodule() throws IOException {
thrown.expect(RunnerException.class);
thrown.expect(IllegalStateException.class);
thrown.expectMessage("The folder 'unexisting-source-dir' does not exist for 'com.foo.project:module1' (base directory = "
+ TestUtils.getResource(this.getClass(), "multi-module-with-unexisting-source-dir").getAbsolutePath() + File.separator + "module1)");
+ TestUtils.getResource(this.getClass(), "multi-module-with-unexisting-source-dir").getAbsolutePath() + File.separator + "module1)");

loadProjectDefinition("multi-module-with-unexisting-source-dir");
}
@@ -335,54 +335,54 @@ public class SonarProjectBuilderTest {

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

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

@Test
public void shouldFailIfExplicitUnexistingBinaryFolder() throws IOException {
thrown.expect(RunnerException.class);
thrown.expect(IllegalStateException.class);
thrown.expectMessage("The folder 'bin' does not exist for 'com.foo.project' (base directory = "
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-binary").getAbsolutePath());
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-binary").getAbsolutePath());

loadProjectDefinition("simple-project-with-unexisting-binary");
}

@Test
public void shouldFailIfExplicitUnmatchingLibFolder() throws IOException {
thrown.expect(RunnerException.class);
thrown.expect(IllegalStateException.class);
thrown.expectMessage("No file matching pattern \"libs/*.txt\" in directory \""
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-lib").getAbsolutePath());
+ TestUtils.getResource(this.getClass(), "simple-project-with-unexisting-lib").getAbsolutePath());

loadProjectDefinition("simple-project-with-unexisting-lib");
}

@Test
public void shouldFailIfExplicitUnexistingTestFolderOnModule() throws IOException {
thrown.expect(RunnerException.class);
thrown.expect(IllegalStateException.class);
thrown.expectMessage("The folder 'tests' does not exist for 'module1' (base directory = "
+ TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-test-dir").getAbsolutePath() + File.separator + "module1)");
+ TestUtils.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 shouldFailIfExplicitUnexistingBinaryFolderOnModule() throws IOException {
thrown.expect(RunnerException.class);
thrown.expect(IllegalStateException.class);
thrown.expectMessage("The folder 'bin' does not exist for 'module1' (base directory = "
+ TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-binary-dir").getAbsolutePath() + File.separator + "module1)");
+ TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-binary-dir").getAbsolutePath() + File.separator + "module1)");

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

@Test
public void shouldFailIfExplicitUnmatchingLibFolderOnModule() throws IOException {
thrown.expect(RunnerException.class);
thrown.expect(IllegalStateException.class);
thrown.expectMessage("No file matching pattern \"lib/*.jar\" in directory \""
+ TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-lib").getAbsolutePath() + File.separator + "module1\"");
+ TestUtils.getResource(this.getClass(), "multi-module-with-explicit-unexisting-lib").getAbsolutePath() + File.separator + "module1\"");

loadProjectDefinition("multi-module-with-explicit-unexisting-lib");
}
@@ -396,14 +396,14 @@ public class SonarProjectBuilderTest {
props.setProperty("foobar.tests", "src/test/java");
props.setProperty("foobar.binaries", "target/classes");

Properties moduleProps = SonarProjectBuilder.extractModuleProperties("bar", props);
Properties moduleProps = ProjectReactorBuilder.extractModuleProperties("bar", props);
assertThat(moduleProps.size()).isEqualTo(0);

moduleProps = SonarProjectBuilder.extractModuleProperties("foo", props);
moduleProps = ProjectReactorBuilder.extractModuleProperties("foo", props);
assertThat(moduleProps.size()).isEqualTo(1);
assertThat(moduleProps.get("sources")).isEqualTo("src/main/java");

moduleProps = SonarProjectBuilder.extractModuleProperties("foobar", props);
moduleProps = ProjectReactorBuilder.extractModuleProperties("foobar", props);
assertThat(moduleProps.size()).isEqualTo(2);
assertThat(moduleProps.get("tests")).isEqualTo("src/test/java");
assertThat(moduleProps.get("binaries")).isEqualTo("target/classes");
@@ -415,10 +415,10 @@ public class SonarProjectBuilderTest {
props.setProperty("foo1", "bla");
props.setProperty("foo4", "bla");

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

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

@Test
@@ -427,10 +427,10 @@ public class SonarProjectBuilderTest {
props.setProperty("foo1", "bla");
props.setProperty("sonar.projectKey", "my-project");

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

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

@Test
@@ -439,7 +439,7 @@ public class SonarProjectBuilderTest {
props.setProperty("foo1", "bla");
props.setProperty("foo4", "bla");

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

// No exception should be thrown
}
@@ -447,22 +447,22 @@ public class SonarProjectBuilderTest {
@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);
assertThat(ProjectReactorBuilder.getLibraries(baseDir, "in*.txt").length).isEqualTo(1);
assertThat(ProjectReactorBuilder.getLibraries(baseDir, "*.txt").length).isEqualTo(2);
assertThat(ProjectReactorBuilder.getLibraries(baseDir.getParentFile(), "shouldFilterFiles/in*.txt").length).isEqualTo(1);
assertThat(ProjectReactorBuilder.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);
assertThat(ProjectReactorBuilder.getLibraries(baseDir.getParentFile(), absolutePattern).length).isEqualTo(1);
}

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

@@ -470,7 +470,7 @@ public class SonarProjectBuilderTest {
public void shouldGetAbsoluteFile() {
File file = TestUtils.getResource(this.getClass(), "shouldGetFile/foo.properties");

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

@@ -488,7 +488,7 @@ public class SonarProjectBuilderTest {
childProps.setProperty("existingChildProp", "barChild");
childProps.setProperty("otherProp", "tutuChild");

SonarProjectBuilder.mergeParentProperties(childProps, parentProps);
ProjectReactorBuilder.mergeParentProperties(childProps, parentProps);

assertThat(childProps.size()).isEqualTo(3);
assertThat(childProps.getProperty("toBeMergeProps")).isEqualTo("fooParent");
@@ -502,7 +502,7 @@ public class SonarProjectBuilderTest {

@Test
public void shouldInitRootWorkDir() {
SonarProjectBuilder builder = SonarProjectBuilder.create(new Properties());
ProjectReactorBuilder builder = new ProjectReactorBuilder(new Properties());
File baseDir = new File("target/tmp/baseDir");

File workDir = builder.initRootProjectWorkDir(baseDir);
@@ -514,7 +514,7 @@ public class SonarProjectBuilderTest {
public void shouldInitWorkDirWithCustomRelativeFolder() {
Properties properties = new Properties();
properties.put("sonar.working.directory", ".foo");
SonarProjectBuilder builder = SonarProjectBuilder.create(properties);
ProjectReactorBuilder builder = new ProjectReactorBuilder(properties);
File baseDir = new File("target/tmp/baseDir");

File workDir = builder.initRootProjectWorkDir(baseDir);
@@ -526,7 +526,7 @@ public class SonarProjectBuilderTest {
public void shouldInitRootWorkDirWithCustomAbsoluteFolder() {
Properties properties = new Properties();
properties.put("sonar.working.directory", new File("src").getAbsolutePath());
SonarProjectBuilder builder = SonarProjectBuilder.create(properties);
ProjectReactorBuilder builder = new ProjectReactorBuilder(properties);
File baseDir = new File("target/tmp/baseDir");

File workDir = builder.initRootProjectWorkDir(baseDir);
@@ -539,7 +539,7 @@ public class SonarProjectBuilderTest {
Properties props = new Properties();
props.put("sonar.projectKey", "my-module-key");

SonarProjectBuilder.prefixProjectKeyWithParentKey(props, "my-parent-key");
ProjectReactorBuilder.prefixProjectKeyWithParentKey(props, "my-parent-key");
assertThat(props.getProperty("sonar.projectKey")).isEqualTo("my-parent-key:my-module-key");
}

@@ -557,15 +557,15 @@ public class SonarProjectBuilderTest {
Properties props2 = new Properties();
props2.put("sonar.projectKey", "mod2");
ProjectDefinition mod2 = ProjectDefinition.create().setProperties(props2);
SonarProjectBuilder.checkUniquenessOfChildKey(mod2, root);
ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root);

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

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

SonarProjectBuilder.checkUniquenessOfChildKey(mod2, root);
ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root);
}

@Test
@@ -574,30 +574,29 @@ public class SonarProjectBuilderTest {
props.put("sonar.projectVersion", "1.0");

// should be set
SonarProjectBuilder.setProjectKeyAndNameIfNotDefined(props, "foo");
ProjectReactorBuilder.setProjectKeyAndNameIfNotDefined(props, "foo");
assertThat(props.getProperty("sonar.projectKey")).isEqualTo("foo");
assertThat(props.getProperty("sonar.projectName")).isEqualTo("foo");

// but not this 2nd time
SonarProjectBuilder.setProjectKeyAndNameIfNotDefined(props, "bar");
ProjectReactorBuilder.setProjectKeyAndNameIfNotDefined(props, "bar");
assertThat(props.getProperty("sonar.projectKey")).isEqualTo("foo");
assertThat(props.getProperty("sonar.projectName")).isEqualTo("foo");
}

@Test
public void shouldFailToLoadPropertiesFile() throws Exception {
thrown.expect(RunnerException.class);
thrown.expect(IllegalStateException.class);
thrown.expectMessage("Impossible to read the property file");

SonarProjectBuilder.toProperties(new File("foo.properties"));
ProjectReactorBuilder.toProperties(new File("foo.properties"));
}

private ProjectDefinition loadProjectDefinition(String projectFolder) throws FileNotFoundException, IOException {
Properties props = SonarProjectBuilder.toProperties(TestUtils.getResource(this.getClass(), projectFolder + "/sonar-project.properties"));
private ProjectDefinition loadProjectDefinition(String projectFolder) throws IOException {
Properties props = ProjectReactorBuilder.toProperties(TestUtils.getResource(this.getClass(), projectFolder + "/sonar-project.properties"));
props.put("sonar.projectBaseDir", TestUtils.getResource(this.getClass(), projectFolder).getAbsolutePath());
ProjectDefinition projectDefinition = SonarProjectBuilder.create(props)
.generateProjectDefinition();
return projectDefinition;
ProjectReactor projectReactor = new ProjectReactorBuilder(props).build();
return projectReactor.getRoot();
}

}

sonar-runner-impl/src/test/java/org/sonar/runner/internal/batch/SonarRunnerUtilsTest.java → sonar-runner-batch/src/test/java/org/sonar/runner/batch/UtilsTest.java View File

@@ -1,5 +1,5 @@
/*
* Sonar Runner - Implementation
* Sonar Runner - Batch
* Copyright (C) 2011 SonarSource
* dev@sonar.codehaus.org
*
@@ -17,7 +17,7 @@
* 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.internal.batch;
package org.sonar.runner.batch;

import org.apache.commons.io.IOUtils;
import org.junit.Test;
@@ -29,22 +29,31 @@ import java.util.Properties;

import static org.fest.assertions.Assertions.assertThat;

public class SonarRunnerUtilsTest {
public class UtilsTest {

@Test
public void shouldGetList() {
Properties props = new Properties();

props.put("prop", " foo , bar , \n\ntoto,tutu");
assertThat(SonarRunnerUtils.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu");
assertThat(Utils.getListFromProperty(props, "prop")).containsOnly("foo", "bar", "toto", "tutu");
}
//
// @Test
// public void test_props() {
// Properties p1 = new Properties();
// p1.setProperty("foo", "bar");
// Properties p2 = new Properties();
// p2.putAll(p1);
// assertThat(p2.getProperty("foo")).isEqualTo("bar");
// }

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

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

private Properties loadPropsFromFile(String filePath) throws IOException {

sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-language-definitions-all-in-root/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-language-definitions-all-in-root/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-language-definitions-all-in-root/src/main/groovy/Fake.groovy → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-language-definitions-all-in-root/src/main/groovy/Fake.groovy View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-language-definitions-all-in-root/src/main/java/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-language-definitions-all-in-root/src/main/java/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-all-in-root/module1/sources/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-all-in-root/module1/sources/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-all-in-root/module2/src/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-all-in-root/module2/src/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-all-in-root/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-all-in-root/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-in-each-module-inherited/module1/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module-inherited/module1/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-in-each-module-inherited/module1/sources/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module-inherited/module1/sources/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-in-each-module-inherited/module2/newBaseDir/src/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module-inherited/module2/newBaseDir/src/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-in-each-module-inherited/module2/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module-inherited/module2/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-in-each-module-inherited/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module-inherited/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-in-each-module/module1/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module/module1/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-in-each-module/module1/sources/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module/module1/sources/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-in-each-module/module2/newBaseDir/src/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module/module2/newBaseDir/src/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-in-each-module/module2/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module/module2/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-definitions-in-each-module/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-definitions-in-each-module/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-basedir/modules/module1/sources/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-basedir/modules/module1/sources/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-basedir/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-basedir/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-configfile-and-overwritten-basedir/any-folder/generated/any-file.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile-and-overwritten-basedir/any-folder/generated/any-file.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-configfile-and-overwritten-basedir/any-folder/sources/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile-and-overwritten-basedir/any-folder/sources/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-configfile-and-overwritten-basedir/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile-and-overwritten-basedir/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-configfile/any-folder/any-file.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile/any-folder/any-file.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-configfile/any-folder/sources/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile/any-folder/sources/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-configfile/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-configfile/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-explicit-unexisting-binary-dir/module1/src/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-binary-dir/module1/src/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-explicit-unexisting-binary-dir/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-binary-dir/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-explicit-unexisting-lib/module1/src/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-lib/module1/src/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-explicit-unexisting-lib/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-lib/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-explicit-unexisting-test-dir/module1/src/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-test-dir/module1/src/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-explicit-unexisting-test-dir/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-explicit-unexisting-test-dir/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-unexisting-basedir/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-basedir/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-unexisting-file/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-file/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-unexisting-source-dir/module1/src/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-source-dir/module1/src/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-unexisting-source-dir/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-source-dir/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-unexisting-test-bin-lib-dir/module1/src/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-test-bin-lib-dir/module1/src/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/multi-module-with-unexisting-test-bin-lib-dir/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/multi-module-with-unexisting-test-bin-lib-dir/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/shouldFilterFiles/exclude.txt → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/shouldFilterFiles/exclude.txt View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/shouldFilterFiles/include.txt → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/shouldFilterFiles/include.txt View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/shouldGetFile/foo.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/shouldGetFile/foo.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project-with-deprecated-props/libs/lib1.txt → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-deprecated-props/libs/lib1.txt View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project-with-deprecated-props/libs/lib2.txt → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-deprecated-props/libs/lib2.txt View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project-with-deprecated-props/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-deprecated-props/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project-with-deprecated-props/sources/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-deprecated-props/sources/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project-with-unexisting-binary/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-binary/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project-with-unexisting-binary/sources/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-binary/sources/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project-with-unexisting-lib/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-lib/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project-with-unexisting-lib/sources/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-lib/sources/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project-with-unexisting-source-dir/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-source-dir/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project-with-unexisting-test-dir/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-test-dir/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project-with-unexisting-test-dir/sources/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project-with-unexisting-test-dir/sources/Fake.java View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project/libs/lib1.txt → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project/libs/lib1.txt View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project/libs/lib2.txt → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project/libs/lib2.txt View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project/sonar-project.properties → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project/sonar-project.properties View File


sonar-runner-impl/src/test/resources/org/sonar/runner/internal/batch/SonarProjectBuilderTest/simple-project/sources/Fake.java → sonar-runner-batch/src/test/resources/org/sonar/runner/batch/ProjectReactorBuilderTest/simple-project/sources/Fake.java View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save