blob: c78866eeffe9bd67b85a37207c2f84d668d3fa86 [file] [log] [blame]
/*
* 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();
}
}