/* * Copyright (C) 2009 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 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; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.logging.ConsoleHandler; import java.util.logging.Formatter; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; /** * Command line interface for running benchmarks and tests on dalvik. */ public final class DalvikRunner { static final File HOME = new File("dalvik/libcore/tools/runner"); static final File HOME_JAVA = new File(HOME, "java"); private static class Options { private final List<File> testFiles = new ArrayList<File>(); @Option(names = { "--expectations" }) private Set<File> expectationFiles = new LinkedHashSet<File>(); { expectationFiles.add(new File("dalvik/libcore/tools/runner/expectations.txt")); } private static String MODE_DEVICE = "device"; private static String MODE_HOST = "host"; private static String MODE_ACTIVITY = "activity"; @Option(names = { "--mode" }) private String mode = MODE_DEVICE; @Option(names = { "--timeout" }) private long timeoutSeconds = 10 * 60; // default is ten minutes; @Option(names = { "--clean-before" }) private boolean cleanBefore = true; @Option(names = { "--clean-after" }) private boolean cleanAfter = true; @Option(names = { "--clean" }) private boolean clean = true; @Option(names = { "--xml-reports-directory" }) private File xmlReportsDirectory; @Option(names = { "--verbose" }) private boolean verbose; @Option(names = { "--tee" }) private String teeName; private PrintStream tee; @Option(names = { "--debug" }) private Integer debugPort; @Option(names = { "--device-runner-dir" }) private File deviceRunnerDir = new File("/sdcard/dalvikrunner"); @Option(names = { "--vm-arg" }) private List<String> vmArgs = new ArrayList<String>(); @Option(names = { "--java-home" }) private File javaHome; @Option(names = { "--sdk" }) private File sdkJar = new File("/home/dalvik-prebuild/android-sdk-linux/platforms/android-2.0/android.jar"); private void printUsage() { System.out.println("Usage: DalvikRunner [options]... <tests>..."); System.out.println(); System.out.println(" <tests>: a .java file containing a jtreg test, JUnit test,"); System.out.println(" Caliper benchmark, or a directory of such tests."); System.out.println(); System.out.println("GENERAL OPTIONS"); System.out.println(); System.out.println(" --expectations <file>: include the specified file when looking for"); System.out.println(" test expectations. The file should include qualified test names"); System.out.println(" and the corresponding expected output."); System.out.println(" Default is: " + expectationFiles); System.out.println(); System.out.println(" --mode <device|host|activity>: specify which environment to run the"); System.out.println(" tests in. Options are on the device VM, on the host VM, and on"); System.out.println(" device within an android.app.Activity."); System.out.println(" Default is: " + mode); System.out.println(); System.out.println(" --clean-before: remove working directories before building and"); System.out.println(" running (default). Disable with --no-clean-before if you are"); System.out.println(" using interactively with your own temporary input files."); System.out.println(); System.out.println(" --clean-after: remove temporary files after running (default)."); System.out.println(" Disable with --no-clean-after and use with --verbose if"); System.out.println(" you'd like to manually re-run commands afterwards."); System.out.println(); 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 <file>: emit test output to file during execution."); System.out.println(" Specify '-' for stdout."); System.out.println(); System.out.println(" --timeout-seconds <seconds>: maximum execution time of each"); System.out.println(" test before the runner aborts it."); System.out.println(" Default is: " + timeoutSeconds); System.out.println(); System.out.println(" --xml-reports-directory <path>: directory to emit JUnit-style"); System.out.println(" XML test results."); System.out.println(); System.out.println(" --verbose: turn on verbose output"); System.out.println(); System.out.println("DEVICE OPTIONS"); System.out.println(); System.out.println(" --debug <port>: enable Java debugging on the specified port."); System.out.println(" This port must be free both on the device and on the local"); System.out.println(" system."); System.out.println(); System.out.println(" --device-runner-dir <directory>: use the specified directory for"); System.out.println(" on-device temporary files and code."); System.out.println(" Default is: " + deviceRunnerDir); System.out.println(); System.out.println("GENERAL VM OPTIONS"); System.out.println(); System.out.println(" --vm-arg <argument>: include the specified argument when spawning a"); System.out.println(" virtual machine. Examples: -Xint:fast, -ea, -Xmx16M"); System.out.println(); System.out.println("HOST VM OPTIONS"); System.out.println(); System.out.println(" --java-home <java_home>: execute the tests on the local workstation"); System.out.println(" using the specified java home directory. This does not impact"); System.out.println(" which javac gets used. When unset, java is used from the PATH."); System.out.println(); System.out.println("COMPILE OPTIONS"); System.out.println(); System.out.println(" --sdk <android jar>: the API jar file to compile against."); System.out.println(" Usually this is <SDK>/platforms/android-<X.X>/android.jar"); System.out.println(" where <SDK> is the path to an Android SDK path and <X.X> is"); System.out.println(" a release version like 1.5."); System.out.println(" Default is: " + sdkJar); System.out.println(); } private boolean parseArgs(String[] args) { final List<String> testFilenames; try { testFilenames = new OptionParser(this).parse(args); } catch (RuntimeException e) { System.out.println(e.getMessage()); return false; } // // Semantic error validation // boolean device; boolean vm; if (mode.equals(MODE_DEVICE)) { device = true; vm = true; } else if (mode.equals(MODE_HOST)) { device = false; vm = true; } else if (mode.equals(MODE_ACTIVITY)) { device = true; vm = false; } else { System.out.println("Unknown mode: " + mode); return false; } if (device) { // check device option consistency if (javaHome != null) { System.out.println("java home " + javaHome + " should not be specified for mode " + mode); return false; } } else { // check host (!device) option consistency if (javaHome != null && !new File(javaHome, "/bin/java").exists()) { System.out.println("Invalid java home: " + javaHome); return false; } } // check vm option consistency if (!vm) { if (!vmArgs.isEmpty()) { System.out.println("vm args " + vmArgs + " should not be specified for mode " + mode); return false; } } if (!sdkJar.exists()) { System.out.println("Could not find SDK jar: " + sdkJar); return false; } if (xmlReportsDirectory != null && !xmlReportsDirectory.isDirectory()) { System.out.println("Invalid XML reports directory: " + xmlReportsDirectory); return false; } if (testFilenames.isEmpty()) { System.out.println("No tests provided."); return false; } if (!clean) { cleanBefore = false; cleanAfter = false; } // // Post-processing arguments // for (String testFilename : testFilenames) { 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); } return true; } } private final Options options = new Options(); private final File localTemp = new File("/tmp/dalvikrunner/" + UUID.randomUUID()); private DalvikRunner() {} private void prepareLogging() { ConsoleHandler handler = new ConsoleHandler(); handler.setLevel(Level.ALL); handler.setFormatter(new Formatter() { @Override public String format(LogRecord r) { return r.getMessage() + "\n"; } }); Logger logger = Logger.getLogger("dalvik.runner"); logger.addHandler(handler); logger.setUseParentHandlers(false); } private void run() { Mode mode; if (options.mode.equals(Options.MODE_DEVICE)) { mode = new DeviceDalvikVm( options.debugPort, options.timeoutSeconds, options.sdkJar, options.tee, localTemp, options.vmArgs, options.cleanBefore, options.cleanAfter, options.deviceRunnerDir); } else if (options.mode.equals(Options.MODE_HOST)) { mode = new JavaVm( options.debugPort, options.timeoutSeconds, options.sdkJar, options.tee, localTemp, options.javaHome, options.vmArgs, options.cleanBefore, options.cleanAfter); } else if (options.mode.equals(Options.MODE_ACTIVITY)) { mode = new ActivityMode( options.debugPort, options.timeoutSeconds, options.sdkJar, options.tee, localTemp, options.cleanBefore, options.cleanAfter, options.deviceRunnerDir); } else { System.out.println("Unknown mode mode " + options.mode + "."); return; } List<CodeFinder> codeFinders = Arrays.asList( new JtregFinder(localTemp), new JUnitFinder(), new CaliperFinder(), new MainFinder()); Driver driver = new Driver( localTemp, mode, options.expectationFiles, options.xmlReportsDirectory, codeFinders); try { driver.loadExpectations(); } catch (IOException e) { System.out.println("Problem loading expectations: " + e); return; } driver.buildAndRunAllTests(options.testFiles); mode.shutdown(); } public static void main(String[] args) { DalvikRunner dalvikRunner = new DalvikRunner(); if (!dalvikRunner.options.parseArgs(args)) { dalvikRunner.options.printUsage(); return; } dalvikRunner.prepareLogging(); dalvikRunner.run(); } }