| /* |
| * 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. |
| * 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.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; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.concurrent.TimeoutException; |
| import java.util.logging.Logger; |
| import java.util.regex.Pattern; |
| |
| /** |
| * A Mode for running tests. Examples including running in a virtual |
| * machine either on the host or a device or within a specific context |
| * such as within an Activity. |
| */ |
| abstract class Mode { |
| |
| private static final Pattern JAVA_TEST_PATTERN = Pattern.compile("\\/(\\w)+\\.java$"); |
| |
| private static final Logger logger = Logger.getLogger(Mode.class.getName()); |
| |
| 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 |
| * set of tests. We build a subset rather than all the files all |
| * the time to reduce dex packaging costs in the activity mode |
| * case. |
| */ |
| protected final Set<File> testRunnerJava = new HashSet<File>(); |
| |
| /** |
| * Classpath of testRunner on the host side including any |
| * supporting libraries for testRunnerJava. Useful for compiling |
| * testRunnerJava as well as executing it on the host. Execution |
| * on the device requires further packaging typically done by |
| * postCompileTestRunner. |
| */ |
| protected final Classpath testRunnerClasspath = new Classpath(); |
| |
| // TODO: this should be an immutable collection. |
| protected final Classpath testClasspath = 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/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/classes.jar").getAbsoluteFile()); |
| |
| Mode(Environment environment, long timeoutSeconds, File sdkJar, PrintStream tee) { |
| this.environment = environment; |
| this.timeoutSeconds = timeoutSeconds; |
| this.sdkJar = sdkJar; |
| this.tee = tee; |
| } |
| |
| /** |
| * Initializes the temporary directories and test harness necessary to run |
| * tests. |
| */ |
| protected void prepare(Set<File> testRunnerJava, Classpath testRunnerClasspath) { |
| this.testRunnerJava.add(new File(DalvikRunner.HOME_JAVA, "dalvik/runner/TestRunner.java")); |
| this.testRunnerJava.addAll(dalvikAnnotationSourceFiles()); |
| this.testRunnerJava.addAll(testRunnerJava); |
| this.testRunnerClasspath.addAll(testRunnerClasspath); |
| environment.prepare(); |
| compileTestRunner(); |
| } |
| |
| private List<File> dalvikAnnotationSourceFiles() { |
| // Hopefully one day we'll strip the dalvik annotations out, but until then we need to make |
| // them available to javac(1). |
| File sourceDir = new File("dalvik/libcore/dalvik/src/main/java/dalvik/annotation"); |
| File[] javaSourceFiles = sourceDir.listFiles(new FilenameFilter() { |
| public boolean accept(File dir, String filename) { |
| return filename.endsWith(".java"); |
| } |
| }); |
| return Arrays.asList(javaSourceFiles); |
| } |
| |
| private void compileTestRunner() { |
| logger.fine("build testrunner"); |
| |
| Classpath classpath = new Classpath(); |
| classpath.addAll(testClasspath); |
| classpath.addAll(testRunnerClasspath); |
| |
| File base = environment.testRunnerClassesDir(); |
| new Mkdir().mkdirs(base); |
| new Javac() |
| .bootClasspath(sdkJar) |
| .classpath(classpath) |
| .sourcepath(DalvikRunner.HOME_JAVA) |
| .destination(base) |
| .compile(testRunnerJava); |
| postCompileTestRunner(); |
| } |
| |
| /** |
| * Hook method called after TestRunner compilation. |
| */ |
| abstract protected void postCompileTestRunner(); |
| |
| /** |
| * Compiles classes for the given test and makes them ready for execution. |
| * If the test could not be compiled successfully, it will be updated with |
| * the appropriate test result. |
| */ |
| public void buildAndInstall(TestRun testRun) { |
| logger.fine("build " + testRun.getQualifiedName()); |
| |
| boolean testCompiled; |
| try { |
| testCompiled = compileTest(testRun); |
| if (!testCompiled) { |
| testRun.setResult(Result.UNSUPPORTED, Collections.<String>emptyList()); |
| return; |
| } |
| } catch (CommandFailedException e) { |
| testRun.setResult(Result.COMPILE_FAILED, e.getOutputLines()); |
| return; |
| } catch (IOException e) { |
| testRun.setResult(Result.ERROR, e); |
| return; |
| } |
| testRun.setTestCompiled(testCompiled); |
| environment.prepareUserDir(testRun); |
| } |
| |
| /** |
| * Compiles the classes for the described test. |
| * |
| * @return the path to the compiled classes (directory or jar), or {@code |
| * null} if the test could not be compiled. |
| * @throws CommandFailedException if javac fails |
| */ |
| private boolean compileTest(TestRun testRun) throws IOException { |
| if (!JAVA_TEST_PATTERN.matcher(testRun.getTestJava().toString()).find()) { |
| return false; |
| } |
| |
| String qualifiedName = testRun.getQualifiedName(); |
| File testClassesDir = environment.testClassesDir(testRun); |
| new Mkdir().mkdirs(testClassesDir); |
| FileOutputStream propertiesOut = new FileOutputStream( |
| new File(testClassesDir, TestProperties.FILE)); |
| Properties properties = new Properties(); |
| fillInProperties(properties, testRun); |
| properties.store(propertiesOut, "generated by " + Mode.class.getName()); |
| propertiesOut.close(); |
| |
| Classpath classpath = new Classpath(); |
| classpath.addAll(testClasspath); |
| classpath.addAll(testRun.getRunnerClasspath()); |
| |
| Set<File> sourceFiles = new HashSet<File>(); |
| sourceFiles.add(testRun.getTestJava()); |
| sourceFiles.addAll(dalvikAnnotationSourceFiles()); |
| |
| // compile the test case |
| new Javac() |
| .bootClasspath(sdkJar) |
| .classpath(classpath) |
| .sourcepath(testRun.getTestDirectory()) |
| .destination(testClassesDir) |
| .compile(sourceFiles); |
| postCompileTest(testRun); |
| return true; |
| } |
| |
| /** |
| * Hook method called after test compilation. |
| * |
| * @param testRun The test being compiled |
| */ |
| abstract protected void postCompileTest(TestRun testRun); |
| |
| |
| /** |
| * Fill in properties for running in this mode |
| */ |
| protected void fillInProperties(Properties properties, TestRun testRun) { |
| properties.setProperty(TestProperties.TEST_CLASS, testRun.getTestClass()); |
| properties.setProperty(TestProperties.QUALIFIED_NAME, testRun.getQualifiedName()); |
| properties.setProperty(TestProperties.RUNNER_CLASS, testRun.getRunnerClass().getName()); |
| } |
| |
| /** |
| * Runs the test, and updates its test result. |
| */ |
| void runTest(TestRun testRun) { |
| if (!testRun.isRunnable()) { |
| throw new IllegalArgumentException(); |
| } |
| |
| List<String> 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()) { |
| testRun.setResult(Result.ERROR, |
| Collections.singletonList("No output returned!")); |
| return; |
| } |
| |
| Result result = TestProperties.RESULT_SUCCESS.equals(output.get(output.size() - 1)) |
| ? Result.SUCCESS |
| : Result.EXEC_FAILED; |
| testRun.setResult(result, output.subList(0, output.size() - 1)); |
| } |
| |
| /** |
| * Run the actual test to gather output |
| */ |
| protected abstract List<String> runTestCommand(TestRun testRun) |
| throws TimeoutException; |
| |
| /** |
| * Deletes files and releases any resources required for the execution of |
| * the given test. |
| */ |
| void cleanup(TestRun testRun) { |
| environment.cleanup(testRun); |
| } |
| |
| /** |
| * Cleans up after all test runs have completed. |
| */ |
| void shutdown() { |
| environment.shutdown(); |
| } |
| } |