]> source.dussan.org Git - sonar-scanner-cli.git/commitdiff
SONARPLUGINS-2574 set env variables + improve unit tests
authorSimon Brandhof <simon.brandhof@gmail.com>
Thu, 4 Apr 2013 20:45:26 +0000 (22:45 +0200)
committerSimon Brandhof <simon.brandhof@gmail.com>
Thu, 4 Apr 2013 20:45:26 +0000 (22:45 +0200)
sonar-runner-api/src/main/java/org/sonar/runner/api/EmbeddedRunner.java
sonar-runner-api/src/main/java/org/sonar/runner/api/ForkedRunner.java
sonar-runner-api/src/main/java/org/sonar/runner/api/Os.java
sonar-runner-api/src/main/java/org/sonar/runner/api/PrintStreamConsumer.java
sonar-runner-api/src/main/java/org/sonar/runner/api/Runner.java
sonar-runner-api/src/main/java/org/sonar/runner/api/RunnerVersion.java
sonar-runner-api/src/main/java/org/sonar/runner/api/StreamConsumer.java
sonar-runner-api/src/test/java/org/sonar/runner/api/EmbeddedRunnerTest.java
sonar-runner-api/src/test/java/org/sonar/runner/api/ForkedRunnerTest.java
sonar-runner-api/src/test/java/org/sonar/runner/api/OsTest.java
sonar-runner-api/src/test/java/org/sonar/runner/api/PrintStreamConsumerTest.java [new file with mode: 0644]

index 0218a32b838dda888a4317eca5631352203086ef..e5ce102a38c1195c876ddd2c6f11d8e1bfa17a29 100644 (file)
@@ -25,17 +25,34 @@ 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>();
 
-  private EmbeddedRunner() {
+  EmbeddedRunner(BatchLauncher bl) {
+    this.batchLauncher = bl;
   }
 
+  /**
+   * Create a new instance.
+   */
   public static EmbeddedRunner create() {
-    return new EmbeddedRunner();
+    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, ","));
   }
@@ -51,6 +68,6 @@ public class EmbeddedRunner extends Runner<EmbeddedRunner> {
 
   @Override
   protected void doExecute() {
-    new BatchLauncher().execute(properties(), extensions);
+    batchLauncher.execute(properties(), extensions);
   }
 }
index 8dca9fe34df99c29b10f8af6e4c1c3ac3822046e..ec148d667ce99198799c49a980703de58dbc9abb 100644 (file)
@@ -24,48 +24,79 @@ 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.*;
-
+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 javaCommand;
+  private String javaExecutable;
   private StreamConsumer stdOut = null, stdErr = null;
   private final JarExtractor jarExtractor;
+  private final CommandExecutor commandExecutor;
 
-  ForkedRunner(JarExtractor jarExtractor) {
+  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());
+    return new ForkedRunner(new JarExtractor(), CommandExecutor.create());
   }
 
-  public ForkedRunner setJavaCommand(@Nullable String s) {
-    this.javaCommand = s;
+  /**
+   * 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;
@@ -92,16 +123,15 @@ public class ForkedRunner extends Runner<ForkedRunner> {
     fork(createCommand());
   }
 
-  Command createCommand() {
+  private Command createCommand() {
     File propertiesFile = writeProperties();
     File jarFile = jarExtractor.extract("sonar-runner-impl");
-
-    Os os = new Os();
-    if (javaCommand == null) {
-      javaCommand = os.usedJavaExe().getAbsolutePath();
+    if (javaExecutable == null) {
+      javaExecutable = new Os().thisJavaExe().getAbsolutePath();
     }
     return Command.builder()
-        .setExecutable(javaCommand)
+        .setExecutable(javaExecutable)
+        .addEnvVariables(jvmEnvVariables)
         .addArguments(jvmArguments)
         .addArguments("-cp", jarFile.getAbsolutePath(), BatchLauncherMain.class.getName(), propertiesFile.getAbsolutePath())
         .build();
@@ -130,9 +160,9 @@ public class ForkedRunner extends Runner<ForkedRunner> {
     if (stdErr == null) {
       stdErr = new PrintStreamConsumer(System.err);
     }
-    int status = CommandExecutor.create().execute(command, stdOut, stdErr, ONE_DAY_IN_MILLISECONDS);
+    int status = commandExecutor.execute(command, stdOut, stdErr, ONE_DAY_IN_MILLISECONDS);
     if (status != 0) {
-      throw new IllegalStateException("TODO");
+      throw new IllegalStateException("Error status: " + status);
     }
   }
 
index da5136129d6300daa19ae2e16e612ea1f08ba322..7f0e48279aec5f79a18825fbb8d4d42482a412d7 100644 (file)
@@ -26,15 +26,15 @@ class Os {
     return System.getProperty("os.name").contains("Windows");
   }
 
-  File usedJavaHome() {
+  File thisJavaHome() {
     return new File(System.getProperty("java.home"));
   }
 
   /**
    * Path to the java executable used by this VM
    */
-  File usedJavaExe() {
-    File bin = new File(usedJavaHome(), "bin");
+  File thisJavaExe() {
+    File bin = new File(thisJavaHome(), "bin");
     return new File(bin, isWindows() ? "java.exe" : "java");
   }
 }
index e64ba7c54f61d949672a61e5caf6a4a0ae47a8cd..b81a98ae850a8f028b990ad9b8775e6dc5a0d94f 100644 (file)
@@ -21,8 +21,14 @@ package org.sonar.runner.api;
 
 import java.io.PrintStream;
 
+/**
+ * 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
+ */
 public class PrintStreamConsumer implements StreamConsumer {
-  private final PrintStream output;
+  final PrintStream output;
 
   public PrintStreamConsumer(PrintStream output) {
     this.output = output;
index 079cdb9f737d1f2999b12df2f74ff6bc1ea36bfe..46346917a95f2988081a205741eb7cb96eebde3c 100644 (file)
@@ -53,6 +53,9 @@ public abstract class Runner<T extends Runner> {
     return clone;
   }
 
+  /**
+   * Declare Sonar properties, for example sonar.projectKey=>foo.
+   */
   public T addProperties(Properties p) {
     properties.putAll(p);
     return (T) this;
@@ -67,6 +70,9 @@ public abstract class Runner<T extends Runner> {
     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);
index d15f72fb25a599ab0d14ef7fb9267ec2ec7352a4..76ed83674391dde54cfb0ecf92af1ac2bdc847d1 100644 (file)
@@ -21,6 +21,10 @@ package org.sonar.runner.api;
 
 import java.util.Scanner;
 
+/**
+ * Version of this sonar-runner API.
+ * @since 2.2
+ */
 public enum RunnerVersion {
 
   INSTANCE;
index 7eac938aaf52b4ec4112b584496ceeb4250530ad..9ed33713bba429e9d8fbc7afd5d9032580099fb8 100644 (file)
@@ -19,6 +19,9 @@
  */
 package org.sonar.runner.api;
 
+/**
+ * @since 2.2
+ */
 public interface StreamConsumer {
   void consumeLine(String line);
 }
index d3d2ca2a64cdf11b36af3677b459f22abe861220..437d6bd3598df4963e5b40e7d4452e7100dd837d 100644 (file)
 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
@@ -30,6 +38,13 @@ public class EmbeddedRunnerTest {
     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();
@@ -49,6 +64,43 @@ public class EmbeddedRunnerTest {
     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 {
   }
 }
index 7f4d32592a6d44b334e3a306a0d794fb08a71f01..ee87a883ac516c03b9045e0f1d276d9588bced5b 100644 (file)
@@ -22,16 +22,21 @@ 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.util.Arrays;
+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 {
@@ -45,40 +50,85 @@ public class ForkedRunnerTest {
     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);
-    File jar = temp.newFile();
+    final File jar = temp.newFile();
     when(jarExtractor.extract("sonar-runner-impl")).thenReturn(jar);
 
-    ForkedRunner runner = new ForkedRunner(jarExtractor);
-    runner.setJavaCommand("java");
+    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());
 
-    Command command = runner.createCommand();
-    assertThat(command).isNotNull();
-    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");
-
-    // the properties
-    String propsPath = command.toStrings()[5];
-    assertThat(propsPath).endsWith(".properties");
-    Properties properties = new Properties();
-    properties.load(new FileInputStream(propsPath));
-    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");
   }
 }
index 20c4a91cc9759f98236ba70f0736b95cd8edb124..7792d535f483c5070782bfa6da014ea826d7b2e5 100644 (file)
@@ -34,13 +34,13 @@ public class OsTest {
 
   @Test
   public void testUsedJavaHome() throws Exception {
-    File javaHome = new Os().usedJavaHome();
+    File javaHome = new Os().thisJavaHome();
     assertThat(javaHome).isNotNull().exists().isDirectory();
   }
 
   @Test
   public void testUsedJavaExe() throws Exception {
-    File javaExe = new Os().usedJavaExe();
+    File javaExe = new Os().thisJavaExe();
     assertThat(javaExe).isNotNull().isFile().exists();
     assertThat(javaExe.getName()).contains("java");
   }
diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/api/PrintStreamConsumerTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/api/PrintStreamConsumerTest.java
new file mode 100644 (file)
index 0000000..3683cf0
--- /dev/null
@@ -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);
+  }
+}