diff options
22 files changed, 335 insertions, 388 deletions
diff --git a/tools/runner/java/vogar/Action.java b/tools/runner/java/vogar/Action.java index 190c457..5a562c7 100644 --- a/tools/runner/java/vogar/Action.java +++ b/tools/runner/java/vogar/Action.java @@ -17,7 +17,6 @@ package vogar; import java.io.File; -import vogar.target.Runner; /** * A named job such as a test or benchmark run. This class tracks the resource @@ -27,36 +26,34 @@ public final class Action { private final String name; private final String actionClass; - private final File actionDirectory; - private final File actionJava; - private final String description; - private final Class<? extends Runner> runnerClass; - private final File runnerJava; - private final Classpath runnerClasspath; + private final File resourcesDirectory; + private final File javaFile; + private final RunnerSpec runnerSpec; private File userDir = new File(System.getProperty("user.dir")); - public Action(String name, String actionClass, File actionDirectory, - File actionJava, String description, Class<? extends Runner> runnerClass, - File runnerJava, Classpath runnerClasspath) { + public Action(String name, String actionClass, File resourcesDirectory, + File javaFile, RunnerSpec runnerSpec) { this.name = name; this.actionClass = actionClass; - this.actionDirectory = actionDirectory; - this.actionJava = actionJava; - this.description = description; - this.runnerClass = runnerClass; - this.runnerJava = runnerJava; - this.runnerClasspath = runnerClasspath; + this.resourcesDirectory = resourcesDirectory; + this.javaFile = javaFile; + this.runnerSpec = runnerSpec; } /** - * Returns the local directory containing this action's java file. + * Returns the local directory containing this action's required resource + * files, or {@code null} if this action is standalone. */ - public File getJavaDirectory() { - return actionDirectory; + public File getResourcesDirectory() { + return resourcesDirectory; } + /** + * Returns this action's java file, or {@code null} if this file wasn't + * built from source. + */ public File getJavaFile() { - return actionJava; + return javaFile; } /** @@ -74,12 +71,8 @@ public final class Action { return name; } - /** - * Returns an English description of this action, or null if no such - * description is known. - */ - public String getDescription() { - return description; + public RunnerSpec getRunnerSpec() { + return runnerSpec; } /** @@ -94,18 +87,6 @@ public final class Action { return userDir; } - public Class<? extends Runner> getRunnerClass() { - return runnerClass; - } - - public File getRunnerJava() { - return runnerJava; - } - - public Classpath getRunnerClasspath() { - return runnerClasspath; - } - @Override public String toString() { return name; } diff --git a/tools/runner/java/vogar/ActivityMode.java b/tools/runner/java/vogar/ActivityMode.java index 795b6a2..da49c89 100644 --- a/tools/runner/java/vogar/ActivityMode.java +++ b/tools/runner/java/vogar/ActivityMode.java @@ -19,17 +19,13 @@ package vogar; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import java.util.logging.Logger; import vogar.commands.Aapt; import vogar.commands.Command; import vogar.commands.Dx; -import vogar.commands.Mkdir; import vogar.commands.Rm; /** @@ -43,114 +39,47 @@ final class ActivityMode extends Mode { ActivityMode(Integer debugPort, File sdkJar, List<String> javacArgs, int monitorPort, File localTemp, boolean cleanBefore, boolean cleanAfter, - File deviceRunnerDir) { + File deviceRunnerDir, Classpath classpath) { super(new EnvironmentDevice(cleanBefore, cleanAfter, debugPort, monitorPort, localTemp, deviceRunnerDir), - sdkJar, javacArgs, monitorPort); + sdkJar, javacArgs, monitorPort, classpath); } private EnvironmentDevice getEnvironmentDevice() { return (EnvironmentDevice) environment; } - @Override protected void prepare(Set<File> testRunnerJava, Classpath testRunnerClasspath) { - testRunnerJava.add(new File("dalvik/libcore/tools/runner/lib/TestActivity.java")); - super.prepare(testRunnerJava, testRunnerClasspath); + @Override protected void prepare(Set<RunnerSpec> runners) { + runnerJava.add(new File("dalvik/libcore/tools/runner/lib/TestActivity.java")); + super.prepare(runners); } - @Override protected void postCompileRunner() { - } - - @Override protected void postCompile(Action action) { + @Override protected void postCompile(Action action, File jar) { logger.fine("aapt and push " + action.getName()); - // Some things of note: - // 1. we can't put multiple dex files in one apk - // 2. we can't just give dex multiple jars with conflicting class names - // 3. dex is slow if we give it too much to chew on - // 4. dex can run out of memory if given too much to chew on + // We can't put multiple dex files in one apk. + // We can't just give dex multiple jars with conflicting class names // With that in mind, the APK packaging strategy is as follows: - // 1. make an empty classes temporary directory - // 2. add test runner classes - // 3. find original jar test came from, add contents to classes - // 4. add supported runner classes specified by finder - // 5. add latest test classes to output - // 6. dx to create a dex - // 7. aapt the dex to create apk - // 8. sign the apk - // 9. install the apk - File packagingDir = makePackagingDirectory(action); - addRunnerClasses(packagingDir); - List<File> found = new ArrayList<File>(); - File originalJar = findOriginalJar(action); - if (originalJar != null) { - found.add(originalJar); - } - found.addAll(action.getRunnerClasspath().getElements()); - extractJars(packagingDir, found); - addActionClasses(action, packagingDir); - File dex = createDex(action, packagingDir); + // 1. dx to create a dex + // 2. aapt the dex to create apk + // 3. sign the apk + // 4. install the apk + File dex = createDex(action, jar); File apkUnsigned = createApk(action, dex); File apkSigned = signApk(action, apkUnsigned); installApk(action, apkSigned); } - private File makePackagingDirectory(Action action) { - File packagingDir = new File(environment.actionCompilationDir(action), "packaging"); - new Rm().directoryTree(packagingDir); - new Mkdir().mkdirs(packagingDir); - return packagingDir; - } - - private void addRunnerClasses(File packagingDir) { - new Command("rsync", "-a", - environment.runnerClassesDir() + "/", - packagingDir + "/").execute(); - } - - private File findOriginalJar(Action action) { - String targetClass = action.getTargetClass(); - String targetClassFile = targetClass.replace('.', '/') + ".class"; - for (File element : classpath.getElements()) { - try { - JarFile jar = new JarFile(element); - JarEntry jarEntry = jar.getJarEntry(targetClassFile); - if (jarEntry != null) { - return element; - } - } catch (IOException e) { - throw new RuntimeException( - "Could not find element " + element + - " of class path " + classpath, e); - } - } - return null; - } - - private static void extractJars(File packagingDir, List<File> jars) { - for (File jar : jars) { - new Command.Builder() - .args("unzip") - .args("-q") - .args("-o") - .args(jar) - .args("-d") - .args(packagingDir).execute(); - } - new Rm().directoryTree(new File(packagingDir, "META-INF")); - } - - private void addActionClasses(Action action, File packagingDir) { - File classesDir = environment.classesDir(action); - new Command("rsync", "-a", - classesDir + "/", - packagingDir + "/").execute(); - } - private File createDex(Action action, File packagingDir) { - File classesDir = environment.classesDir(action); - File dex = new File(classesDir + ".dex"); - new Dx().dex(dex, Classpath.of(packagingDir)); + /** + * Returns a single dexfile containing {@code action}'s classes and all + * dependencies. + */ + private File createDex(Action action, File actionJar) { + File dex = environment.file(action, "classes.dex"); + Classpath classesToDex = Classpath.of(actionJar); + classesToDex.addAll(this.classpath); + new Dx().dex(dex, classesToDex); return dex; } @@ -179,9 +108,7 @@ final class ActivityMode extends Mode { " </activity>\n" + " </application>\n" + "</manifest>\n"; - File androidManifestFile = - new File(environment.actionCompilationDir(action), - "AndroidManifest.xml"); + File androidManifestFile = environment.file(action, "classes", "AndroidManifest.xml"); try { FileOutputStream androidManifestOut = new FileOutputStream(androidManifestFile); @@ -191,17 +118,15 @@ final class ActivityMode extends Mode { throw new RuntimeException("Problem writing " + androidManifestFile, e); } - File classesDir = environment.classesDir(action); - File apkUnsigned = new File(classesDir + ".apk.unsigned"); + File apkUnsigned = environment.file(action, action + ".apk.unsigned"); new Aapt().apk(apkUnsigned, androidManifestFile); new Aapt().add(apkUnsigned, dex); - new Aapt().add(apkUnsigned, new File(classesDir , TestProperties.FILE)); + new Aapt().add(apkUnsigned, environment.file(action, "classes", TestProperties.FILE)); return apkUnsigned; } private File signApk(Action action, File apkUnsigned) { - File classesDir = environment.classesDir(action); - File apkSigned = new File(classesDir, action.getName() + ".apk"); + File apkSigned = environment.file(action, action + ".apk"); // TODO: we should be able to work with a shipping SDK, not depend on out/... // TODO: we should be able to work without hardwired keys, not depend on build/... new Command.Builder() diff --git a/tools/runner/java/vogar/CaliperFinder.java b/tools/runner/java/vogar/CaliperSpec.java index 3e2101d..70095aa 100644 --- a/tools/runner/java/vogar/CaliperFinder.java +++ b/tools/runner/java/vogar/CaliperSpec.java @@ -24,21 +24,28 @@ import vogar.target.Runner; * Create {@link Action}s for {@code .java} files with Caliper benchmarks in * them. */ -class CaliperFinder extends NamingPatternCodeFinder { +class CaliperSpec extends NamingPatternRunnerSpec { @Override protected boolean matches(File file) { return super.matches(file) && file.getName().endsWith("Benchmark.java"); } + public boolean supports(String clazz) { + return clazz.endsWith("Benchmark"); + } + public Class<? extends Runner> getRunnerClass() { return CaliperRunner.class; } - public File getRunnerJava() { + public File getSource() { return new File(Vogar.HOME_JAVA, "vogar/target/CaliperRunner.java"); } - public Classpath getRunnerClasspath() { - return new Classpath(); + public Classpath getClasspath() { + return Classpath.of( + new File("dalvik/libcore/tools/runner/lib/jsr305.jar"), + new File("dalvik/libcore/tools/runner/lib/guava.jar"), + new File("dalvik/libcore/tools/runner/lib/caliper.jar")); } } diff --git a/tools/runner/java/vogar/DeviceDalvikVm.java b/tools/runner/java/vogar/DeviceDalvikVm.java index 4b44f02..7243b1e 100644 --- a/tools/runner/java/vogar/DeviceDalvikVm.java +++ b/tools/runner/java/vogar/DeviceDalvikVm.java @@ -17,7 +17,9 @@ package vogar; import java.io.File; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.logging.Logger; import vogar.commands.Dx; @@ -27,53 +29,54 @@ import vogar.commands.Dx; final class DeviceDalvikVm extends Vm { private static final Logger logger = Logger.getLogger(DeviceDalvikVm.class.getName()); + /** A list of generic names that we avoid when naming generated files. */ + private static final Set<String> BANNED_NAMES = new HashSet<String>(); + { + BANNED_NAMES.add("classes"); + BANNED_NAMES.add("javalib"); + } + DeviceDalvikVm(Integer debugPort, File sdkJar, List<String> javacArgs, int monitorPort, File localTemp, List<String> additionalVmArgs, List<String> targetArgs, boolean cleanBefore, boolean cleanAfter, - File runnerDir) { + File runnerDir, Classpath classpath) { super(new EnvironmentDevice(cleanBefore, cleanAfter, debugPort, monitorPort, localTemp, - runnerDir), sdkJar, javacArgs, additionalVmArgs, targetArgs, monitorPort); + runnerDir), sdkJar, javacArgs, additionalVmArgs, targetArgs, monitorPort, classpath); } private EnvironmentDevice getEnvironmentDevice() { return (EnvironmentDevice) environment; } - @Override protected void postCompileRunner() { - // TODO: does this really need to be a special case? - postCompile("testrunner", environment.runnerClassesDir()); - + @Override protected void installRunner() { // dex everything on the classpath and push it to the device. for (File classpathElement : classpath.getElements()) { - String name = basenameOfJar(classpathElement); - logger.fine("dex and push " + name); - // make the local dex (inside a jar) - // TODO: this is *really* expensive. we need a cache! - File outputFile = getEnvironmentDevice().actionDir(name + ".jar"); - new Dx().dex(outputFile, Classpath.of(classpathElement)); - // push the local dex to the device - getEnvironmentDevice().adb.push(outputFile, deviceDexFile(name)); + dexAndPush(basenameOfJar(classpathElement), classpathElement); } } - private String basenameOfJar(File jarFile) { - return jarFile.getName().replaceAll("\\.jar$", ""); + private String basenameOfJar(File file) { + String name = file.getName().replaceAll("\\.jar$", ""); + while (BANNED_NAMES.contains(name)) { + file = file.getParentFile(); + name = file.getName(); + } + return name; } - @Override protected void postCompile(Action action) { - postCompile(action.getName(), environment.classesDir(action)); + @Override protected void postCompile(Action action, File jar) { + dexAndPush(action.getName(), jar); } - private void postCompile(String name, File dir) { + private void dexAndPush(String name, File jar) { logger.fine("dex and push " + name); // make the local dex (inside a jar) - File localDex = new File(dir.getPath() + ".jar"); - new Dx().dex(localDex, Classpath.of(dir)); + File localDex = environment.file(name, name + ".dx.jar"); + new Dx().dex(localDex, Classpath.of(jar)); // post the local dex to the device - File deviceDex = deviceDexFile(name); - getEnvironmentDevice().adb.push(localDex, deviceDex); + getEnvironmentDevice().adb.push(localDex, deviceDexFile(name)); } private File deviceDexFile(String name) { @@ -97,13 +100,12 @@ final class DeviceDalvikVm extends Vm { .temp(getEnvironmentDevice().vogarTemp); } - @Override protected Classpath getRuntimeSupportClasspath(Action action) { - Classpath classpath = new Classpath(); - classpath.addAll(deviceDexFile(action.getName())); - classpath.addAll(deviceDexFile("testrunner")); - for (File classpathElement : this.classpath.getElements()) { - classpath.addAll(deviceDexFile(basenameOfJar(classpathElement))); + @Override protected Classpath getRuntimeClasspath(Action action) { + Classpath result = new Classpath(); + result.addAll(deviceDexFile(action.getName())); + for (File classpathElement : classpath.getElements()) { + result.addAll(deviceDexFile(basenameOfJar(classpathElement))); } - return classpath; + return result; } } diff --git a/tools/runner/java/vogar/Driver.java b/tools/runner/java/vogar/Driver.java index 40dcf58..00c46cf 100644 --- a/tools/runner/java/vogar/Driver.java +++ b/tools/runner/java/vogar/Driver.java @@ -46,7 +46,7 @@ final class Driver implements HostMonitor.Handler { private final File localTemp; private final ExpectationStore expectationStore; - private final List<CodeFinder> codeFinders; + private final List<RunnerSpec> runnerSpecs; private final Mode mode; private final XmlReportPrinter reportPrinter; private final Console console; @@ -59,6 +59,7 @@ final class Driver implements HostMonitor.Handler { private Timer actionTimeoutTimer = new Timer("action timeout", true); + private final Set<RunnerSpec> runnerSpecsBearingActions = new HashSet<RunnerSpec>(); private final Map<String, Action> actions = Collections.synchronizedMap( new LinkedHashMap<String, Action>()); private final Map<String, Outcome> outcomes = Collections.synchronizedMap( @@ -71,13 +72,13 @@ final class Driver implements HostMonitor.Handler { private int unsupportedActions = 0; public Driver(File localTemp, Mode mode, ExpectationStore expectationStore, - List<CodeFinder> codeFinders, XmlReportPrinter reportPrinter, + List<RunnerSpec> runnerSpecs, XmlReportPrinter reportPrinter, Console console, HostMonitor monitor, int monitorPort, long timeoutSeconds) { this.localTemp = localTemp; this.expectationStore = expectationStore; this.mode = mode; this.console = console; - this.codeFinders = codeFinders; + this.runnerSpecs = runnerSpecs; this.reportPrinter = reportPrinter; this.monitor = monitor; this.monitorPort = monitorPort; @@ -87,44 +88,27 @@ final class Driver implements HostMonitor.Handler { /** * Builds and executes the actions in the given files. */ - public void buildAndRunAllActions(Collection<File> files) { + public void buildAndRun(Collection<File> files, Collection<String> classes) { if (!actions.isEmpty()) { throw new IllegalStateException("Drivers are not reusable"); } new Mkdir().mkdirs(localTemp); - for (File file : files) { - Set<Action> actionsForFile = Collections.emptySet(); - for (CodeFinder codeFinder : codeFinders) { - actionsForFile = codeFinder.findActions(file); + filesToActions(files); + classesToActions(classes); - // break as soon as we find any match. We don't need multiple - // matches for the same file, since that would run it twice. - if (!actionsForFile.isEmpty()) { - break; - } - } - - for (Action action : actionsForFile) { - actions.put(action.getName(), action); - } + if (actions.isEmpty()) { + logger.info("Nothing to do."); + return; } - // compute TestRunner java and classpath to pass to mode.prepare - Set<File> runnerJava = new HashSet<File>(); - Classpath runnerClasspath = new Classpath(); - for (final Action action : actions.values()) { - runnerJava.add(action.getRunnerJava()); - runnerClasspath.addAll(action.getRunnerClasspath()); - } + logger.info("Actions: " + actions.size()); // mode.prepare before mode.buildAndInstall to ensure the runner is // built. packaging of activity APK files needs the runner along with // the action-specific files. - mode.prepare(runnerJava, runnerClasspath); - - logger.info("Actions: " + actions.size()); + mode.prepare(runnerSpecsBearingActions); // build and install actions in a background thread. Using lots of // threads helps for packages that contain many unsupported actions @@ -189,6 +173,8 @@ final class Driver implements HostMonitor.Handler { logger.info(numFiles + " XML files written."); } + mode.shutdown(); + if (failures > 0 || unsupportedActions > 0) { Collections.sort(failureNames); console.summarizeFailures(failureNames); @@ -200,6 +186,40 @@ final class Driver implements HostMonitor.Handler { } } + private void classesToActions(Collection<String> classes) { + for (String clazz : classes) { + for (RunnerSpec runnerSpec : runnerSpecs) { + if (runnerSpec.supports(clazz)) { + runnerSpecsBearingActions.add(runnerSpec); + Action action = new Action(clazz, clazz, null, null, runnerSpec); + actions.put(action.getName(), action); + break; + } + } + } + } + + private void filesToActions(Collection<File> files) { + for (File file : files) { + Set<Action> actionsForFile = Collections.emptySet(); + + for (RunnerSpec runnerSpec : runnerSpecs) { + actionsForFile = runnerSpec.findActions(file); + + // break as soon as we find any match. We don't need multiple + // matches for the same file, since that would run it twice. + if (!actionsForFile.isEmpty()) { + runnerSpecsBearingActions.add(runnerSpec); + break; + } + } + + for (Action action : actionsForFile) { + actions.put(action.getName(), action); + } + } + } + /** * Executes a single action and then prints the result. */ diff --git a/tools/runner/java/vogar/Environment.java b/tools/runner/java/vogar/Environment.java index e149e0a..fc5c3ea 100644 --- a/tools/runner/java/vogar/Environment.java +++ b/tools/runner/java/vogar/Environment.java @@ -58,29 +58,16 @@ abstract class Environment { void cleanup(Action action) { if (cleanAfter) { logger.fine("clean " + action.getName()); - new Rm().directoryTree(actionCompilationDir(action)); - new Rm().directoryTree(actionUserDir(action)); + new Rm().directoryTree(file(action)); } } - final File actionDir(String name) { - return new File(localTemp, name); + final File file(Object... path) { + return new File(localTemp + "/" + Strings.join(path, "/")); } - final File runnerDir(String name) { - return new File(actionDir("testrunner"), name); - } - - final File runnerClassesDir() { - return runnerDir("classes"); - } - - final File actionCompilationDir(Action action) { - return new File(localTemp, action.getName()); - } - - final File classesDir(Action action) { - return new File(actionCompilationDir(action), "classes"); + final File hostJar(Object nameOrAction) { + return file(nameOrAction, nameOrAction + ".jar"); } final File actionUserDir(Action action) { @@ -88,5 +75,9 @@ abstract class Environment { return new File(testTemp, action.getName()); } - abstract void shutdown(); + void shutdown() { + if (cleanAfter) { + new Rm().directoryTree(localTemp); + } + } } diff --git a/tools/runner/java/vogar/EnvironmentDevice.java b/tools/runner/java/vogar/EnvironmentDevice.java index b315ea1..eddde8d 100644 --- a/tools/runner/java/vogar/EnvironmentDevice.java +++ b/tools/runner/java/vogar/EnvironmentDevice.java @@ -51,7 +51,10 @@ class EnvironmentDevice extends Environment { @Override protected void prepareUserDir(Action action) { File actionClassesDirOnDevice = actionClassesDirOnDevice(action); adb.mkdir(actionClassesDirOnDevice); - adb.push(action.getJavaDirectory(), actionClassesDirOnDevice); + File resourcesDirectory = action.getResourcesDirectory(); + if (resourcesDirectory != null) { + adb.push(resourcesDirectory, actionClassesDirOnDevice); + } action.setUserDir(actionClassesDirOnDevice); } @@ -67,6 +70,7 @@ class EnvironmentDevice extends Environment { } @Override void shutdown() { + super.shutdown(); if (cleanAfter) { adb.rm(runnerDir); } diff --git a/tools/runner/java/vogar/EnvironmentHost.java b/tools/runner/java/vogar/EnvironmentHost.java index 33a59e1..7942332 100644 --- a/tools/runner/java/vogar/EnvironmentHost.java +++ b/tools/runner/java/vogar/EnvironmentHost.java @@ -37,11 +37,15 @@ class EnvironmentHost extends Environment { throw new IllegalStateException(); } - new Mkdir().mkdirs(actionUserDir.getParentFile()); - new Command("cp", "-r", action.getJavaDirectory().toString(), - actionUserDir.toString()).execute(); + File resourcesDirectory = action.getResourcesDirectory(); + if (resourcesDirectory != null) { + new Mkdir().mkdirs(actionUserDir.getParentFile()); + new Command("cp", "-r", resourcesDirectory.toString(), + actionUserDir.toString()).execute(); + } else { + new Mkdir().mkdirs(actionUserDir); + } + action.setUserDir(actionUserDir); } - - @Override void shutdown() {} } diff --git a/tools/runner/java/vogar/JUnitFinder.java b/tools/runner/java/vogar/JUnitSpec.java index 0dcfbcd..d8169ba 100644 --- a/tools/runner/java/vogar/JUnitFinder.java +++ b/tools/runner/java/vogar/JUnitSpec.java @@ -23,24 +23,33 @@ import vogar.target.Runner; /** * Create {@link Action}s for {@code .java} files with JUnit tests in them. */ -class JUnitFinder extends NamingPatternCodeFinder { +class JUnitSpec extends NamingPatternRunnerSpec { @Override protected boolean matches(File file) { String filename = file.getName(); - return super.matches(file) - && (filename.endsWith("Test.java") || filename.endsWith("TestSuite.java")); + return super.matches(file) && (filename.endsWith("Test.java") + || filename.endsWith("TestSuite.java") + || filename.contains("Tests")); + } + + public boolean supports(String clazz) { + return clazz.endsWith("Test") + || clazz.endsWith("TestSuite") + || clazz.contains("Tests"); } public Class<? extends Runner> getRunnerClass() { return JUnitRunner.class; } - public File getRunnerJava() { + public File getSource() { return new File(Vogar.HOME_JAVA, "vogar/target/JUnitRunner.java"); } - public Classpath getRunnerClasspath() { - // TODO: we should be able to work with a shipping SDK, not depend on out/... - return Classpath.of(new File("out/host/common/obj/JAVA_LIBRARIES/junit_intermediates/javalib.jar").getAbsoluteFile()); + public Classpath getClasspath() { + // TODO: jar up just the junit classes and drop the jar in our lib/ directory. + return Classpath.of( + new File("out/target/common/obj/JAVA_LIBRARIES/core-tests-luni_intermediates/classes.jar").getAbsoluteFile(), + new File("out/host/common/obj/JAVA_LIBRARIES/junit_intermediates/javalib.jar").getAbsoluteFile()); } } diff --git a/tools/runner/java/vogar/JavaVm.java b/tools/runner/java/vogar/JavaVm.java index f728744..f8bdd56 100644 --- a/tools/runner/java/vogar/JavaVm.java +++ b/tools/runner/java/vogar/JavaVm.java @@ -28,31 +28,25 @@ final class JavaVm extends Vm { JavaVm(Integer debugPort, File sdkJar, List<String> javacArgs, int monitorPort, File localTemp, File javaHome, List<String> additionalVmArgs, - List<String> targetArgs, boolean cleanBefore, boolean cleanAfter) { + List<String> targetArgs, boolean cleanBefore, boolean cleanAfter, + Classpath classpath) { super(new EnvironmentHost(cleanBefore, cleanAfter, debugPort, localTemp), - sdkJar, javacArgs, additionalVmArgs, targetArgs, monitorPort); + sdkJar, javacArgs, additionalVmArgs, targetArgs, monitorPort, classpath); this.javaHome = javaHome; } - @Override protected void postCompileRunner() { - } - - @Override protected void postCompile(Action action) { - } - - @Override protected VmCommandBuilder newVmCommandBuilder( - File workingDirectory) { + @Override protected VmCommandBuilder newVmCommandBuilder(File workingDirectory) { String java = javaHome == null ? "java" : new File(javaHome, "bin/java").getPath(); return new VmCommandBuilder() .vmCommand(java) .workingDir(workingDirectory); } - @Override protected Classpath getRuntimeSupportClasspath(Action action) { - Classpath classpath = new Classpath(); - classpath.addAll(environment.classesDir(action)); - classpath.addAll(this.classpath); - classpath.addAll(environment.runnerClassesDir()); - classpath.addAll(runnerClasspath); + + @Override protected Classpath getRuntimeClasspath(Action action) { + Classpath result = new Classpath(); + result.addAll(classpath); + result.addAll(environment.hostJar(action)); + /* * For javax.net.ssl tests dependency on Bouncy Castle for * creating a self-signed X509 certificate. Needs to be run @@ -61,7 +55,7 @@ final class JavaVm extends Vm { * * --java-home /usr/lib/jvm/java-6-openjdk */ - classpath.addAll(new File("/usr/share/java/bcprov.jar")); - return classpath; + result.addAll(new File("/usr/share/java/bcprov.jar")); + return result; } } diff --git a/tools/runner/java/vogar/JtregFinder.java b/tools/runner/java/vogar/JtregSpec.java index b413166..1c43949 100644 --- a/tools/runner/java/vogar/JtregFinder.java +++ b/tools/runner/java/vogar/JtregSpec.java @@ -35,12 +35,12 @@ import vogar.target.Runner; /** * Create {@link Action}s for {@code .java} files with jtreg tests in them. */ -class JtregFinder implements CodeFinder { +class JtregSpec implements RunnerSpec { // TODO: add support for the @library directive, as seen in // test/com/sun/crypto/provider/Cipher/AES/TestKATForECB_VT.java - private static final Logger logger = Logger.getLogger(JtregFinder.class.getName()); + private static final Logger logger = Logger.getLogger(JtregSpec.class.getName()); /** * The subpath of a platform implementation under which tests live. Used to @@ -51,7 +51,7 @@ class JtregFinder implements CodeFinder { private final File localTemp; - JtregFinder(File localTemp) { + JtregSpec(File localTemp) { this.localTemp = localTemp; } @@ -85,9 +85,7 @@ class JtregFinder implements CodeFinder { TestDescription description = testResult.getDescription(); String qualifiedName = qualifiedName(description); String testClass = description.getName(); - result.add(new Action(qualifiedName, testClass, description.getDir(), description.getFile(), - description.getTitle(), - getRunnerClass(), getRunnerJava(), getRunnerClasspath())); + result.add(new Action(qualifiedName, testClass, description.getDir(), description.getFile(), this)); } return result; } catch (Exception jtregFailure) { @@ -96,6 +94,11 @@ class JtregFinder implements CodeFinder { } } + public boolean supports(String clazz) { + // the jtreg runner cannot run prebuilt classes + return false; + } + /** * Returns a fully qualified name of the form {@code * java.lang.Math.PowTests} from the given test description. The returned @@ -128,11 +131,11 @@ class JtregFinder implements CodeFinder { return JtregRunner.class; } - public File getRunnerJava() { + public File getSource() { return new File(Vogar.HOME_JAVA, "vogar/target/JtregRunner.java"); } - public Classpath getRunnerClasspath() { + public Classpath getClasspath() { return new Classpath(); } } diff --git a/tools/runner/java/vogar/MainFinder.java b/tools/runner/java/vogar/MainSpec.java index 4c1f1db..d2bebfb 100644 --- a/tools/runner/java/vogar/MainFinder.java +++ b/tools/runner/java/vogar/MainSpec.java @@ -23,17 +23,21 @@ import vogar.target.Runner; /** * Create {@link Action}s for {@code .java} files with main methods in them. */ -class MainFinder extends NamingPatternCodeFinder { +class MainSpec extends NamingPatternRunnerSpec { + + public boolean supports(String clazz) { + return true; + } public Class<? extends Runner> getRunnerClass() { return MainRunner.class; } - public File getRunnerJava() { + public File getSource() { return new File(Vogar.HOME_JAVA, "vogar/target/MainRunner.java"); } - public Classpath getRunnerClasspath() { + public Classpath getClasspath() { return new Classpath(); } } diff --git a/tools/runner/java/vogar/Mode.java b/tools/runner/java/vogar/Mode.java index eaabb94..7009adb 100644 --- a/tools/runner/java/vogar/Mode.java +++ b/tools/runner/java/vogar/Mode.java @@ -56,42 +56,34 @@ abstract class Mode { protected final Set<File> runnerJava = new HashSet<File>(); /** - * Classpath of runner on the host side including any supporting libraries - * for runnerJava. Useful for compiling runnerJava as well as executing it - * on the host. Execution on the device requires further packaging typically - * done by postCompile. + * User classes that need to be included in the classpath for both + * compilation and execution. Also includes dependencies of all active + * runners. */ - protected final Classpath runnerClasspath = new Classpath(); - - // TODO: this should be an immutable collection. - protected final Classpath classpath = Classpath.of( - new File("dalvik/libcore/tools/runner/lib/jsr305.jar"), - new File("dalvik/libcore/tools/runner/lib/guava.jar"), - new File("dalvik/libcore/tools/runner/lib/caliper.jar"), - // TODO: we should be able to work with a shipping SDK, not depend on out/... - // dalvik/libcore/**/test/ for junit - // TODO: jar up just the junit classes and drop the jar in our lib/ directory. - new File("out/host/common/obj/JAVA_LIBRARIES/kxml2-2.3.0_intermediates/javalib.jar").getAbsoluteFile(), - new File("out/target/common/obj/JAVA_LIBRARIES/core-tests-luni_intermediates/classes.jar").getAbsoluteFile()); - - Mode(Environment environment, File sdkJar, List<String> javacArgs, int monitorPort) { + protected final Classpath classpath = new Classpath(); + + Mode(Environment environment, File sdkJar, List<String> javacArgs, + int monitorPort, Classpath classpath) { this.environment = environment; this.sdkJar = sdkJar; this.javacArgs = javacArgs; this.monitorPort = monitorPort; + this.classpath.addAll(classpath); } /** * Initializes the temporary directories and harness necessary to run * actions. */ - protected void prepare(Set<File> runnerJava, Classpath runnerClasspath) { - this.runnerJava.add(new File(Vogar.HOME_JAVA, "vogar/target/TestRunner.java")); - this.runnerJava.addAll(dalvikAnnotationSourceFiles()); - this.runnerJava.addAll(runnerJava); - this.runnerClasspath.addAll(runnerClasspath); + protected void prepare(Set<RunnerSpec> runners) { + for (RunnerSpec runnerSpec : runners) { + runnerJava.add(runnerSpec.getSource()); + classpath.addAll(runnerSpec.getClasspath()); + } + runnerJava.add(new File(Vogar.HOME_JAVA, "vogar/target/TestRunner.java")); environment.prepare(); - compileRunner(); + classpath.addAll(compileRunner()); + installRunner(); } private List<File> dalvikAnnotationSourceFiles() { @@ -106,31 +98,27 @@ abstract class Mode { return Arrays.asList(javaSourceFiles); } - private void compileRunner() { + /** + * Returns a .jar file containing the compiled runner .java files. + */ + private File compileRunner() { logger.fine("build runner"); - - Classpath classpath = new Classpath(); - classpath.addAll(this.classpath); - classpath.addAll(runnerClasspath); - - File base = environment.runnerClassesDir(); - new Mkdir().mkdirs(base); + File classes = environment.file("runner", "classes"); + File jar = environment.hostJar("runner"); + new Mkdir().mkdirs(classes); new Javac() .bootClasspath(sdkJar) .classpath(classpath) .sourcepath(Vogar.HOME_JAVA) - .destination(base) + .destination(classes) .extra(javacArgs) .compile(runnerJava); - postCompileRunner(); + new Command("jar", "cvfM", jar.getPath(), + "-C", classes.getPath(), "./").execute(); + return jar; } /** - * Hook method called after runner compilation. - */ - abstract protected void postCompileRunner(); - - /** * Compiles classes for the given action and makes them ready for execution. * * @return null if the compilation succeeded, or an outcome describing the @@ -140,7 +128,8 @@ abstract class Mode { logger.fine("build " + action.getName()); try { - compile(action); + File jar = compile(action); + postCompile(action, jar); } catch (CommandFailedException e) { return new Outcome(action.getName(), action.getName(), Result.COMPILE_FAILED, e.getOutputLines()); @@ -152,17 +141,12 @@ abstract class Mode { } /** - * Compiles the classes for the described action. + * Returns the .jar file containing the action's compiled classes. * * @throws CommandFailedException if javac fails */ - private void compile(Action action) throws IOException { - if (!JAVA_SOURCE_PATTERN.matcher(action.getJavaFile().toString()).find()) { - throw new CommandFailedException(Collections.<String>emptyList(), - Collections.singletonList("Cannot compile: " + action.getJavaFile())); - } - - File classesDir = environment.classesDir(action); + private File compile(Action action) throws IOException { + File classesDir = environment.file(action, "classes"); new Mkdir().mkdirs(classesDir); FileOutputStream propertiesOut = new FileOutputStream( new File(classesDir, TestProperties.FILE)); @@ -171,30 +155,32 @@ abstract class Mode { properties.store(propertiesOut, "generated by " + Mode.class.getName()); propertiesOut.close(); - Classpath classpath = new Classpath(); - classpath.addAll(this.classpath); - classpath.addAll(action.getRunnerClasspath()); + Javac javac = new Javac(); Set<File> sourceFiles = new HashSet<File>(); - sourceFiles.add(action.getJavaFile()); sourceFiles.addAll(dalvikAnnotationSourceFiles()); - // compile the action case - new Javac() - .bootClasspath(sdkJar) + File javaFile = action.getJavaFile(); + if (javaFile != null) { + if (!JAVA_SOURCE_PATTERN.matcher(javaFile.toString()).find()) { + throw new CommandFailedException(Collections.<String>emptyList(), + Collections.singletonList("Cannot compile: " + javaFile)); + } + sourceFiles.add(javaFile); + javac.sourcepath(javaFile.getParentFile()); + } + + javac.bootClasspath(sdkJar) .classpath(classpath) - .sourcepath(action.getJavaDirectory()) .destination(classesDir) .extra(javacArgs) .compile(sourceFiles); - postCompile(action); - } - - /** - * Hook method called after action compilation. - */ - abstract protected void postCompile(Action action); + File jar = environment.hostJar(action); + new Command("jar", "cvfM", jar.getPath(), + "-C", classesDir.getPath(), "./").execute(); + return jar; + } /** * Fill in properties for running in this mode @@ -202,11 +188,21 @@ abstract class Mode { protected void fillInProperties(Properties properties, Action action) { properties.setProperty(TestProperties.TEST_CLASS, action.getTargetClass()); properties.setProperty(TestProperties.QUALIFIED_NAME, action.getName()); - properties.setProperty(TestProperties.RUNNER_CLASS, action.getRunnerClass().getName()); + properties.setProperty(TestProperties.RUNNER_CLASS, action.getRunnerSpec().getRunnerClass().getName()); properties.setProperty(TestProperties.MONITOR_PORT, String.valueOf(monitorPort)); } /** + * Hook method called after runner compilation. + */ + protected void installRunner() {} + + /** + * Hook method called after action compilation. + */ + protected void postCompile(Action action, File jar) {} + + /** * Create the command that executes the action. */ protected abstract Command createActionCommand(Action action); diff --git a/tools/runner/java/vogar/NamingPatternCodeFinder.java b/tools/runner/java/vogar/NamingPatternRunnerSpec.java index d87a35f..429bab9 100644 --- a/tools/runner/java/vogar/NamingPatternCodeFinder.java +++ b/tools/runner/java/vogar/NamingPatternRunnerSpec.java @@ -27,7 +27,7 @@ import java.util.regex.Pattern; * A code finder that traverses through the directory tree looking for matching * naming patterns. */ -abstract class NamingPatternCodeFinder implements CodeFinder { +abstract class NamingPatternRunnerSpec implements RunnerSpec { private final String PACKAGE_PATTERN = "(?m)^\\s*package\\s+(\\S+)\\s*;"; @@ -61,10 +61,7 @@ abstract class NamingPatternCodeFinder implements CodeFinder { } String className = fileToClass(file); - File directory = file.getParentFile(); - String description = null; - sink.add(new Action(className, className, directory, file, description, - getRunnerClass(), getRunnerJava(), getRunnerClasspath())); + sink.add(new Action(className, className, null, file, this)); } /** diff --git a/tools/runner/java/vogar/CodeFinder.java b/tools/runner/java/vogar/RunnerSpec.java index 4437848..73a4ce1 100644 --- a/tools/runner/java/vogar/CodeFinder.java +++ b/tools/runner/java/vogar/RunnerSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2010 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. @@ -21,28 +21,36 @@ import java.util.Set; import vogar.target.Runner; /** - * A strategy for finding runnable things in a directory. + * Defines a runner for a type of Java code, such as a JUnit test, benchmark, + * or class with main method. */ -public interface CodeFinder { +public interface RunnerSpec { /** * Returns all actions in the given file or directory. If the returned set * is empty, no executable code of this kind were found. */ - public Set<Action> findActions(File file); + Set<Action> findActions(File file); + + /** + * Returns true if this runner can exercise {@code clazz}. + * + * @param clazz a fully qualified classname. + */ + boolean supports(String clazz); /** * Return the class for the TestRunner */ - public Class<? extends Runner> getRunnerClass(); + Class<? extends Runner> getRunnerClass(); /** * Return the Java file for the TestRunner */ - public File getRunnerJava(); + File getSource(); /** * Return the compile classpath for the TestRunner */ - public Classpath getRunnerClasspath(); + Classpath getClasspath(); } diff --git a/tools/runner/java/vogar/TestProperties.java b/tools/runner/java/vogar/TestProperties.java index d35f349..d9a5b7b 100644 --- a/tools/runner/java/vogar/TestProperties.java +++ b/tools/runner/java/vogar/TestProperties.java @@ -57,22 +57,5 @@ final public class TestProperties { */ public static final String MONITOR_PORT = "monitorPort"; - /** - * Result value for successful test - */ - public static final String RESULT_SUCCESS = "SUCCESS"; - - /** - * Result value for failed test - */ - public static final String RESULT_FAILURE = "FAILURE"; - - public static String result(boolean success) { - return success ? RESULT_SUCCESS : RESULT_FAILURE; - } - - /** - * This class should not be instantiated - */ private TestProperties() {} } diff --git a/tools/runner/java/vogar/Vm.java b/tools/runner/java/vogar/Vm.java index 4edb0bc..350b861 100644 --- a/tools/runner/java/vogar/Vm.java +++ b/tools/runner/java/vogar/Vm.java @@ -35,8 +35,9 @@ public abstract class Vm extends Mode { protected final List<String> targetArgs; Vm(Environment environment, File sdkJar, List<String> javacArgs, - List<String> additionalVmArgs, List<String> targetArgs, int monitorPort) { - super(environment, sdkJar, javacArgs, monitorPort); + List<String> additionalVmArgs, List<String> targetArgs, int monitorPort, + Classpath classpath) { + super(environment, sdkJar, javacArgs, monitorPort, classpath); this.additionalVmArgs = additionalVmArgs; this.targetArgs = targetArgs; } @@ -46,7 +47,7 @@ public abstract class Vm extends Mode { */ @Override protected Command createActionCommand(Action action) { return newVmCommandBuilder(action.getUserDir()) - .classpath(getRuntimeSupportClasspath(action)) + .classpath(getRuntimeClasspath(action)) .userDir(action.getUserDir()) .debugPort(environment.debugPort) .vmArgs(additionalVmArgs) @@ -64,7 +65,7 @@ public abstract class Vm extends Mode { * Returns the classpath containing JUnit and the dalvik annotations * required for action execution. */ - protected abstract Classpath getRuntimeSupportClasspath(Action action); + protected abstract Classpath getRuntimeClasspath(Action action); /** * Builds a virtual machine command. diff --git a/tools/runner/java/vogar/Vogar.java b/tools/runner/java/vogar/Vogar.java index 2e9e3a9..6286d87 100644 --- a/tools/runner/java/vogar/Vogar.java +++ b/tools/runner/java/vogar/Vogar.java @@ -24,6 +24,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.regex.Pattern; /** * Command line interface for running benchmarks and tests on dalvik. @@ -33,9 +34,17 @@ public final class Vogar { static final File HOME = new File("dalvik/libcore/tools/runner"); static final File HOME_JAVA = new File(HOME, "java"); + static final Pattern CLASS_NAME_PATTERN + = Pattern.compile("(\\w+)(\\.\\w+)*\\.[A-Z]\\w*"); + private static class Options { + private final Classpath classpath = Classpath.of( + // TODO: we should be able to work with a shipping SDK, not depend on out/... + new File("out/host/common/obj/JAVA_LIBRARIES/kxml2-2.3.0_intermediates/javalib.jar").getAbsoluteFile()); + private final List<File> actionFiles = new ArrayList<File>(); + private final List<String> actionClasses = new ArrayList<String>(); private final List<String> targetArgs = new ArrayList<String>(); @Option(names = { "--expectations" }) @@ -101,8 +110,9 @@ public final class Vogar { private void printUsage() { System.out.println("Usage: Vogar [options]... <actions>... [target args]..."); System.out.println(); - System.out.println(" <actions>: .java files or directories containing jtreg tests, JUnit"); - System.out.println(" tests, Caliper benchmarks, or executable Java classes."); + System.out.println(" <actions>: .java files, .jar files, directories, or class names."); + System.out.println(" These should be JUnit tests, jtreg tests, Caliper benchmarks"); + System.out.println(" or executable Java classes."); System.out.println(); System.out.println(" [args]: arguments passed to the target process. This is only useful when"); System.out.println(" the target process is a Caliper benchmark or main method."); @@ -258,23 +268,31 @@ public final class Vogar { } // separate the actions and the target args - boolean action = true; - for (String arg : actionsAndTargetArgs) { + int index = 0; + for (; index < actionsAndTargetArgs.size(); index++) { + String arg = actionsAndTargetArgs.get(index); if (arg.equals("--")) { - action = false; - continue; + index++; + break; } - File actionFile = new File(arg); - if (action && actionFile.exists()) { - actionFiles.add(actionFile); + File file = new File(arg); + if (file.exists()) { + if (arg.endsWith(".jar")) { + classpath.addAll(file); + } else { + actionFiles.add(file); + } + } else if (CLASS_NAME_PATTERN.matcher(arg).matches()) { + actionClasses.add(arg); } else { - targetArgs.add(arg); - action = false; + break; } } - if (actionFiles.isEmpty()) { + targetArgs.addAll(actionsAndTargetArgs.subList(index, actionsAndTargetArgs.size())); + + if (actionFiles.isEmpty() && actionClasses.isEmpty()) { System.out.println("No actions provided."); return false; } @@ -311,7 +329,8 @@ public final class Vogar { options.targetArgs, options.cleanBefore, options.cleanAfter, - options.deviceRunnerDir); + options.deviceRunnerDir, + options.classpath); } else if (options.mode.equals(Options.MODE_HOST)) { monitorPort = 8788; mode = new JavaVm( @@ -324,8 +343,8 @@ public final class Vogar { options.vmArgs, options.targetArgs, options.cleanBefore, - options.cleanAfter - ); + options.cleanAfter, + options.classpath); } else if (options.mode.equals(Options.MODE_ACTIVITY)) { monitorPort = 8787; mode = new ActivityMode( @@ -336,7 +355,8 @@ public final class Vogar { localTemp, options.cleanBefore, options.cleanAfter, - options.deviceRunnerDir); + options.deviceRunnerDir, + options.classpath); } else { System.out.println("Unknown mode mode " + options.mode + "."); return; @@ -344,11 +364,11 @@ public final class Vogar { HostMonitor monitor = new HostMonitor(options.monitorTimeout); - List<CodeFinder> codeFinders = Arrays.asList( - new JtregFinder(localTemp), - new JUnitFinder(), - new CaliperFinder(), - new MainFinder()); + List<RunnerSpec> runnerSpecs = Arrays.asList( + new JtregSpec(localTemp), + new JUnitSpec(), + new CaliperSpec(), + new MainSpec()); ExpectationStore expectationStore; try { @@ -366,16 +386,14 @@ public final class Vogar { localTemp, mode, expectationStore, - codeFinders, + runnerSpecs, xmlReportPrinter, console, monitor, monitorPort, options.timeoutSeconds); - driver.buildAndRunAllActions(options.actionFiles); - - mode.shutdown(); + driver.buildAndRun(options.actionFiles, options.actionClasses); } public static void main(String[] args) { diff --git a/tools/runner/java/vogar/commands/Dx.java b/tools/runner/java/vogar/commands/Dx.java index 0fcf280..d2e3d00 100644 --- a/tools/runner/java/vogar/commands/Dx.java +++ b/tools/runner/java/vogar/commands/Dx.java @@ -33,6 +33,7 @@ public final class Dx { * Converts all the .class files on 'classpath' into a dex file written to 'output'. */ public void dex(File output, Classpath classpath) { + output.getParentFile().mkdirs(); File key = DEX_CACHE.makeKey(classpath); if (key != null && key.exists()) { logger.fine("dex cache hit for " + classpath); diff --git a/tools/runner/java/vogar/target/TestRunner.java b/tools/runner/java/vogar/target/TestRunner.java index 739cbc5..9419e6f 100644 --- a/tools/runner/java/vogar/target/TestRunner.java +++ b/tools/runner/java/vogar/target/TestRunner.java @@ -75,7 +75,7 @@ public class TestRunner { } } - public void run(String[] args) { + public void run(String... args) { final TargetMonitor monitor = new TargetMonitor(); monitor.await(monitorPort); diff --git a/tools/runner/lib/TestActivity.java b/tools/runner/lib/TestActivity.java index 39a6575..6e6d09f 100644 --- a/tools/runner/lib/TestActivity.java +++ b/tools/runner/lib/TestActivity.java @@ -48,7 +48,7 @@ public class TestActivity extends Activity { 1, Threads.daemonThreadFactory()); executor.submit(new Runnable() { public void run() { - new TestRunner().run(args); + new TestRunner().run(); } }); executor.shutdown(); diff --git a/tools/runner/vogar b/tools/runner/vogar index 4e20ac3..470b029 100755 --- a/tools/runner/vogar +++ b/tools/runner/vogar @@ -21,6 +21,5 @@ android_root=`dirname $0`/../../../.. cd ${android_root} classpath=${android_root}/out/host/linux-x86/framework/dalvik_runner.jar -core_jar=${android_root}/out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar -exec java -cp $classpath vogar.Vogar --sdk ${core_jar} "$@" +exec java -cp $classpath vogar.Vogar "$@" |