From d1ad6ae8509d5890a7175a1dcd0a3f7fceb75d23 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 23 Feb 2010 11:31:46 -0800 Subject: [PATCH] DalvikRunner --tee option to send output to a file or stdout at runtime Added --tee option so we can watch test output while the test is running, as opposed to waiting until all the output is collected. As part of this, Command.Builder can now optionally specifiy a PrintStream via tee (name from tee(1)). Added ADB.waitForFile to accompany ADB.waitForNonEmptyDirectory Removed gross bash wait loop hack in Activity Mode by replacing Mode.buildCommands with Mode.runTestCommand. Fixed bug that out generated APK package names did not contain a required "." in all cases. --- .../runner/java/dalvik/runner/ActivityMode.java | 62 ++++++++++------------ libcore/tools/runner/java/dalvik/runner/Adb.java | 26 +++++++-- .../tools/runner/java/dalvik/runner/Command.java | 13 +++++ .../runner/java/dalvik/runner/DalvikRunner.java | 27 ++++++++++ .../runner/java/dalvik/runner/DeviceDalvikVm.java | 5 +- .../tools/runner/java/dalvik/runner/JavaVm.java | 7 +-- libcore/tools/runner/java/dalvik/runner/Mode.java | 34 ++++++------ libcore/tools/runner/java/dalvik/runner/Vm.java | 23 ++++++-- 8 files changed, 132 insertions(+), 65 deletions(-) diff --git a/libcore/tools/runner/java/dalvik/runner/ActivityMode.java b/libcore/tools/runner/java/dalvik/runner/ActivityMode.java index b2b4a3b2a..163c72ad5 100644 --- a/libcore/tools/runner/java/dalvik/runner/ActivityMode.java +++ b/libcore/tools/runner/java/dalvik/runner/ActivityMode.java @@ -19,10 +19,12 @@ package dalvik.runner; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Set; +import java.util.concurrent.TimeoutException; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Logger; @@ -36,11 +38,11 @@ final class ActivityMode extends Mode { private static final String TEST_ACTIVITY_CLASS = "dalvik.runner.TestActivity"; - ActivityMode(Integer debugPort, long timeoutSeconds, File sdkJar, File localTemp, + ActivityMode(Integer debugPort, long timeoutSeconds, File sdkJar, PrintStream tee, File localTemp, boolean cleanBefore, boolean cleanAfter, File deviceRunnerDir) { super(new EnvironmentDevice(cleanBefore, cleanAfter, debugPort, localTemp, deviceRunnerDir), - timeoutSeconds, sdkJar); + timeoutSeconds, sdkJar, tee); } private EnvironmentDevice getEnvironmentDevice() { @@ -148,11 +150,21 @@ final class ActivityMode extends Mode { return dex; } + /** + * According to android.content.pm.PackageParser, package name + * "must have at least one '.' separator" Since the qualified name + * may not contain a dot, we prefix containing one to ensure we + * are compliant. + */ + private static String packageName(TestRun testRun) { + return "DalvikRunner." + testRun.getQualifiedName(); + } + private File createApk (TestRun testRun, File dex) { String androidManifest = "\n" + "\n" + + " package=\"" + packageName(testRun) + "\">\n" + " \n" + " \n" + " \n" + @@ -202,7 +214,7 @@ final class ActivityMode extends Mode { private void installApk(TestRun testRun, File apkSigned) { // install the local apk ona the device - getEnvironmentDevice().adb.uninstall(testRun.getQualifiedName()); + getEnvironmentDevice().adb.uninstall(packageName(testRun)); getEnvironmentDevice().adb.install(apkSigned); } @@ -211,40 +223,20 @@ final class ActivityMode extends Mode { properties.setProperty(TestProperties.DEVICE_RUNNER_DIR, getEnvironmentDevice().runnerDir.getPath()); } - @Override protected List buildCommands(TestRun testRun) { - List commands = new ArrayList(); - commands.add(new Command.Builder() - .args("adb") - .args("shell") - .args("am") - .args("start") - .args("-a") - .args("android.intent.action.MAIN") - .args("-n") - .args(testRun.getQualifiedName() + "/" + TEST_ACTIVITY_CLASS).build()); + @Override protected List runTestCommand(TestRun testRun) + throws TimeoutException { + new Command( + "adb", "shell", "am", "start", + "-a","android.intent.action.MAIN", + "-n", (packageName(testRun) + "/" + TEST_ACTIVITY_CLASS)).executeWithTimeout(timeoutSeconds); File resultDir = new File(getEnvironmentDevice().runnerDir, testRun.getQualifiedName()); File resultFile = new File(resultDir, TestProperties.RESULT_FILE); - /* - * The follow bash script waits for the result file to - * exist. It polls once a second to see if it is there with - * "adb shell ls". The "tr" is to remove the carriage return - * and newline from the adb output. When it does exist, we - * "adb shell cat" it so we can see the SUCCESS/FAILURE - * results that are expected by Mode.runTest. - */ - // TODO: move loop to Java - commands.add(new Command.Builder() - .args("bash") - .args("-c") - .args( - "while [ ! \"`adb shell ls " + resultFile + " | tr -d '\\r\\n'`\" = " + - " \"" + resultFile + "\" ] ; do " + - " sleep 1; " + - "done; " + - "adb shell cat " + resultFile).build()); - - return commands; + getEnvironmentDevice().adb.waitForFile(resultFile, timeoutSeconds); + return new Command.Builder() + .args("adb", "shell", "cat", resultFile.getPath()) + .tee(tee) + .build().executeWithTimeout(timeoutSeconds); } @Override void cleanup(TestRun testRun) { diff --git a/libcore/tools/runner/java/dalvik/runner/Adb.java b/libcore/tools/runner/java/dalvik/runner/Adb.java index 075ca5fb5..0ab14ec7f 100644 --- a/libcore/tools/runner/java/dalvik/runner/Adb.java +++ b/libcore/tools/runner/java/dalvik/runner/Adb.java @@ -58,10 +58,22 @@ final class Adb { } /** + * Loop until we see a file on the device. For example, wait + * result.txt appears. + */ + public void waitForFile(File file, long timeoutSeconds) { + waitFor(true, file, timeoutSeconds); + } + + /** * Loop until we see a non-empty directory on the device. For * example, wait until /sdcard is mounted. */ - public void waitForNonEmptyDirectory(File path, int timeoutSeconds) { + public void waitForNonEmptyDirectory(File path, long timeoutSeconds) { + waitFor(false, path, timeoutSeconds); + } + + private void waitFor(boolean file, File path, long timeoutSeconds) { final int millisPerSecond = 1000; final long start = System.currentTimeMillis(); final long deadline = start + (millisPerSecond * timeoutSeconds); @@ -82,8 +94,16 @@ final class Adb { } catch (InterruptedException e) { throw new RuntimeException(e); } - if (!output.isEmpty()) { - return; + if (file) { + // for files, we expect one line of output that matches the filename + if (output.size() == 1 && output.get(0).equals(path.getPath())) { + return; + } + } else { + // for a non empty directory, we just want any output + if (!output.isEmpty()) { + return; + } } } } diff --git a/libcore/tools/runner/java/dalvik/runner/Command.java b/libcore/tools/runner/java/dalvik/runner/Command.java index 4319cf909..88ba38e46 100644 --- a/libcore/tools/runner/java/dalvik/runner/Command.java +++ b/libcore/tools/runner/java/dalvik/runner/Command.java @@ -20,6 +20,7 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; +import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -44,6 +45,7 @@ final class Command { private final List args; private final File workingDirectory; private final boolean permitNonZeroExitStatus; + private final PrintStream tee; private Process process; Command(String... args) { @@ -54,12 +56,14 @@ final class Command { this.args = new ArrayList(args); this.workingDirectory = null; this.permitNonZeroExitStatus = false; + this.tee = null; } private Command(Builder builder) { this.args = new ArrayList(builder.args); this.workingDirectory = builder.workingDirectory; this.permitNonZeroExitStatus = builder.permitNonZeroExitStatus; + this.tee = builder.tee; } public List getArgs() { @@ -106,6 +110,9 @@ final class Command { List outputLines = new ArrayList(); String outputLine; while ((outputLine = in.readLine()) != null) { + if (tee != null) { + tee.println(outputLine); + } outputLines.add(outputLine); } @@ -167,6 +174,7 @@ final class Command { private final List args = new ArrayList(); private File workingDirectory; private boolean permitNonZeroExitStatus = false; + private PrintStream tee = null; public Builder args(Object... objects) { for (Object object : objects) { @@ -200,6 +208,11 @@ final class Command { return this; } + public Builder tee(PrintStream printStream) { + tee = printStream; + return this; + } + public Command build() { return new Command(this); } diff --git a/libcore/tools/runner/java/dalvik/runner/DalvikRunner.java b/libcore/tools/runner/java/dalvik/runner/DalvikRunner.java index 015fb9abd..c78866eef 100644 --- a/libcore/tools/runner/java/dalvik/runner/DalvikRunner.java +++ b/libcore/tools/runner/java/dalvik/runner/DalvikRunner.java @@ -16,8 +16,12 @@ package dalvik.runner; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; @@ -72,6 +76,10 @@ public final class DalvikRunner { @Option(names = { "--verbose" }) private boolean verbose; + @Option(names = { "--tee" }) + private String teeName; + private PrintStream tee; + @Option(names = { "--debug" }) private Integer debugPort; @@ -116,6 +124,9 @@ public final class DalvikRunner { System.out.println(" --clean: synonym for --clean-before and --clean-after (default)."); System.out.println(" Disable with --no-clean if you want no files removed."); System.out.println(); + System.out.println(" --tee : emit test output to file during execution."); + System.out.println(" Specify '-' for stdout."); + System.out.println(); System.out.println(" --timeout-seconds : maximum execution time of each"); System.out.println(" test before the runner aborts it."); System.out.println(" Default is: " + timeoutSeconds); @@ -235,6 +246,19 @@ public final class DalvikRunner { testFiles.add(new File(testFilename)); } + if (teeName != null) { + if (teeName.equals("-")) { + tee = System.out; + } else { + try { + tee = new PrintStream(new BufferedOutputStream(new FileOutputStream(teeName))); + } catch (FileNotFoundException e) { + System.out.println("Could not open file teeName: " + e); + return false; + } + } + } + if (verbose) { Logger.getLogger("dalvik.runner").setLevel(Level.FINE); } @@ -269,6 +293,7 @@ public final class DalvikRunner { options.debugPort, options.timeoutSeconds, options.sdkJar, + options.tee, localTemp, options.vmArgs, options.cleanBefore, @@ -279,6 +304,7 @@ public final class DalvikRunner { options.debugPort, options.timeoutSeconds, options.sdkJar, + options.tee, localTemp, options.javaHome, options.vmArgs, @@ -289,6 +315,7 @@ public final class DalvikRunner { options.debugPort, options.timeoutSeconds, options.sdkJar, + options.tee, localTemp, options.cleanBefore, options.cleanAfter, diff --git a/libcore/tools/runner/java/dalvik/runner/DeviceDalvikVm.java b/libcore/tools/runner/java/dalvik/runner/DeviceDalvikVm.java index 7bdf48288..061e374fc 100644 --- a/libcore/tools/runner/java/dalvik/runner/DeviceDalvikVm.java +++ b/libcore/tools/runner/java/dalvik/runner/DeviceDalvikVm.java @@ -17,6 +17,7 @@ package dalvik.runner; import java.io.File; +import java.io.PrintStream; import java.util.List; import java.util.logging.Logger; @@ -26,11 +27,11 @@ import java.util.logging.Logger; final class DeviceDalvikVm extends Vm { private static final Logger logger = Logger.getLogger(DeviceDalvikVm.class.getName()); - DeviceDalvikVm(Integer debugPort, long timeoutSeconds, File sdkJar, + DeviceDalvikVm(Integer debugPort, long timeoutSeconds, File sdkJar, PrintStream tee, File localTemp, List additionalVmArgs, boolean cleanBefore, boolean cleanAfter, File runnerDir) { super(new EnvironmentDevice(cleanBefore, cleanAfter, debugPort, localTemp, runnerDir), - timeoutSeconds, sdkJar, additionalVmArgs); + timeoutSeconds, sdkJar, tee, additionalVmArgs); } private EnvironmentDevice getEnvironmentDevice() { diff --git a/libcore/tools/runner/java/dalvik/runner/JavaVm.java b/libcore/tools/runner/java/dalvik/runner/JavaVm.java index 2dfc3e73c..38e0386a0 100644 --- a/libcore/tools/runner/java/dalvik/runner/JavaVm.java +++ b/libcore/tools/runner/java/dalvik/runner/JavaVm.java @@ -17,6 +17,7 @@ package dalvik.runner; import java.io.File; +import java.io.PrintStream; import java.util.List; import java.util.Set; @@ -27,11 +28,11 @@ final class JavaVm extends Vm { private final File javaHome; - JavaVm(Integer debugPort, long timeoutSeconds, File sdkJar, File localTemp, - File javaHome, List additionalVmArgs, + JavaVm(Integer debugPort, long timeoutSeconds, File sdkJar, PrintStream tee, + File localTemp, File javaHome, List additionalVmArgs, boolean cleanBefore, boolean cleanAfter) { super(new EnvironmentHost(cleanBefore, cleanAfter, debugPort, localTemp), - timeoutSeconds, sdkJar, additionalVmArgs); + timeoutSeconds, sdkJar, tee, additionalVmArgs); this.javaHome = javaHome; } diff --git a/libcore/tools/runner/java/dalvik/runner/Mode.java b/libcore/tools/runner/java/dalvik/runner/Mode.java index c2d6b14b3..0ad717227 100644 --- a/libcore/tools/runner/java/dalvik/runner/Mode.java +++ b/libcore/tools/runner/java/dalvik/runner/Mode.java @@ -20,6 +20,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; +import java.io.PrintStream; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -44,6 +45,7 @@ abstract class Mode { protected final Environment environment; protected final long timeoutSeconds; protected final File sdkJar; + protected final PrintStream tee; /** * Set of Java files needed to built to tun the currently selected @@ -72,10 +74,11 @@ abstract class Mode { // TODO: jar up just the junit classes and drop the jar in our lib/ directory. new File("out/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/classes.jar").getAbsoluteFile()); - Mode(Environment environment, long timeoutSeconds, File sdkJar) { + Mode(Environment environment, long timeoutSeconds, File sdkJar, PrintStream tee) { this.environment = environment; this.timeoutSeconds = timeoutSeconds; this.sdkJar = sdkJar; + this.tee = tee; } /** @@ -218,20 +221,16 @@ abstract class Mode { throw new IllegalArgumentException(); } - final List commands = buildCommands(testRun); - - List output = null; - for (final Command command : commands) { - try { - output = command.executeWithTimeout(timeoutSeconds); - } catch (TimeoutException e) { - testRun.setResult(Result.EXEC_TIMEOUT, - Collections.singletonList("Exceeded timeout! (" + timeoutSeconds + "s)")); - return; - } catch (Exception e) { - testRun.setResult(Result.ERROR, e); - return; - } + List output; + try { + output = runTestCommand(testRun); + } catch (TimeoutException e) { + testRun.setResult(Result.EXEC_TIMEOUT, + Collections.singletonList("Exceeded timeout! (" + timeoutSeconds + "s)")); + return; + } catch (Exception e) { + testRun.setResult(Result.ERROR, e); + return; } // we only look at the output of the last command if (output.isEmpty()) { @@ -247,9 +246,10 @@ abstract class Mode { } /** - * Returns commands for test execution. + * Run the actual test to gather output */ - protected abstract List buildCommands(TestRun testRun); + protected abstract List runTestCommand(TestRun testRun) + throws TimeoutException; /** * Deletes files and releases any resources required for the execution of diff --git a/libcore/tools/runner/java/dalvik/runner/Vm.java b/libcore/tools/runner/java/dalvik/runner/Vm.java index 9f96ec5ff..8ff58585a 100644 --- a/libcore/tools/runner/java/dalvik/runner/Vm.java +++ b/libcore/tools/runner/java/dalvik/runner/Vm.java @@ -19,6 +19,7 @@ package dalvik.runner; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -27,6 +28,7 @@ import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; +import java.util.concurrent.TimeoutException; import java.util.logging.Logger; /** @@ -39,22 +41,25 @@ public abstract class Vm extends Mode { protected final List additionalVmArgs; Vm(Environment environment, long timeoutSeconds, File sdkJar, - List additionalVmArgs) { - super(environment, timeoutSeconds, sdkJar); + PrintStream tee, List additionalVmArgs) { + super(environment, timeoutSeconds, sdkJar, tee); this.additionalVmArgs = additionalVmArgs; } /** * Returns a VM for test execution. */ - @Override protected List buildCommands(TestRun testRun) { - return Collections.singletonList(newVmCommandBuilder(testRun.getUserDir()) + @Override protected List runTestCommand(TestRun testRun) + throws TimeoutException { + Command command = newVmCommandBuilder(testRun.getUserDir()) .classpath(getRuntimeSupportClasspath(testRun)) .userDir(testRun.getUserDir()) .debugPort(environment.debugPort) .vmArgs(additionalVmArgs) .mainClass(TestRunner.class.getName()) - .build()); + .output(tee) + .build(); + return command.executeWithTimeout(timeoutSeconds); } /** @@ -78,6 +83,7 @@ public abstract class Vm extends Mode { private File userDir; private Integer debugPort; private String mainClass; + private PrintStream output; private List vmCommand = Collections.singletonList("java"); private List vmArgs = new ArrayList(); @@ -116,6 +122,11 @@ public abstract class Vm extends Mode { return this; } + public VmCommandBuilder output(PrintStream output) { + this.output = output; + return this; + } + public VmCommandBuilder vmArgs(String... vmArgs) { return vmArgs(Arrays.asList(vmArgs)); } @@ -146,6 +157,8 @@ public abstract class Vm extends Mode { builder.args(vmArgs); builder.args(mainClass); + builder.tee(output); + return builder.build(); } } -- 2.11.0