blob: 6658fb0732130dd6e680904ca16b19dda3d3e5a8 [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;
20import android.app.Activity;
21import android.app.Instrumentation;
22import android.os.Bundle;
23import android.os.Debug;
24import android.os.Looper;
25import android.test.suitebuilder.TestMethod;
26import android.test.suitebuilder.TestPredicates;
27import android.test.suitebuilder.TestSuiteBuilder;
28import android.util.Log;
29
30import com.android.internal.util.Predicate;
31
32import junit.framework.AssertionFailedError;
33import junit.framework.Test;
34import junit.framework.TestCase;
35import junit.framework.TestListener;
36import junit.framework.TestResult;
37import junit.framework.TestSuite;
38import junit.runner.BaseTestRunner;
39import junit.textui.ResultPrinter;
40
41import java.io.ByteArrayOutputStream;
42import java.io.File;
43import java.io.PrintStream;
44import java.lang.reflect.InvocationTargetException;
45import java.lang.reflect.Method;
Urs Grobda13ef52009-04-17 11:30:14 -070046import java.util.ArrayList;
47import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49
50/**
51 * An {@link Instrumentation} that runs various types of {@link junit.framework.TestCase}s against
52 * an Android package (application). Typical usage:
53 * <ol>
54 * <li>Write {@link junit.framework.TestCase}s that perform unit, functional, or performance tests
55 * against the classes in your package. Typically these are subclassed from:
56 * <ul><li>{@link android.test.ActivityInstrumentationTestCase}</li>
57 * <li>{@link android.test.ActivityUnitTestCase}</li>
58 * <li>{@link android.test.AndroidTestCase}</li>
59 * <li>{@link android.test.ApplicationTestCase}</li>
60 * <li>{@link android.test.InstrumentationTestCase}</li>
61 * <li>{@link android.test.ProviderTestCase}</li>
62 * <li>{@link android.test.ServiceTestCase}</li>
63 * <li>{@link android.test.SingleLaunchActivityTestCase}</li></ul>
64 * <li>In an appropriate AndroidManifest.xml, define the this instrumentation with
65 * the appropriate android:targetPackage set.
66 * <li>Run the instrumentation using "adb shell am instrument -w",
67 * with no optional arguments, to run all tests (except performance tests).
68 * <li>Run the instrumentation using "adb shell am instrument -w",
69 * with the argument '-e func true' to run all functional tests. These are tests that derive from
70 * {@link android.test.InstrumentationTestCase}.
71 * <li>Run the instrumentation using "adb shell am instrument -w",
72 * with the argument '-e unit true' to run all unit tests. These are tests that <i>do not</i>derive
73 * from {@link android.test.InstrumentationTestCase} (and are not performance tests).
74 * <li>Run the instrumentation using "adb shell am instrument -w",
75 * with the argument '-e class' set to run an individual {@link junit.framework.TestCase}.
76 * </ol>
77 * <p/>
78 * <b>Running all tests:</b> adb shell am instrument -w
79 * com.android.foo/android.test.InstrumentationTestRunner
80 * <p/>
81 * <b>Running all small tests:</b> adb shell am instrument -w
82 * -e size small
83 * com.android.foo/android.test.InstrumentationTestRunner
84 * <p/>
85 * <b>Running all medium tests:</b> adb shell am instrument -w
86 * -e size medium
87 * com.android.foo/android.test.InstrumentationTestRunner
88 * <p/>
89 * <b>Running all large tests:</b> adb shell am instrument -w
90 * -e size large
91 * com.android.foo/android.test.InstrumentationTestRunner
92 * <p/>
93 * <b>Running a single testcase:</b> adb shell am instrument -w
94 * -e class com.android.foo.FooTest
95 * com.android.foo/android.test.InstrumentationTestRunner
96 * <p/>
97 * <b>Running a single test:</b> adb shell am instrument -w
98 * -e class com.android.foo.FooTest#testFoo
99 * com.android.foo/android.test.InstrumentationTestRunner
100 * <p/>
101 * <b>Running multiple tests:</b> adb shell am instrument -w
102 * -e class com.android.foo.FooTest,com.android.foo.TooTest
103 * com.android.foo/android.test.InstrumentationTestRunner
104 * <p/>
105 * <b>Including performance tests:</b> adb shell am instrument -w
106 * -e perf true
107 * com.android.foo/android.test.InstrumentationTestRunner
108 * <p/>
109 * <b>To debug your tests, set a break point in your code and pass:</b>
110 * -e debug true
111 * <p/>
112 * <b>To run in 'log only' mode</b>
113 * -e log true
114 * This option will load and iterate through all test classes and methods, but will bypass actual
115 * test execution. Useful for quickly obtaining info on the tests to be executed by an
116 * instrumentation command.
117 * <p/>
118 * <b>To generate EMMA code coverage:</b>
119 * -e coverage true
120 * Note: this requires an emma instrumented build. By default, the code coverage results file
Brett Chabot51e03642009-05-28 18:18:15 -0700121 * will be saved in a /data/<app>/coverage.ec file, unless overridden by coverageFile flag (see
122 * below)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 * <p/>
124 * <b> To specify EMMA code coverage results file path:</b>
125 * -e coverageFile /sdcard/myFile.ec
126 * <br/>
127 * in addition to the other arguments.
128 */
129
130/* (not JavaDoc)
131 * Although not necessary in most case, another way to use this class is to extend it and have the
132 * derived class return
133 * the desired test suite from the {@link #getTestSuite()} method. The test suite returned from this
134 * method will be used if no target class is defined in the meta-data or command line argument
135 * parameters. If a derived class is used it needs to be added as an instrumentation to the
136 * AndroidManifest.xml and the command to run it would look like:
137 * <p/>
138 * adb shell am instrument -w com.android.foo/<i>com.android.FooInstrumentationTestRunner</i>
139 * <p/>
140 * Where <i>com.android.FooInstrumentationTestRunner</i> is the derived class.
141 *
142 * This model is used by many existing app tests, but can probably be deprecated.
143 */
144public class InstrumentationTestRunner extends Instrumentation implements TestSuiteProvider {
145
146 /** @hide */
147 public static final String ARGUMENT_TEST_CLASS = "class";
148 /** @hide */
149 public static final String ARGUMENT_TEST_PACKAGE = "package";
150 /** @hide */
151 public static final String ARGUMENT_TEST_SIZE_PREDICATE = "size";
152 /** @hide */
153 public static final String ARGUMENT_INCLUDE_PERF = "perf";
154 /** @hide */
155 public static final String ARGUMENT_DELAY_MSEC = "delay_msec";
156
157 private static final String SMALL_SUITE = "small";
158 private static final String MEDIUM_SUITE = "medium";
159 private static final String LARGE_SUITE = "large";
160
161 private static final String ARGUMENT_LOG_ONLY = "log";
162
163
164 /**
165 * This constant defines the maximum allowed runtime (in ms) for a test included in the "small" suite.
166 * It is used to make an educated guess at what suite an unlabeled test belongs.
167 */
168 private static final float SMALL_SUITE_MAX_RUNTIME = 100;
169
170 /**
171 * This constant defines the maximum allowed runtime (in ms) for a test included in the "medium" suite.
172 * It is used to make an educated guess at what suite an unlabeled test belongs.
173 */
174 private static final float MEDIUM_SUITE_MAX_RUNTIME = 1000;
175
176 /**
177 * The following keys are used in the status bundle to provide structured reports to
178 * an IInstrumentationWatcher.
179 */
180
181 /**
182 * This value, if stored with key {@link android.app.Instrumentation#REPORT_KEY_IDENTIFIER},
183 * identifies InstrumentationTestRunner as the source of the report. This is sent with all
184 * status messages.
185 */
186 public static final String REPORT_VALUE_ID = "InstrumentationTestRunner";
187 /**
188 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
189 * identifies the total number of tests that are being run. This is sent with all status
190 * messages.
191 */
192 public static final String REPORT_KEY_NUM_TOTAL = "numtests";
193 /**
194 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
195 * identifies the sequence number of the current test. This is sent with any status message
196 * describing a specific test being started or completed.
197 */
198 public static final String REPORT_KEY_NUM_CURRENT = "current";
199 /**
200 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
201 * identifies the name of the current test class. This is sent with any status message
202 * describing a specific test being started or completed.
203 */
204 public static final String REPORT_KEY_NAME_CLASS = "class";
205 /**
206 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
207 * identifies the name of the current test. This is sent with any status message
208 * describing a specific test being started or completed.
209 */
210 public static final String REPORT_KEY_NAME_TEST = "test";
211 /**
212 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
213 * reports the run time in seconds of the current test.
214 */
215 private static final String REPORT_KEY_RUN_TIME = "runtime";
216 /**
217 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
218 * reports the guessed suite assignment for the current test.
219 */
220 private static final String REPORT_KEY_SUITE_ASSIGNMENT = "suiteassignment";
221 /**
Brett Chabot51e03642009-05-28 18:18:15 -0700222 * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
223 * identifies the path to the generated code coverage file.
224 */
225 private static final String REPORT_KEY_COVERAGE_PATH = "coverageFilePath";
226 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 * The test is starting.
228 */
229 public static final int REPORT_VALUE_RESULT_START = 1;
230 /**
231 * The test completed successfully.
232 */
233 public static final int REPORT_VALUE_RESULT_OK = 0;
234 /**
235 * The test completed with an error.
236 */
237 public static final int REPORT_VALUE_RESULT_ERROR = -1;
238 /**
239 * The test completed with a failure.
240 */
241 public static final int REPORT_VALUE_RESULT_FAILURE = -2;
242 /**
243 * If included in the status bundle sent to an IInstrumentationWatcher, this key
244 * identifies a stack trace describing an error or failure. This is sent with any status
245 * message describing a specific test being completed.
246 */
247 public static final String REPORT_KEY_STACK = "stack";
248
Brett Chabot51e03642009-05-28 18:18:15 -0700249 // Default file name for code coverage
250 private static final String DEFAULT_COVERAGE_FILE_NAME = "coverage.ec";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251
252 private static final String LOG_TAG = "InstrumentationTestRunner";
253
254 private final Bundle mResults = new Bundle();
255 private AndroidTestRunner mTestRunner;
256 private boolean mDebug;
257 private boolean mJustCount;
258 private boolean mSuiteAssignmentMode;
259 private int mTestCount;
260 private String mPackageOfTests;
261 private boolean mCoverage;
262 private String mCoverageFilePath;
263 private int mDelayMsec;
264
265 @Override
266 public void onCreate(Bundle arguments) {
267 super.onCreate(arguments);
268
269 // Apk paths used to search for test classes when using TestSuiteBuilders.
270 String[] apkPaths =
271 {getTargetContext().getPackageCodePath(), getContext().getPackageCodePath()};
272 ClassPathPackageInfoSource.setApkPaths(apkPaths);
273
274 Predicate<TestMethod> testSizePredicate = null;
275 boolean includePerformance = false;
276 String testClassesArg = null;
277 boolean logOnly = false;
278
279 if (arguments != null) {
280 // Test class name passed as an argument should override any meta-data declaration.
281 testClassesArg = arguments.getString(ARGUMENT_TEST_CLASS);
282 mDebug = getBooleanArgument(arguments, "debug");
283 mJustCount = getBooleanArgument(arguments, "count");
284 mSuiteAssignmentMode = getBooleanArgument(arguments, "suiteAssignment");
285 mPackageOfTests = arguments.getString(ARGUMENT_TEST_PACKAGE);
286 testSizePredicate = getSizePredicateFromArg(
287 arguments.getString(ARGUMENT_TEST_SIZE_PREDICATE));
288 includePerformance = getBooleanArgument(arguments, ARGUMENT_INCLUDE_PERF);
289 logOnly = getBooleanArgument(arguments, ARGUMENT_LOG_ONLY);
290 mCoverage = getBooleanArgument(arguments, "coverage");
291 mCoverageFilePath = arguments.getString("coverageFile");
292
293 try {
294 Object delay = arguments.get(ARGUMENT_DELAY_MSEC); // Accept either string or int
295 if (delay != null) mDelayMsec = Integer.parseInt(delay.toString());
296 } catch (NumberFormatException e) {
297 Log.e(LOG_TAG, "Invalid delay_msec parameter", e);
298 }
299 }
300
301 TestSuiteBuilder testSuiteBuilder = new TestSuiteBuilder(getClass().getName(),
302 getTargetContext().getClassLoader());
303
304 if (testSizePredicate != null) {
305 testSuiteBuilder.addRequirements(testSizePredicate);
306 }
307 if (!includePerformance) {
308 testSuiteBuilder.addRequirements(REJECT_PERFORMANCE);
309 }
310
311 if (testClassesArg == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 if (mPackageOfTests != null) {
313 testSuiteBuilder.includePackages(mPackageOfTests);
314 } else {
Brett Chabot61b10ac2009-03-31 17:04:34 -0700315 TestSuite testSuite = getTestSuite();
316 if (testSuite != null) {
317 testSuiteBuilder.addTestSuite(testSuite);
318 } else {
319 // no package or class bundle arguments were supplied, and no test suite
320 // provided so add all tests in application
321 testSuiteBuilder.includePackages("");
322 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 }
324 } else {
325 parseTestClasses(testClassesArg, testSuiteBuilder);
326 }
Urs Grobda13ef52009-04-17 11:30:14 -0700327
328 testSuiteBuilder.addRequirements(getBuilderRequirements());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329
330 mTestRunner = getAndroidTestRunner();
331 mTestRunner.setContext(getTargetContext());
332 mTestRunner.setInstrumentaiton(this);
333 mTestRunner.setSkipExecution(logOnly);
334 mTestRunner.setTest(testSuiteBuilder.build());
335 mTestCount = mTestRunner.getTestCases().size();
336 if (mSuiteAssignmentMode) {
337 mTestRunner.addTestListener(new SuiteAssignmentPrinter());
338 } else {
339 mTestRunner.addTestListener(new TestPrinter("TestRunner", false));
340 mTestRunner.addTestListener(new WatcherResultPrinter(mTestCount));
341 }
342 start();
343 }
344
Urs Grobda13ef52009-04-17 11:30:14 -0700345 List<Predicate<TestMethod>> getBuilderRequirements() {
346 return new ArrayList<Predicate<TestMethod>>();
347 }
348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 /**
350 * Parses and loads the specified set of test classes
351 * @param testClassArg - comma-separated list of test classes and methods
352 * @param testSuiteBuilder - builder to add tests to
353 */
354 private void parseTestClasses(String testClassArg, TestSuiteBuilder testSuiteBuilder) {
355 String[] testClasses = testClassArg.split(",");
356 for (String testClass : testClasses) {
357 parseTestClass(testClass, testSuiteBuilder);
358 }
359 }
360
361 /**
362 * Parse and load the given test class and, optionally, method
363 * @param testClassName - full package name of test class and optionally method to add. Expected
364 * format: com.android.TestClass#testMethod
365 * @param testSuiteBuilder - builder to add tests to
366 */
367 private void parseTestClass(String testClassName, TestSuiteBuilder testSuiteBuilder) {
368 int methodSeparatorIndex = testClassName.indexOf('#');
369 String testMethodName = null;
370
371 if (methodSeparatorIndex > 0) {
372 testMethodName = testClassName.substring(methodSeparatorIndex + 1);
373 testClassName = testClassName.substring(0, methodSeparatorIndex);
374 }
375 testSuiteBuilder.addTestClassByName(testClassName, testMethodName,
376 getTargetContext());
377 }
378
379 protected AndroidTestRunner getAndroidTestRunner() {
380 return new AndroidTestRunner();
381 }
382
383 private boolean getBooleanArgument(Bundle arguments, String tag) {
384 String tagString = arguments.getString(tag);
385 return tagString != null && Boolean.parseBoolean(tagString);
386 }
387
388 /*
389 * Returns the size predicate object, corresponding to the "size" argument value.
390 */
391 private Predicate<TestMethod> getSizePredicateFromArg(String sizeArg) {
392
393 if (SMALL_SUITE.equals(sizeArg)) {
394 return TestPredicates.SELECT_SMALL;
395 } else if (MEDIUM_SUITE.equals(sizeArg)) {
396 return TestPredicates.SELECT_MEDIUM;
397 } else if (LARGE_SUITE.equals(sizeArg)) {
398 return TestPredicates.SELECT_LARGE;
399 } else {
400 return null;
401 }
402 }
403
404 @Override
405 public void onStart() {
406 Looper.prepare();
407
408 if (mJustCount) {
409 mResults.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
410 mResults.putInt(REPORT_KEY_NUM_TOTAL, mTestCount);
411 finish(Activity.RESULT_OK, mResults);
412 } else {
413 if (mDebug) {
414 Debug.waitForDebugger();
415 }
416
417 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
418 PrintStream writer = new PrintStream(byteArrayOutputStream);
419 try {
420 StringResultPrinter resultPrinter = new StringResultPrinter(writer);
421
422 mTestRunner.addTestListener(resultPrinter);
423
424 long startTime = System.currentTimeMillis();
425 mTestRunner.runTest();
426 long runTime = System.currentTimeMillis() - startTime;
427
428 resultPrinter.print(mTestRunner.getTestResult(), runTime);
429 } finally {
430 mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
431 String.format("\nTest results for %s=%s",
432 mTestRunner.getTestClassName(),
433 byteArrayOutputStream.toString()));
434
435 if (mCoverage) {
436 generateCoverageReport();
437 }
438 writer.close();
439
440 finish(Activity.RESULT_OK, mResults);
441 }
442 }
443 }
444
445 public TestSuite getTestSuite() {
446 return getAllTests();
447 }
448
449 /**
450 * Override this to define all of the tests to run in your package.
451 */
452 public TestSuite getAllTests() {
453 return null;
454 }
455
456 /**
457 * Override this to provide access to the class loader of your package.
458 */
459 public ClassLoader getLoader() {
460 return null;
461 }
462
463 private void generateCoverageReport() {
464 // use reflection to call emma dump coverage method, to avoid
465 // always statically compiling against emma jar
Brett Chabot51e03642009-05-28 18:18:15 -0700466 String coverageFilePath = getCoverageFilePath();
467 java.io.File coverageFile = new java.io.File(coverageFilePath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 try {
469 Class emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
470 Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData",
471 coverageFile.getClass(), boolean.class, boolean.class);
472
473 dumpCoverageMethod.invoke(null, coverageFile, false, false);
Brett Chabot51e03642009-05-28 18:18:15 -0700474 // output path to generated coverage file so it can be parsed by a test harness if
475 // needed
476 mResults.putString(REPORT_KEY_COVERAGE_PATH, coverageFilePath);
477 // also output a more user friendly msg
478 mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
479 String.format("Generated code coverage data to %s", coverageFilePath));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 } catch (ClassNotFoundException e) {
481 reportEmmaError("Is emma jar on classpath?", e);
482 } catch (SecurityException e) {
483 reportEmmaError(e);
484 } catch (NoSuchMethodException e) {
485 reportEmmaError(e);
486 } catch (IllegalArgumentException e) {
487 reportEmmaError(e);
488 } catch (IllegalAccessException e) {
489 reportEmmaError(e);
490 } catch (InvocationTargetException e) {
491 reportEmmaError(e);
492 }
493 }
494
495 private String getCoverageFilePath() {
496 if (mCoverageFilePath == null) {
Brett Chabot51e03642009-05-28 18:18:15 -0700497 return getTargetContext().getFilesDir().getAbsolutePath() + File.separator +
498 DEFAULT_COVERAGE_FILE_NAME;
499 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 else {
501 return mCoverageFilePath;
502 }
503 }
504
505 private void reportEmmaError(Exception e) {
506 reportEmmaError("", e);
507 }
508
509 private void reportEmmaError(String hint, Exception e) {
510 String msg = "Failed to generate emma coverage. " + hint;
511 Log.e(LOG_TAG, msg, e);
512 mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "\nError: " + msg);
513 }
514
515 // TODO kill this, use status() and prettyprint model for better output
516 private class StringResultPrinter extends ResultPrinter {
517
518 public StringResultPrinter(PrintStream writer) {
519 super(writer);
520 }
521
522 synchronized void print(TestResult result, long runTime) {
523 printHeader(runTime);
524 printFooter(result);
525 }
526 }
527
528 /**
529 * This class sends status reports back to the IInstrumentationWatcher about
530 * which suite each test belongs.
531 */
532 private class SuiteAssignmentPrinter implements TestListener
533 {
534
535 private Bundle mTestResult;
536 private long mStartTime;
537 private long mEndTime;
538 private boolean mTimingValid;
539
540 public SuiteAssignmentPrinter() {
541 }
542
543 /**
544 * send a status for the start of a each test, so long tests can be seen as "running"
545 */
546 public void startTest(Test test) {
547 mTimingValid = true;
548 mStartTime = System.currentTimeMillis();
549 }
550
551 /**
552 * @see junit.framework.TestListener#addError(Test, Throwable)
553 */
554 public void addError(Test test, Throwable t) {
555 mTimingValid = false;
556 }
557
558 /**
559 * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
560 */
561 public void addFailure(Test test, AssertionFailedError t) {
562 mTimingValid = false;
563 }
564
565 /**
566 * @see junit.framework.TestListener#endTest(Test)
567 */
568 public void endTest(Test test) {
569 float runTime;
570 String assignmentSuite;
571 mEndTime = System.currentTimeMillis();
572 mTestResult = new Bundle();
573
574 if (!mTimingValid || mStartTime < 0) {
575 assignmentSuite = "NA";
576 runTime = -1;
577 } else {
578 runTime = mEndTime - mStartTime;
579 if (runTime < SMALL_SUITE_MAX_RUNTIME
580 && !InstrumentationTestCase.class.isAssignableFrom(test.getClass())) {
581 assignmentSuite = SMALL_SUITE;
582 } else if (runTime < MEDIUM_SUITE_MAX_RUNTIME) {
583 assignmentSuite = MEDIUM_SUITE;
584 } else {
585 assignmentSuite = LARGE_SUITE;
586 }
587 }
588 // Clear mStartTime so that we can verify that it gets set next time.
589 mStartTime = -1;
590
591 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
592 test.getClass().getName() + "#" + ((TestCase) test).getName()
593 + "\nin " + assignmentSuite + " suite\nrunTime: "
594 + String.valueOf(runTime) + "\n");
595 mTestResult.putFloat(REPORT_KEY_RUN_TIME, runTime);
596 mTestResult.putString(REPORT_KEY_SUITE_ASSIGNMENT, assignmentSuite);
597
598 sendStatus(0, mTestResult);
599 }
600 }
601
602 /**
603 * This class sends status reports back to the IInstrumentationWatcher
604 */
605 private class WatcherResultPrinter implements TestListener
606 {
607 private final Bundle mResultTemplate;
608 Bundle mTestResult;
609 int mTestNum = 0;
610 int mTestResultCode = 0;
611 String mTestClass = null;
612
613 public WatcherResultPrinter(int numTests) {
614 mResultTemplate = new Bundle();
615 mResultTemplate.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
616 mResultTemplate.putInt(REPORT_KEY_NUM_TOTAL, numTests);
617 }
618
619 /**
620 * send a status for the start of a each test, so long tests can be seen as "running"
621 */
622 public void startTest(Test test) {
623 String testClass = test.getClass().getName();
624 mTestResult = new Bundle(mResultTemplate);
625 mTestResult.putString(REPORT_KEY_NAME_CLASS, testClass);
626 mTestResult.putString(REPORT_KEY_NAME_TEST, ((TestCase) test).getName());
627 mTestResult.putInt(REPORT_KEY_NUM_CURRENT, ++mTestNum);
628 // pretty printing
629 if (testClass != null && !testClass.equals(mTestClass)) {
630 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
631 String.format("\n%s:", testClass));
632 mTestClass = testClass;
633 } else {
634 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "");
635 }
636
637 // The delay_msec parameter is normally used to provide buffers of idle time
638 // for power measurement purposes. To make sure there is a delay before and after
639 // every test in a suite, we delay *after* every test (see endTest below) and also
640 // delay *before* the first test. So, delay test1 delay test2 delay.
641
642 try {
643 if (mTestNum == 1) Thread.sleep(mDelayMsec);
644 } catch (InterruptedException e) {
645 throw new IllegalStateException(e);
646 }
647
648 sendStatus(REPORT_VALUE_RESULT_START, mTestResult);
649 mTestResultCode = 0;
650 }
651
652 /**
653 * @see junit.framework.TestListener#addError(Test, Throwable)
654 */
655 public void addError(Test test, Throwable t) {
656 mTestResult.putString(REPORT_KEY_STACK, BaseTestRunner.getFilteredTrace(t));
657 mTestResultCode = REPORT_VALUE_RESULT_ERROR;
658 // pretty printing
659 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
660 String.format("\nError in %s:\n%s",
661 ((TestCase) test).getName(), BaseTestRunner.getFilteredTrace(t)));
662 }
663
664 /**
665 * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
666 */
667 public void addFailure(Test test, AssertionFailedError t) {
668 mTestResult.putString(REPORT_KEY_STACK, BaseTestRunner.getFilteredTrace(t));
669 mTestResultCode = REPORT_VALUE_RESULT_FAILURE;
670 // pretty printing
671 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
672 String.format("\nFailure in %s:\n%s",
673 ((TestCase) test).getName(), BaseTestRunner.getFilteredTrace(t)));
674 }
675
676 /**
677 * @see junit.framework.TestListener#endTest(Test)
678 */
679 public void endTest(Test test) {
680 if (mTestResultCode == 0) {
681 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, ".");
682 }
683 sendStatus(mTestResultCode, mTestResult);
684
685 try { // Sleep after every test, if specified
686 Thread.sleep(mDelayMsec);
687 } catch (InterruptedException e) {
688 throw new IllegalStateException(e);
689 }
690 }
691
692 // TODO report the end of the cycle
693 // TODO report runtime for each test
694 }
695}