2 * Copyright (C) 2010 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package dalvik.runner;
20 import java.io.FileOutputStream;
21 import java.io.FilenameFilter;
22 import java.io.IOException;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Properties;
29 import java.util.concurrent.TimeoutException;
30 import java.util.logging.Logger;
31 import java.util.regex.Pattern;
34 * A Mode for running tests. Examples including running in a virtual
35 * machine either on the host or a device or within a specific context
36 * such as within an Activity.
40 private static final Pattern JAVA_TEST_PATTERN = Pattern.compile("\\/(\\w)+\\.java$");
42 private static final Logger logger = Logger.getLogger(Mode.class.getName());
44 protected final Environment environment;
45 protected final long timeoutSeconds;
46 protected final File sdkJar;
49 * Set of Java files needed to built to tun the currently selected
50 * set of tests. We build a subset rather than all the files all
51 * the time to reduce dex packaging costs in the activity mode
54 protected final Set<File> testRunnerJava = new HashSet<File>();
57 * Classpath of testRunner on the host side including any
58 * supporting libraries for testRunnerJava. Useful for compiling
59 * testRunnerJava as well as executing it on the host. Execution
60 * on the device requires further packaging typically done by
61 * postCompileTestRunner.
63 protected final Classpath testRunnerClasspath = new Classpath();
65 // TODO: this should be an immutable collection.
66 protected final Classpath testClasspath = Classpath.of(
67 new File("dalvik/libcore/tools/runner/lib/jsr305.jar"),
68 new File("dalvik/libcore/tools/runner/lib/guava.jar"),
69 new File("dalvik/libcore/tools/runner/lib/caliper.jar"),
70 // TODO: we should be able to work with a shipping SDK, not depend on out/...
71 // dalvik/libcore/**/test/ for junit
72 // TODO: jar up just the junit classes and drop the jar in our lib/ directory.
73 new File("out/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/classes.jar").getAbsoluteFile());
75 Mode(Environment environment, long timeoutSeconds, File sdkJar) {
76 this.environment = environment;
77 this.timeoutSeconds = timeoutSeconds;
82 * Initializes the temporary directories and test harness necessary to run
85 protected void prepare(Set<File> testRunnerJava, Classpath testRunnerClasspath) {
86 this.testRunnerJava.add(new File(DalvikRunner.HOME_JAVA, "dalvik/runner/TestRunner.java"));
87 this.testRunnerJava.addAll(dalvikAnnotationSourceFiles());
88 this.testRunnerJava.addAll(testRunnerJava);
89 this.testRunnerClasspath.addAll(testRunnerClasspath);
90 environment.prepare();
94 private List<File> dalvikAnnotationSourceFiles() {
95 // Hopefully one day we'll strip the dalvik annotations out, but until then we need to make
96 // them available to javac(1).
97 File sourceDir = new File("dalvik/libcore/dalvik/src/main/java/dalvik/annotation");
98 File[] javaSourceFiles = sourceDir.listFiles(new FilenameFilter() {
99 public boolean accept(File dir, String filename) {
100 return filename.endsWith(".java");
103 return Arrays.asList(javaSourceFiles);
106 private void compileTestRunner() {
107 logger.fine("build testrunner");
109 Classpath classpath = new Classpath();
110 classpath.addAll(testClasspath);
111 classpath.addAll(testRunnerClasspath);
113 File base = environment.testRunnerClassesDir();
114 new Mkdir().mkdirs(base);
116 .bootClasspath(sdkJar)
117 .classpath(classpath)
118 .sourcepath(DalvikRunner.HOME_JAVA)
120 .compile(testRunnerJava);
121 postCompileTestRunner();
125 * Hook method called after TestRunner compilation.
127 abstract protected void postCompileTestRunner();
130 * Compiles classes for the given test and makes them ready for execution.
131 * If the test could not be compiled successfully, it will be updated with
132 * the appropriate test result.
134 public void buildAndInstall(TestRun testRun) {
135 logger.fine("build " + testRun.getQualifiedName());
137 boolean testCompiled;
139 testCompiled = compileTest(testRun);
141 testRun.setResult(Result.UNSUPPORTED, Collections.<String>emptyList());
144 } catch (CommandFailedException e) {
145 testRun.setResult(Result.COMPILE_FAILED, e.getOutputLines());
147 } catch (IOException e) {
148 testRun.setResult(Result.ERROR, e);
151 testRun.setTestCompiled(testCompiled);
152 environment.prepareUserDir(testRun);
156 * Compiles the classes for the described test.
158 * @return the path to the compiled classes (directory or jar), or {@code
159 * null} if the test could not be compiled.
160 * @throws CommandFailedException if javac fails
162 private boolean compileTest(TestRun testRun) throws IOException {
163 if (!JAVA_TEST_PATTERN.matcher(testRun.getTestJava().toString()).find()) {
167 String qualifiedName = testRun.getQualifiedName();
168 File testClassesDir = environment.testClassesDir(testRun);
169 new Mkdir().mkdirs(testClassesDir);
170 FileOutputStream propertiesOut = new FileOutputStream(
171 new File(testClassesDir, TestProperties.FILE));
172 Properties properties = new Properties();
173 fillInProperties(properties, testRun);
174 properties.store(propertiesOut, "generated by " + Mode.class.getName());
175 propertiesOut.close();
177 Classpath classpath = new Classpath();
178 classpath.addAll(testClasspath);
179 classpath.addAll(testRun.getRunnerClasspath());
181 Set<File> sourceFiles = new HashSet<File>();
182 sourceFiles.add(testRun.getTestJava());
183 sourceFiles.addAll(dalvikAnnotationSourceFiles());
185 // compile the test case
187 .bootClasspath(sdkJar)
188 .classpath(classpath)
189 .sourcepath(testRun.getTestDirectory())
190 .destination(testClassesDir)
191 .compile(sourceFiles);
192 postCompileTest(testRun);
197 * Hook method called after test compilation.
199 * @param testRun The test being compiled
201 abstract protected void postCompileTest(TestRun testRun);
205 * Fill in properties for running in this mode
207 protected void fillInProperties(Properties properties, TestRun testRun) {
208 properties.setProperty(TestProperties.TEST_CLASS, testRun.getTestClass());
209 properties.setProperty(TestProperties.QUALIFIED_NAME, testRun.getQualifiedName());
210 properties.setProperty(TestProperties.RUNNER_CLASS, testRun.getRunnerClass().getName());
214 * Runs the test, and updates its test result.
216 void runTest(TestRun testRun) {
217 if (!testRun.isRunnable()) {
218 throw new IllegalArgumentException();
221 final List<Command> commands = buildCommands(testRun);
223 List<String> output = null;
224 for (final Command command : commands) {
226 output = command.executeWithTimeout(timeoutSeconds);
227 } catch (TimeoutException e) {
228 testRun.setResult(Result.EXEC_TIMEOUT,
229 Collections.singletonList("Exceeded timeout! (" + timeoutSeconds + "s)"));
231 } catch (Exception e) {
232 testRun.setResult(Result.ERROR, e);
236 // we only look at the output of the last command
237 if (output.isEmpty()) {
238 testRun.setResult(Result.ERROR,
239 Collections.singletonList("No output returned!"));
243 Result result = TestProperties.RESULT_SUCCESS.equals(output.get(output.size() - 1))
245 : Result.EXEC_FAILED;
246 testRun.setResult(result, output.subList(0, output.size() - 1));
250 * Returns commands for test execution.
252 protected abstract List<Command> buildCommands(TestRun testRun);
255 * Deletes files and releases any resources required for the execution of
258 void cleanup(TestRun testRun) {
259 environment.cleanup(testRun);
263 * Cleans up after all test runs have completed.
266 environment.shutdown();