blob: 773d7a95468c84657f625731b30ce669f5be509b [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17package android.test;
18
19import static android.test.suitebuilder.TestPredicates.REJECT_PERFORMANCE;
Jack Wangff1df692009-08-26 17:19:13 -070020
21import com.android.internal.util.Predicate;
22
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.app.Activity;
24import android.app.Instrumentation;
25import android.os.Bundle;
26import android.os.Debug;
27import android.os.Looper;
Jack Wangff1df692009-08-26 17:19:13 -070028import android.os.Parcelable;
29import android.os.PerformanceCollector;
30import android.os.Process;
31import android.os.SystemClock;
32import android.os.PerformanceCollector.PerformanceResultsWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.test.suitebuilder.TestMethod;
34import android.test.suitebuilder.TestPredicates;
35import android.test.suitebuilder.TestSuiteBuilder;
36import android.util.Log;
37
Jack Wangff1df692009-08-26 17:19:13 -070038import java.io.ByteArrayOutputStream;
39import java.io.File;
40import java.io.PrintStream;
41import java.lang.reflect.InvocationTargetException;
42import java.lang.reflect.Method;
43import java.util.ArrayList;
44import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045
46import junit.framework.AssertionFailedError;
47import junit.framework.Test;
48import junit.framework.TestCase;
49import junit.framework.TestListener;
50import junit.framework.TestResult;
51import junit.framework.TestSuite;
52import junit.runner.BaseTestRunner;
53import junit.textui.ResultPrinter;
54
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055/**
56 * An {@link Instrumentation} that runs various types of {@link junit.framework.TestCase}s against
57 * an Android package (application). Typical usage:
58 * <ol>
59 * <li>Write {@link junit.framework.TestCase}s that perform unit, functional, or performance tests
60 * against the classes in your package. Typically these are subclassed from:
Jack Wangff1df692009-08-26 17:19:13 -070061 * <ul><li>{@link android.test.ActivityInstrumentationTestCase2}</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 * <li>{@link android.test.ActivityUnitTestCase}</li>
63 * <li>{@link android.test.AndroidTestCase}</li>
64 * <li>{@link android.test.ApplicationTestCase}</li>
65 * <li>{@link android.test.InstrumentationTestCase}</li>
66 * <li>{@link android.test.ProviderTestCase}</li>
67 * <li>{@link android.test.ServiceTestCase}</li>
68 * <li>{@link android.test.SingleLaunchActivityTestCase}</li></ul>
69 * <li>In an appropriate AndroidManifest.xml, define the this instrumentation with
70 * the appropriate android:targetPackage set.
71 * <li>Run the instrumentation using "adb shell am instrument -w",
72 * with no optional arguments, to run all tests (except performance tests).
73 * <li>Run the instrumentation using "adb shell am instrument -w",
74 * with the argument '-e func true' to run all functional tests. These are tests that derive from
75 * {@link android.test.InstrumentationTestCase}.
76 * <li>Run the instrumentation using "adb shell am instrument -w",
77 * with the argument '-e unit true' to run all unit tests. These are tests that <i>do not</i>derive
78 * from {@link android.test.InstrumentationTestCase} (and are not performance tests).
79 * <li>Run the instrumentation using "adb shell am instrument -w",
80 * with the argument '-e class' set to run an individual {@link junit.framework.TestCase}.
81 * </ol>
82 * <p/>
83 * <b>Running all tests:</b> adb shell am instrument -w
84 * com.android.foo/android.test.InstrumentationTestRunner
85 * <p/>
86 * <b>Running all small tests:</b> adb shell am instrument -w
87 * -e size small
88 * com.android.foo/android.test.InstrumentationTestRunner
89 * <p/>
90 * <b>Running all medium tests:</b> adb shell am instrument -w
91 * -e size medium
92 * com.android.foo/android.test.InstrumentationTestRunner
93 * <p/>
94 * <b>Running all large tests:</b> adb shell am instrument -w
95 * -e size large
96 * com.android.foo/android.test.InstrumentationTestRunner
97 * <p/>
98 * <b>Running a single testcase:</b> adb shell am instrument -w
99 * -e class com.android.foo.FooTest
100 * com.android.foo/android.test.InstrumentationTestRunner
101 * <p/>
102 * <b>Running a single test:</b> adb shell am instrument -w
103 * -e class com.android.foo.FooTest#testFoo
104 * com.android.foo/android.test.InstrumentationTestRunner
105 * <p/>
106 * <b>Running multiple tests:</b> adb shell am instrument -w
107 * -e class com.android.foo.FooTest,com.android.foo.TooTest
108 * com.android.foo/android.test.InstrumentationTestRunner
109 * <p/>
110 * <b>Including performance tests:</b> adb shell am instrument -w
111 * -e perf true
112 * com.android.foo/android.test.InstrumentationTestRunner
113 * <p/>
114 * <b>To debug your tests, set a break point in your code and pass:</b>
115 * -e debug true
116 * <p/>
117 * <b>To run in 'log only' mode</b>
118 * -e log true
Jack Wangff1df692009-08-26 17:19:13 -0700119 * This option will load and iterate through all test classes and methods, but will bypass actual
120 * test execution. Useful for quickly obtaining info on the tests to be executed by an
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 * instrumentation command.
122 * <p/>
123 * <b>To generate EMMA code coverage:</b>
124 * -e coverage true
Jack Wangff1df692009-08-26 17:19:13 -0700125 * Note: this requires an emma instrumented build. By default, the code coverage results file
Brett Chabot51e03642009-05-28 18:18:15 -0700126 * will be saved in a /data/<app>/coverage.ec file, unless overridden by coverageFile flag (see
127 * below)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 * <p/>
129 * <b> To specify EMMA code coverage results file path:</b>
130 * -e coverageFile /sdcard/myFile.ec
131 * <br/>
132 * in addition to the other arguments.
133 */
134
135/* (not JavaDoc)
136 * Although not necessary in most case, another way to use this class is to extend it and have the
Jack Wangff1df692009-08-26 17:19:13 -0700137 * derived class return the desired test suite from the {@link #getTestSuite()} method. The test
138 * suite returned from this method will be used if no target class is defined in the meta-data or
139 * command line argument parameters. If a derived class is used it needs to be added as an
140 * instrumentation to the AndroidManifest.xml and the command to run it would look like:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 * <p/>
142 * adb shell am instrument -w com.android.foo/<i>com.android.FooInstrumentationTestRunner</i>
143 * <p/>
144 * Where <i>com.android.FooInstrumentationTestRunner</i> is the derived class.
145 *
146 * This model is used by many existing app tests, but can probably be deprecated.
147 */
148public class InstrumentationTestRunner extends Instrumentation implements TestSuiteProvider {
149
150 /** @hide */
151 public static final String ARGUMENT_TEST_CLASS = "class";
152 /** @hide */
153 public static final String ARGUMENT_TEST_PACKAGE = "package";
154 /** @hide */
155 public static final String ARGUMENT_TEST_SIZE_PREDICATE = "size";
156 /** @hide */
157 public static final String ARGUMENT_INCLUDE_PERF = "perf";
158 /** @hide */
159 public static final String ARGUMENT_DELAY_MSEC = "delay_msec";
160
161 private static final String SMALL_SUITE = "small";
Jack Wangff1df692009-08-26 17:19:13 -0700162 private static final String MEDIUM_SUITE = "medium";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 private static final String LARGE_SUITE = "large";
Jack Wangff1df692009-08-26 17:19:13 -0700164
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 private static final String ARGUMENT_LOG_ONLY = "log";
166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 /**
Jack Wangff1df692009-08-26 17:19:13 -0700168 * This constant defines the maximum allowed runtime (in ms) for a test included in the "small"
169 * suite. It is used to make an educated guess at what suite an unlabeled test belongs.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 */
171 private static final float SMALL_SUITE_MAX_RUNTIME = 100;
Jack Wangff1df692009-08-26 17:19:13 -0700172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 /**
Jack Wangff1df692009-08-26 17:19:13 -0700174 * This constant defines the maximum allowed runtime (in ms) for a test included in the
175 * "medium" suite. It is used to make an educated guess at what suite an unlabeled test belongs.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 */
177 private static final float MEDIUM_SUITE_MAX_RUNTIME = 1000;
Jack Wangff1df692009-08-26 17:19:13 -0700178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 /**
Jack Wangff1df692009-08-26 17:19:13 -0700180 * The following keys are used in the status bundle to provide structured reports to
181 * an IInstrumentationWatcher.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 */
183
184 /**
Jack Wangff1df692009-08-26 17:19:13 -0700185 * This value, if stored with key {@link android.app.Instrumentation#REPORT_KEY_IDENTIFIER},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 * identifies InstrumentationTestRunner as the source of the report. This is sent with all
187 * status messages.
188 */
189 public static final String REPORT_VALUE_ID = "InstrumentationTestRunner";
190 /**
Jack Wangff1df692009-08-26 17:19:13 -0700191 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 * identifies the total number of tests that are being run. This is sent with all status
193 * messages.
194 */
195 public static final String REPORT_KEY_NUM_TOTAL = "numtests";
196 /**
Jack Wangff1df692009-08-26 17:19:13 -0700197 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 * identifies the sequence number of the current test. This is sent with any status message
199 * describing a specific test being started or completed.
200 */
201 public static final String REPORT_KEY_NUM_CURRENT = "current";
202 /**
Jack Wangff1df692009-08-26 17:19:13 -0700203 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 * identifies the name of the current test class. This is sent with any status message
205 * describing a specific test being started or completed.
206 */
207 public static final String REPORT_KEY_NAME_CLASS = "class";
208 /**
Jack Wangff1df692009-08-26 17:19:13 -0700209 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 * identifies the name of the current test. This is sent with any status message
211 * describing a specific test being started or completed.
212 */
213 public static final String REPORT_KEY_NAME_TEST = "test";
214 /**
Jack Wangff1df692009-08-26 17:19:13 -0700215 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 * reports the run time in seconds of the current test.
217 */
218 private static final String REPORT_KEY_RUN_TIME = "runtime";
219 /**
Jack Wangff1df692009-08-26 17:19:13 -0700220 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 * reports the guessed suite assignment for the current test.
222 */
223 private static final String REPORT_KEY_SUITE_ASSIGNMENT = "suiteassignment";
224 /**
Brett Chabot51e03642009-05-28 18:18:15 -0700225 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
226 * identifies the path to the generated code coverage file.
227 */
228 private static final String REPORT_KEY_COVERAGE_PATH = "coverageFilePath";
229 /**
Jack Wang075997f2009-10-27 22:01:09 -0700230 * If included at the start of reporting keys, this prefix marks the key as a performance
231 * metric.
232 */
233 private static final String REPORT_KEY_PREFIX = "performance.";
234 /**
Jack Wangff1df692009-08-26 17:19:13 -0700235 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
236 * reports the cpu time in milliseconds of the current test.
237 */
238 private static final String REPORT_KEY_PERF_CPU_TIME =
Jack Wang075997f2009-10-27 22:01:09 -0700239 REPORT_KEY_PREFIX + PerformanceCollector.METRIC_KEY_CPU_TIME;
Jack Wangff1df692009-08-26 17:19:13 -0700240 /**
241 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
242 * reports the run time in milliseconds of the current test.
243 */
244 private static final String REPORT_KEY_PERF_EXECUTION_TIME =
Jack Wang075997f2009-10-27 22:01:09 -0700245 REPORT_KEY_PREFIX + PerformanceCollector.METRIC_KEY_EXECUTION_TIME;
Jack Wangff1df692009-08-26 17:19:13 -0700246
247 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 * The test is starting.
249 */
250 public static final int REPORT_VALUE_RESULT_START = 1;
251 /**
252 * The test completed successfully.
253 */
254 public static final int REPORT_VALUE_RESULT_OK = 0;
255 /**
256 * The test completed with an error.
257 */
258 public static final int REPORT_VALUE_RESULT_ERROR = -1;
259 /**
260 * The test completed with a failure.
261 */
262 public static final int REPORT_VALUE_RESULT_FAILURE = -2;
263 /**
Jack Wangff1df692009-08-26 17:19:13 -0700264 * If included in the status bundle sent to an IInstrumentationWatcher, this key
265 * identifies a stack trace describing an error or failure. This is sent with any status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 * message describing a specific test being completed.
267 */
268 public static final String REPORT_KEY_STACK = "stack";
269
Brett Chabot51e03642009-05-28 18:18:15 -0700270 // Default file name for code coverage
271 private static final String DEFAULT_COVERAGE_FILE_NAME = "coverage.ec";
Jack Wangff1df692009-08-26 17:19:13 -0700272
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 private static final String LOG_TAG = "InstrumentationTestRunner";
274
275 private final Bundle mResults = new Bundle();
276 private AndroidTestRunner mTestRunner;
277 private boolean mDebug;
278 private boolean mJustCount;
279 private boolean mSuiteAssignmentMode;
280 private int mTestCount;
281 private String mPackageOfTests;
282 private boolean mCoverage;
283 private String mCoverageFilePath;
284 private int mDelayMsec;
285
286 @Override
287 public void onCreate(Bundle arguments) {
288 super.onCreate(arguments);
289
290 // Apk paths used to search for test classes when using TestSuiteBuilders.
291 String[] apkPaths =
292 {getTargetContext().getPackageCodePath(), getContext().getPackageCodePath()};
293 ClassPathPackageInfoSource.setApkPaths(apkPaths);
294
295 Predicate<TestMethod> testSizePredicate = null;
296 boolean includePerformance = false;
297 String testClassesArg = null;
298 boolean logOnly = false;
299
300 if (arguments != null) {
301 // Test class name passed as an argument should override any meta-data declaration.
302 testClassesArg = arguments.getString(ARGUMENT_TEST_CLASS);
303 mDebug = getBooleanArgument(arguments, "debug");
304 mJustCount = getBooleanArgument(arguments, "count");
305 mSuiteAssignmentMode = getBooleanArgument(arguments, "suiteAssignment");
306 mPackageOfTests = arguments.getString(ARGUMENT_TEST_PACKAGE);
307 testSizePredicate = getSizePredicateFromArg(
308 arguments.getString(ARGUMENT_TEST_SIZE_PREDICATE));
309 includePerformance = getBooleanArgument(arguments, ARGUMENT_INCLUDE_PERF);
310 logOnly = getBooleanArgument(arguments, ARGUMENT_LOG_ONLY);
311 mCoverage = getBooleanArgument(arguments, "coverage");
312 mCoverageFilePath = arguments.getString("coverageFile");
313
314 try {
315 Object delay = arguments.get(ARGUMENT_DELAY_MSEC); // Accept either string or int
316 if (delay != null) mDelayMsec = Integer.parseInt(delay.toString());
317 } catch (NumberFormatException e) {
318 Log.e(LOG_TAG, "Invalid delay_msec parameter", e);
319 }
320 }
321
322 TestSuiteBuilder testSuiteBuilder = new TestSuiteBuilder(getClass().getName(),
323 getTargetContext().getClassLoader());
324
325 if (testSizePredicate != null) {
326 testSuiteBuilder.addRequirements(testSizePredicate);
327 }
328 if (!includePerformance) {
329 testSuiteBuilder.addRequirements(REJECT_PERFORMANCE);
330 }
331
332 if (testClassesArg == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 if (mPackageOfTests != null) {
334 testSuiteBuilder.includePackages(mPackageOfTests);
335 } else {
Brett Chabot61b10ac2009-03-31 17:04:34 -0700336 TestSuite testSuite = getTestSuite();
337 if (testSuite != null) {
338 testSuiteBuilder.addTestSuite(testSuite);
339 } else {
Jack Wangff1df692009-08-26 17:19:13 -0700340 // no package or class bundle arguments were supplied, and no test suite
Brett Chabot61b10ac2009-03-31 17:04:34 -0700341 // provided so add all tests in application
342 testSuiteBuilder.includePackages("");
343 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 }
345 } else {
346 parseTestClasses(testClassesArg, testSuiteBuilder);
347 }
Jack Wangff1df692009-08-26 17:19:13 -0700348
Urs Grobda13ef52009-04-17 11:30:14 -0700349 testSuiteBuilder.addRequirements(getBuilderRequirements());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350
351 mTestRunner = getAndroidTestRunner();
352 mTestRunner.setContext(getTargetContext());
Jack Wang7aba54b2009-08-20 19:20:54 -0700353 mTestRunner.setInstrumentation(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 mTestRunner.setSkipExecution(logOnly);
355 mTestRunner.setTest(testSuiteBuilder.build());
356 mTestCount = mTestRunner.getTestCases().size();
357 if (mSuiteAssignmentMode) {
358 mTestRunner.addTestListener(new SuiteAssignmentPrinter());
359 } else {
Jack Wangff1df692009-08-26 17:19:13 -0700360 WatcherResultPrinter resultPrinter = new WatcherResultPrinter(mTestCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 mTestRunner.addTestListener(new TestPrinter("TestRunner", false));
Jack Wangff1df692009-08-26 17:19:13 -0700362 mTestRunner.addTestListener(resultPrinter);
363 mTestRunner.setPerformanceResultsWriter(resultPrinter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 }
365 start();
366 }
367
Urs Grobda13ef52009-04-17 11:30:14 -0700368 List<Predicate<TestMethod>> getBuilderRequirements() {
369 return new ArrayList<Predicate<TestMethod>>();
370 }
371
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 /**
Jack Wangff1df692009-08-26 17:19:13 -0700373 * Parses and loads the specified set of test classes
374 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 * @param testClassArg - comma-separated list of test classes and methods
376 * @param testSuiteBuilder - builder to add tests to
377 */
378 private void parseTestClasses(String testClassArg, TestSuiteBuilder testSuiteBuilder) {
379 String[] testClasses = testClassArg.split(",");
380 for (String testClass : testClasses) {
381 parseTestClass(testClass, testSuiteBuilder);
382 }
383 }
384
385 /**
386 * Parse and load the given test class and, optionally, method
Jack Wangff1df692009-08-26 17:19:13 -0700387 *
388 * @param testClassName - full package name of test class and optionally method to add.
389 * Expected format: com.android.TestClass#testMethod
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 * @param testSuiteBuilder - builder to add tests to
391 */
392 private void parseTestClass(String testClassName, TestSuiteBuilder testSuiteBuilder) {
393 int methodSeparatorIndex = testClassName.indexOf('#');
394 String testMethodName = null;
395
396 if (methodSeparatorIndex > 0) {
397 testMethodName = testClassName.substring(methodSeparatorIndex + 1);
398 testClassName = testClassName.substring(0, methodSeparatorIndex);
399 }
Jack Wangff1df692009-08-26 17:19:13 -0700400 testSuiteBuilder.addTestClassByName(testClassName, testMethodName, getTargetContext());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 }
402
403 protected AndroidTestRunner getAndroidTestRunner() {
404 return new AndroidTestRunner();
405 }
406
407 private boolean getBooleanArgument(Bundle arguments, String tag) {
408 String tagString = arguments.getString(tag);
409 return tagString != null && Boolean.parseBoolean(tagString);
410 }
Jack Wangff1df692009-08-26 17:19:13 -0700411
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 /*
413 * Returns the size predicate object, corresponding to the "size" argument value.
414 */
415 private Predicate<TestMethod> getSizePredicateFromArg(String sizeArg) {
Jack Wangff1df692009-08-26 17:19:13 -0700416
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 if (SMALL_SUITE.equals(sizeArg)) {
418 return TestPredicates.SELECT_SMALL;
419 } else if (MEDIUM_SUITE.equals(sizeArg)) {
420 return TestPredicates.SELECT_MEDIUM;
421 } else if (LARGE_SUITE.equals(sizeArg)) {
422 return TestPredicates.SELECT_LARGE;
423 } else {
424 return null;
425 }
426 }
Jack Wangff1df692009-08-26 17:19:13 -0700427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 @Override
429 public void onStart() {
430 Looper.prepare();
Jack Wangff1df692009-08-26 17:19:13 -0700431
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 if (mJustCount) {
433 mResults.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
434 mResults.putInt(REPORT_KEY_NUM_TOTAL, mTestCount);
435 finish(Activity.RESULT_OK, mResults);
436 } else {
437 if (mDebug) {
438 Debug.waitForDebugger();
439 }
Jack Wangff1df692009-08-26 17:19:13 -0700440
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
442 PrintStream writer = new PrintStream(byteArrayOutputStream);
443 try {
444 StringResultPrinter resultPrinter = new StringResultPrinter(writer);
Jack Wangff1df692009-08-26 17:19:13 -0700445
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 mTestRunner.addTestListener(resultPrinter);
Jack Wangff1df692009-08-26 17:19:13 -0700447
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 long startTime = System.currentTimeMillis();
449 mTestRunner.runTest();
450 long runTime = System.currentTimeMillis() - startTime;
Jack Wangff1df692009-08-26 17:19:13 -0700451
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 resultPrinter.print(mTestRunner.getTestResult(), runTime);
453 } finally {
Jack Wangff1df692009-08-26 17:19:13 -0700454 mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
455 String.format("\nTest results for %s=%s",
456 mTestRunner.getTestClassName(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 byteArrayOutputStream.toString()));
458
459 if (mCoverage) {
460 generateCoverageReport();
461 }
462 writer.close();
Jack Wangff1df692009-08-26 17:19:13 -0700463
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 finish(Activity.RESULT_OK, mResults);
465 }
466 }
467 }
468
469 public TestSuite getTestSuite() {
470 return getAllTests();
471 }
472
473 /**
474 * Override this to define all of the tests to run in your package.
475 */
476 public TestSuite getAllTests() {
477 return null;
478 }
479
480 /**
481 * Override this to provide access to the class loader of your package.
482 */
483 public ClassLoader getLoader() {
484 return null;
485 }
Jack Wangff1df692009-08-26 17:19:13 -0700486
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 private void generateCoverageReport() {
488 // use reflection to call emma dump coverage method, to avoid
489 // always statically compiling against emma jar
Brett Chabot51e03642009-05-28 18:18:15 -0700490 String coverageFilePath = getCoverageFilePath();
491 java.io.File coverageFile = new java.io.File(coverageFilePath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 try {
493 Class emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
Jack Wangff1df692009-08-26 17:19:13 -0700494 Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 coverageFile.getClass(), boolean.class, boolean.class);
Jack Wangff1df692009-08-26 17:19:13 -0700496
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 dumpCoverageMethod.invoke(null, coverageFile, false, false);
Brett Chabot51e03642009-05-28 18:18:15 -0700498 // output path to generated coverage file so it can be parsed by a test harness if
499 // needed
500 mResults.putString(REPORT_KEY_COVERAGE_PATH, coverageFilePath);
501 // also output a more user friendly msg
502 mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
503 String.format("Generated code coverage data to %s", coverageFilePath));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 } catch (ClassNotFoundException e) {
505 reportEmmaError("Is emma jar on classpath?", e);
506 } catch (SecurityException e) {
507 reportEmmaError(e);
508 } catch (NoSuchMethodException e) {
509 reportEmmaError(e);
510 } catch (IllegalArgumentException e) {
511 reportEmmaError(e);
512 } catch (IllegalAccessException e) {
513 reportEmmaError(e);
514 } catch (InvocationTargetException e) {
515 reportEmmaError(e);
516 }
517 }
518
519 private String getCoverageFilePath() {
520 if (mCoverageFilePath == null) {
Brett Chabot51e03642009-05-28 18:18:15 -0700521 return getTargetContext().getFilesDir().getAbsolutePath() + File.separator +
Jack Wangff1df692009-08-26 17:19:13 -0700522 DEFAULT_COVERAGE_FILE_NAME;
523 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 return mCoverageFilePath;
525 }
526 }
527
528 private void reportEmmaError(Exception e) {
Jack Wangff1df692009-08-26 17:19:13 -0700529 reportEmmaError("", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 }
531
532 private void reportEmmaError(String hint, Exception e) {
533 String msg = "Failed to generate emma coverage. " + hint;
534 Log.e(LOG_TAG, msg, e);
535 mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "\nError: " + msg);
536 }
537
538 // TODO kill this, use status() and prettyprint model for better output
539 private class StringResultPrinter extends ResultPrinter {
540
541 public StringResultPrinter(PrintStream writer) {
542 super(writer);
543 }
544
545 synchronized void print(TestResult result, long runTime) {
546 printHeader(runTime);
547 printFooter(result);
548 }
549 }
Jack Wangff1df692009-08-26 17:19:13 -0700550
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 /**
Jack Wangff1df692009-08-26 17:19:13 -0700552 * This class sends status reports back to the IInstrumentationWatcher about
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800553 * which suite each test belongs.
554 */
Jack Wangff1df692009-08-26 17:19:13 -0700555 private class SuiteAssignmentPrinter implements TestListener {
556
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 private Bundle mTestResult;
558 private long mStartTime;
559 private long mEndTime;
560 private boolean mTimingValid;
Jack Wangff1df692009-08-26 17:19:13 -0700561
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 public SuiteAssignmentPrinter() {
563 }
Jack Wangff1df692009-08-26 17:19:13 -0700564
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 /**
566 * send a status for the start of a each test, so long tests can be seen as "running"
567 */
568 public void startTest(Test test) {
569 mTimingValid = true;
Jack Wangff1df692009-08-26 17:19:13 -0700570 mStartTime = System.currentTimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 }
Jack Wangff1df692009-08-26 17:19:13 -0700572
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 /**
574 * @see junit.framework.TestListener#addError(Test, Throwable)
575 */
576 public void addError(Test test, Throwable t) {
577 mTimingValid = false;
578 }
579
580 /**
581 * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
582 */
583 public void addFailure(Test test, AssertionFailedError t) {
584 mTimingValid = false;
585 }
586
587 /**
588 * @see junit.framework.TestListener#endTest(Test)
589 */
590 public void endTest(Test test) {
591 float runTime;
592 String assignmentSuite;
593 mEndTime = System.currentTimeMillis();
594 mTestResult = new Bundle();
595
596 if (!mTimingValid || mStartTime < 0) {
597 assignmentSuite = "NA";
598 runTime = -1;
599 } else {
600 runTime = mEndTime - mStartTime;
Jack Wangff1df692009-08-26 17:19:13 -0700601 if (runTime < SMALL_SUITE_MAX_RUNTIME
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 && !InstrumentationTestCase.class.isAssignableFrom(test.getClass())) {
603 assignmentSuite = SMALL_SUITE;
604 } else if (runTime < MEDIUM_SUITE_MAX_RUNTIME) {
605 assignmentSuite = MEDIUM_SUITE;
606 } else {
607 assignmentSuite = LARGE_SUITE;
608 }
609 }
610 // Clear mStartTime so that we can verify that it gets set next time.
611 mStartTime = -1;
612
Jack Wangff1df692009-08-26 17:19:13 -0700613 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
614 test.getClass().getName() + "#" + ((TestCase) test).getName()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615 + "\nin " + assignmentSuite + " suite\nrunTime: "
616 + String.valueOf(runTime) + "\n");
617 mTestResult.putFloat(REPORT_KEY_RUN_TIME, runTime);
618 mTestResult.putString(REPORT_KEY_SUITE_ASSIGNMENT, assignmentSuite);
619
620 sendStatus(0, mTestResult);
621 }
622 }
Jack Wangff1df692009-08-26 17:19:13 -0700623
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 /**
625 * This class sends status reports back to the IInstrumentationWatcher
626 */
Jack Wangff1df692009-08-26 17:19:13 -0700627 private class WatcherResultPrinter implements TestListener, PerformanceResultsWriter {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 private final Bundle mResultTemplate;
629 Bundle mTestResult;
630 int mTestNum = 0;
631 int mTestResultCode = 0;
632 String mTestClass = null;
Jack Wangff1df692009-08-26 17:19:13 -0700633 boolean mIsTimedTest = false;
634 long mCpuTime = 0;
635 long mExecTime = 0;
636
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 public WatcherResultPrinter(int numTests) {
638 mResultTemplate = new Bundle();
639 mResultTemplate.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
640 mResultTemplate.putInt(REPORT_KEY_NUM_TOTAL, numTests);
641 }
Jack Wangff1df692009-08-26 17:19:13 -0700642
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 /**
Jack Wangff1df692009-08-26 17:19:13 -0700644 * send a status for the start of a each test, so long tests can be seen
645 * as "running"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 */
647 public void startTest(Test test) {
648 String testClass = test.getClass().getName();
Jack Wangff1df692009-08-26 17:19:13 -0700649 String testName = ((TestCase)test).getName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650 mTestResult = new Bundle(mResultTemplate);
651 mTestResult.putString(REPORT_KEY_NAME_CLASS, testClass);
Jack Wangff1df692009-08-26 17:19:13 -0700652 mTestResult.putString(REPORT_KEY_NAME_TEST, testName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 mTestResult.putInt(REPORT_KEY_NUM_CURRENT, ++mTestNum);
654 // pretty printing
655 if (testClass != null && !testClass.equals(mTestClass)) {
Jack Wangff1df692009-08-26 17:19:13 -0700656 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657 String.format("\n%s:", testClass));
658 mTestClass = testClass;
659 } else {
660 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "");
661 }
662
663 // The delay_msec parameter is normally used to provide buffers of idle time
Jack Wangff1df692009-08-26 17:19:13 -0700664 // for power measurement purposes. To make sure there is a delay before and after
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 // every test in a suite, we delay *after* every test (see endTest below) and also
Jack Wangff1df692009-08-26 17:19:13 -0700666 // delay *before* the first test. So, delay test1 delay test2 delay.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667
668 try {
669 if (mTestNum == 1) Thread.sleep(mDelayMsec);
670 } catch (InterruptedException e) {
671 throw new IllegalStateException(e);
672 }
673
674 sendStatus(REPORT_VALUE_RESULT_START, mTestResult);
675 mTestResultCode = 0;
Jack Wangff1df692009-08-26 17:19:13 -0700676
677 mIsTimedTest = false;
678 try {
679 // Look for TimedTest annotation on both test class and test
680 // method
681 mIsTimedTest = test.getClass().isAnnotationPresent(TimedTest.class) ||
682 test.getClass().getMethod(testName).isAnnotationPresent(TimedTest.class);
683 } catch (SecurityException e) {
684 throw new IllegalStateException(e);
685 } catch (NoSuchMethodException e) {
686 throw new IllegalStateException(e);
687 }
688
689 if (mIsTimedTest) {
690 mExecTime = SystemClock.uptimeMillis();
691 mCpuTime = Process.getElapsedCpuTime();
692 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 }
Jack Wangff1df692009-08-26 17:19:13 -0700694
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 /**
696 * @see junit.framework.TestListener#addError(Test, Throwable)
697 */
698 public void addError(Test test, Throwable t) {
699 mTestResult.putString(REPORT_KEY_STACK, BaseTestRunner.getFilteredTrace(t));
700 mTestResultCode = REPORT_VALUE_RESULT_ERROR;
701 // pretty printing
Jack Wangff1df692009-08-26 17:19:13 -0700702 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
703 String.format("\nError in %s:\n%s",
704 ((TestCase)test).getName(), BaseTestRunner.getFilteredTrace(t)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 }
706
707 /**
708 * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
709 */
710 public void addFailure(Test test, AssertionFailedError t) {
711 mTestResult.putString(REPORT_KEY_STACK, BaseTestRunner.getFilteredTrace(t));
712 mTestResultCode = REPORT_VALUE_RESULT_FAILURE;
713 // pretty printing
Jack Wangff1df692009-08-26 17:19:13 -0700714 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
715 String.format("\nFailure in %s:\n%s",
716 ((TestCase)test).getName(), BaseTestRunner.getFilteredTrace(t)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717 }
718
719 /**
720 * @see junit.framework.TestListener#endTest(Test)
721 */
722 public void endTest(Test test) {
Jack Wangff1df692009-08-26 17:19:13 -0700723 if (mIsTimedTest) {
724 mCpuTime = Process.getElapsedCpuTime() - mCpuTime;
725 mExecTime = SystemClock.uptimeMillis() - mExecTime;
726 mTestResult.putLong(REPORT_KEY_PERF_CPU_TIME, mCpuTime);
727 mTestResult.putLong(REPORT_KEY_PERF_EXECUTION_TIME, mExecTime);
728 }
729
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 if (mTestResultCode == 0) {
731 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, ".");
732 }
733 sendStatus(mTestResultCode, mTestResult);
734
Jack Wangff1df692009-08-26 17:19:13 -0700735 try { // Sleep after every test, if specified
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 Thread.sleep(mDelayMsec);
737 } catch (InterruptedException e) {
738 throw new IllegalStateException(e);
739 }
740 }
741
Jack Wangff1df692009-08-26 17:19:13 -0700742 public void writeBeginSnapshot(String label) {
743 // Do nothing
744 }
745
746 public void writeEndSnapshot(Bundle results) {
Jack Wang075997f2009-10-27 22:01:09 -0700747 // Copy all snapshot data fields into mResults, which is outputted
748 // via Instrumentation.finish
749 mResults.putAll(results);
Jack Wangff1df692009-08-26 17:19:13 -0700750 }
751
752 public void writeStartTiming(String label) {
753 // Do nothing
754 }
755
756 public void writeStopTiming(Bundle results) {
757 // Copy results into mTestResult by flattening list of iterations,
758 // which is outputted via WatcherResultPrinter.endTest
759 int i = 0;
760 for (Parcelable p :
761 results.getParcelableArrayList(PerformanceCollector.METRIC_KEY_ITERATIONS)) {
762 Bundle iteration = (Bundle)p;
763 String index = "performance.iteration" + i + ".";
764 mTestResult.putString(index + PerformanceCollector.METRIC_KEY_LABEL,
765 iteration.getString(PerformanceCollector.METRIC_KEY_LABEL));
766 mTestResult.putLong(index + PerformanceCollector.METRIC_KEY_CPU_TIME,
767 iteration.getLong(PerformanceCollector.METRIC_KEY_CPU_TIME));
768 mTestResult.putLong(index + PerformanceCollector.METRIC_KEY_EXECUTION_TIME,
769 iteration.getLong(PerformanceCollector.METRIC_KEY_EXECUTION_TIME));
770 i++;
771 }
772 }
773
Jack Wang075997f2009-10-27 22:01:09 -0700774 public void writeMeasurement(String label, long value) {
775 mTestResult.putLong(REPORT_KEY_PREFIX + label, value);
776 }
777
778 public void writeMeasurement(String label, float value) {
779 mTestResult.putFloat(REPORT_KEY_PREFIX + label, value);
780 }
781
782 public void writeMeasurement(String label, String value) {
783 mTestResult.putString(REPORT_KEY_PREFIX + label, value);
784 }
785
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 // TODO report the end of the cycle
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 }
788}