Add a mechanism to allow tests to specify arguments for Vogar

Vogar supports dozens of different flags and via Run it is tied
in quite tightly into most of the code. Until such time as the
code had been refactored it is necessary when writing tests to
specify some arguments to Vogar. That tight coupling makes it
difficult to provide test specific arguments to Vogar in a clean
way because at the time the test method itself runs Vogar and a
number of other classes have already been created.

Fortunately, JUnit 4 provides a mechanism (MethodRule) to allow
a test method (or an @Before method) access to annotations on
the test method itself. That provides a nice clean way for a
test method to specify the arguments for Vogar.

This adds VogarArgs annotation to specify the arguments and
VogarArgsRule to retrieve them.

Change-Id: I8252d1b00b436f9f9e7e315e3cf2831c3b735cfa
diff --git a/test/vogar/android/AbstractModeTest.java b/test/vogar/android/AbstractModeTest.java
index 6133e98..dd766b4 100644
--- a/test/vogar/android/AbstractModeTest.java
+++ b/test/vogar/android/AbstractModeTest.java
@@ -16,10 +16,13 @@
 
 package vogar.android;
 
+import com.google.common.base.Joiner;
 import com.google.common.base.Supplier;
 import java.io.File;
 import java.io.IOException;
+import junit.framework.AssertionFailedError;
 import org.junit.Before;
+import org.junit.Rule;
 import org.mockito.Mock;
 import vogar.Action;
 import vogar.Classpath;
@@ -38,6 +41,9 @@
  */
 public abstract class AbstractModeTest {
 
+    @Rule
+    public VogarArgsRule vogarArgsRule = new VogarArgsRule();
+
     @Mock protected Console console;
 
     protected Mkdir mkdir;
@@ -68,7 +74,11 @@
         Target target = createTarget();
 
         final Vogar vogar = new Vogar();
-        vogar.parseArgs(new String[0]);
+        String[] args = vogarArgsRule.getTestSpecificArgs();
+        if (!vogar.parseArgs(args)) {
+            throw new AssertionFailedError("Parse error in: " + Joiner.on(",").join(args)
+                    + ". Please check stdout.");
+        }
 
         run = new Run(vogar, false, console, mkdir, androidSdk, new Rm(console), target,
                 new File("runner/dir"));
@@ -80,7 +90,7 @@
     protected abstract Target createTarget();
 
     protected VmCommandBuilder newVmCommandBuilder(Mode mode) {
-        Action action = new Action("fred", "blah", new File("resources"), new File("source"),
+        Action action = new Action("action", "blah", new File("resources"), new File("source"),
                 new File("java"));
         return mode.newVmCommandBuilder(action, new File("/work"));
     }
diff --git a/test/vogar/android/DeviceRuntimeAdbTargetTest.java b/test/vogar/android/DeviceRuntimeAdbTargetTest.java
index c516113..595b39d 100644
--- a/test/vogar/android/DeviceRuntimeAdbTargetTest.java
+++ b/test/vogar/android/DeviceRuntimeAdbTargetTest.java
@@ -50,12 +50,45 @@
     }
 
     @Test
+    @VogarArgs({"action"})
     public void testAdbTarget()
             throws IOException {
 
         Mode deviceRuntime = new DeviceRuntime(run, ModeId.DEVICE, Variant.X32,
                 deviceUserNameSupplier);
 
+        VmCommandBuilder builder = newVmCommandBuilder(deviceRuntime)
+                .classpath(classpath)
+                .mainClass("mainclass")
+                .args("-x", "a b");
+        Command command = builder.build(run.target);
+        List<String> args = command.getArgs();
+        assertEquals(Arrays.asList(
+                "adb", "shell", ""
+                        + "cd /work"
+                        + " &&"
+                        + " ANDROID_DATA=runner"
+                        + " dalvikvm32"
+                        + " -classpath"
+                        + " classes"
+                        + " -Duser.home=runner/dir/user.home"
+                        + " -Duser.name=fred"
+                        + " -Duser.language=en"
+                        + " -Duser.region=US"
+                        + " -Xcheck:jni"
+                        + " -Xjnigreflimit:2000"
+                        + " mainclass"
+                        + " -x a\\ b"), args);
+    }
+
+    @Test
+    @VogarArgs({"--benchmark", "action"})
+    public void testAdbTarget_Benchmark()
+            throws IOException {
+
+        Mode deviceRuntime = new DeviceRuntime(run, ModeId.DEVICE, Variant.X32,
+                deviceUserNameSupplier);
+
         Classpath classpath = new Classpath();
         classpath.addAll(new File("classes"));
         VmCommandBuilder builder = newVmCommandBuilder(deviceRuntime)
@@ -76,7 +109,6 @@
                         + " -Duser.name=fred"
                         + " -Duser.language=en"
                         + " -Duser.region=US"
-                        + " -Xcheck:jni"
                         + " -Xjnigreflimit:2000"
                         + " mainclass"
                         + " -x a\\ b"), args);
diff --git a/test/vogar/android/DeviceRuntimeSshTargetTest.java b/test/vogar/android/DeviceRuntimeSshTargetTest.java
index 0878d8d..4002492 100644
--- a/test/vogar/android/DeviceRuntimeSshTargetTest.java
+++ b/test/vogar/android/DeviceRuntimeSshTargetTest.java
@@ -44,6 +44,7 @@
     }
 
     @Test
+    @VogarArgs({"action"})
     public void testSshTarget()
             throws IOException {
 
diff --git a/test/vogar/android/HostRuntimeLocalTargetTest.java b/test/vogar/android/HostRuntimeLocalTargetTest.java
index 5b9b659..3556610 100644
--- a/test/vogar/android/HostRuntimeLocalTargetTest.java
+++ b/test/vogar/android/HostRuntimeLocalTargetTest.java
@@ -16,14 +16,12 @@
 
 package vogar.android;
 
-import java.io.File;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.runners.MockitoJUnitRunner;
-import vogar.Classpath;
 import vogar.LocalTarget;
 import vogar.Mode;
 import vogar.ModeId;
@@ -46,13 +44,12 @@
     }
 
     @Test
+    @VogarArgs({"action"})
     public void testLocalTarget()
             throws IOException {
 
         Mode hostRuntime = new HostRuntime(run, ModeId.HOST, Variant.X32);
 
-        Classpath classpath = new Classpath();
-        classpath.addAll(new File("classes"));
         VmCommandBuilder builder = newVmCommandBuilder(hostRuntime)
                 .classpath(classpath)
                 .mainClass("mainclass")
@@ -82,4 +79,40 @@
                         + " mainclass"
                         + " -x a\\ b"), args);
     }
+
+    @Test
+    @VogarArgs({"--benchmark", "action"})
+    public void testLocalTarget_Benchmark()
+            throws IOException {
+
+        Mode hostRuntime = new HostRuntime(run, ModeId.HOST, Variant.X32);
+
+        VmCommandBuilder builder = newVmCommandBuilder(hostRuntime)
+                .classpath(classpath)
+                .mainClass("mainclass")
+                .args("-x", "a b");
+        Command command = builder.build(run.target);
+        List<String> args = command.getArgs();
+        assertEquals(Arrays.asList(
+                "sh", "-c", ""
+                        + "ANDROID_PRINTF_LOG=tag"
+                        + " ANDROID_LOG_TAGS=*:i"
+                        + " ANDROID_DATA=" + run.localFile("android-data")
+                        + " ANDROID_ROOT=out/host/linux-x86"
+                        + " LD_LIBRARY_PATH=out/host/linux-x86/lib"
+                        + " DYLD_LIBRARY_PATH=out/host/linux-x86/lib"
+                        + " LD_USE_LOAD_BIAS=1"
+                        + " out/host/linux-x86/bin/dalvikvm32"
+                        + " -classpath classes"
+                        + " -Xbootclasspath"
+                        + ":out/host/linux-x86/framework/core-libart-hostdex.jar"
+                        + ":out/host/linux-x86/framework/conscrypt-hostdex.jar"
+                        + ":out/host/linux-x86/framework/okhttp-hostdex.jar"
+                        + ":out/host/linux-x86/framework/bouncycastle-hostdex.jar"
+                        + " -Duser.language=en"
+                        + " -Duser.region=US"
+                        + " -Xjnigreflimit:2000"
+                        + " mainclass"
+                        + " -x a\\ b"), args);
+    }
 }
diff --git a/test/vogar/android/VogarArgs.java b/test/vogar/android/VogarArgs.java
new file mode 100644
index 0000000..7466fa6
--- /dev/null
+++ b/test/vogar/android/VogarArgs.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package vogar.android;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import vogar.Vogar;
+
+/**
+ * Specify additional arguments to pass to {@link Vogar#parseArgs(String[])}.
+ *
+ * @see VogarArgsRule
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface VogarArgs {
+    String[] value();
+}
diff --git a/test/vogar/android/VogarArgsRule.java b/test/vogar/android/VogarArgsRule.java
new file mode 100644
index 0000000..3a7b080
--- /dev/null
+++ b/test/vogar/android/VogarArgsRule.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package vogar.android;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+/**
+ * Obtains test specific arguments from the {@link VogarArgs} annotation on the test and makes them
+ * available to the test itself.
+ *
+ * <p>Although MethodRule has been marked as deprecated in the current version of JUnit (4.10) it
+ * has been undeprecated in JUnit 4.11.
+ *
+ * @see VogarArgs
+ */
+@SuppressWarnings("deprecation")
+public class VogarArgsRule implements MethodRule {
+
+    private String[] testSpecificArgs = null;
+
+    @Override
+    public Statement apply(Statement base, FrameworkMethod method, Object target) {
+        VogarArgs vogarArgs = method.getAnnotation(VogarArgs.class);
+        if (vogarArgs == null) {
+            throw new AssertionFailedError("Must specify @" + VogarArgs.class.getSimpleName());
+        } else {
+            this.testSpecificArgs = vogarArgs.value();
+        }
+        return base;
+    }
+
+    public String[] getTestSpecificArgs() {
+        return testSpecificArgs;
+    }
+}