1/*
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 android.test.suitebuilder.annotation.MediumTest;
20import android.test.suitebuilder.annotation.SmallTest;
21import com.android.internal.util.Predicate;
22
23import android.app.Activity;
24import android.app.Instrumentation;
25import android.os.Bundle;
26import android.os.Debug;
27import android.os.Looper;
28import android.test.suitebuilder.TestMethod;
29import android.test.suitebuilder.TestPredicates;
30import android.test.suitebuilder.TestSuiteBuilder;
31import android.test.suitebuilder.annotation.LargeTest;
32import android.util.Log;
33
34import java.io.ByteArrayOutputStream;
35import java.io.File;
36import java.io.PrintStream;
37import java.lang.annotation.Annotation;
38import java.lang.reflect.InvocationTargetException;
39import java.lang.reflect.Method;
40import java.util.ArrayList;
41import java.util.List;
42
43import junit.framework.AssertionFailedError;
44import junit.framework.Test;
45import junit.framework.TestCase;
46import junit.framework.TestListener;
47import junit.framework.TestResult;
48import junit.framework.TestSuite;
49import junit.runner.BaseTestRunner;
50import junit.textui.ResultPrinter;
51
52import static android.test.suitebuilder.TestPredicates.hasAnnotation;
53
54/**
55 * An {@link Instrumentation} that runs various types of {@link junit.framework.TestCase}s against
56 * an Android package (application).
57 *
58 * <div class="special reference">
59 * <h3>Developer Guides</h3>
60 * <p>For more information about application testing, read the
61 * <a href="{@docRoot}guide/topics/testing/index.html">Testing</a> developer guide.</p>
62 * </div>
63 *
64 * <h3>Typical Usage</h3>
65 * <ol>
66 * <li>Write {@link junit.framework.TestCase}s that perform unit, functional, or performance tests
67 * against the classes in your package.  Typically these are subclassed from:
68 *   <ul><li>{@link android.test.ActivityInstrumentationTestCase2}</li>
69 *   <li>{@link android.test.ActivityUnitTestCase}</li>
70 *   <li>{@link android.test.AndroidTestCase}</li>
71 *   <li>{@link android.test.ApplicationTestCase}</li>
72 *   <li>{@link android.test.InstrumentationTestCase}</li>
73 *   <li>{@link android.test.ProviderTestCase}</li>
74 *   <li>{@link android.test.ServiceTestCase}</li>
75 *   <li>{@link android.test.SingleLaunchActivityTestCase}</li></ul>
76 * <li>Set the <code>android:targetPackage</code> attribute of the <code>&lt;instrumentation&gt;</code>
77 * element in the test package's manifest. You should set the attribute value
78 * to the package name of the target application under test.
79 * <li>Run the instrumentation using "adb shell am instrument -w",
80 * with no optional arguments, to run all tests (except performance tests).
81 * <li>Run the instrumentation using "adb shell am instrument -w",
82 * with the argument '-e func true' to run all functional tests. These are tests that derive from
83 * {@link android.test.InstrumentationTestCase}.
84 * <li>Run the instrumentation using "adb shell am instrument -w",
85 * with the argument '-e unit true' to run all unit tests. These are tests that <i>do not</i>derive
86 * from {@link android.test.InstrumentationTestCase} (and are not performance tests).
87 * <li>Run the instrumentation using "adb shell am instrument -w",
88 * with the argument '-e class' set to run an individual {@link junit.framework.TestCase}.
89 * </ol>
90 * <p/>
91 * <b>Running all tests:</b> adb shell am instrument -w
92 * com.android.foo/android.test.InstrumentationTestRunner
93 * <p/>
94 * <b>Running all small tests:</b> adb shell am instrument -w
95 * -e size small
96 * com.android.foo/android.test.InstrumentationTestRunner
97 * <p/>
98 * <b>Running all medium tests:</b> adb shell am instrument -w
99 * -e size medium
100 * com.android.foo/android.test.InstrumentationTestRunner
101 * <p/>
102 * <b>Running all large tests:</b> adb shell am instrument -w
103 * -e size large
104 * com.android.foo/android.test.InstrumentationTestRunner
105 * <p/>
106 * <b>Filter test run to tests with given annotation:</b> adb shell am instrument -w
107 * -e annotation com.android.foo.MyAnnotation
108 * com.android.foo/android.test.InstrumentationTestRunner
109 * <p/>
110 * If used with other options, the resulting test run will contain the union of the two options.
111 * e.g. "-e size large -e annotation com.android.foo.MyAnnotation" will run only tests with both
112 * the {@link LargeTest} and "com.android.foo.MyAnnotation" annotations.
113 * <p/>
114 * <b>Filter test run to tests <i>without</i> given annotation:</b> adb shell am instrument -w
115 * -e notAnnotation com.android.foo.MyAnnotation
116 * com.android.foo/android.test.InstrumentationTestRunner
117 * <p/>
118 * <b>Running a single testcase:</b> adb shell am instrument -w
119 * -e class com.android.foo.FooTest
120 * com.android.foo/android.test.InstrumentationTestRunner
121 * <p/>
122 * <b>Running a single test:</b> adb shell am instrument -w
123 * -e class com.android.foo.FooTest#testFoo
124 * com.android.foo/android.test.InstrumentationTestRunner
125 * <p/>
126 * <b>Running multiple tests:</b> adb shell am instrument -w
127 * -e class com.android.foo.FooTest,com.android.foo.TooTest
128 * com.android.foo/android.test.InstrumentationTestRunner
129 * <p/>
130 * <b>Running all tests in a java package:</b> adb shell am instrument -w
131 * -e package com.android.foo.subpkg
132 *  com.android.foo/android.test.InstrumentationTestRunner
133 * <p/>
134 * <b>Including performance tests:</b> adb shell am instrument -w
135 * -e perf true
136 * com.android.foo/android.test.InstrumentationTestRunner
137 * <p/>
138 * <b>To debug your tests, set a break point in your code and pass:</b>
139 * -e debug true
140 * <p/>
141 * <b>To run in 'log only' mode</b>
142 * -e log true
143 * This option will load and iterate through all test classes and methods, but will bypass actual
144 * test execution. Useful for quickly obtaining info on the tests to be executed by an
145 * instrumentation command.
146 * <p/>
147 * <b>To generate EMMA code coverage:</b>
148 * -e coverage true
149 * Note: this requires an emma instrumented build. By default, the code coverage results file
150 * will be saved in a /data/<app>/coverage.ec file, unless overridden by coverageFile flag (see
151 * below)
152 * <p/>
153 * <b> To specify EMMA code coverage results file path:</b>
154 * -e coverageFile /sdcard/myFile.ec
155 * <br/>
156 * in addition to the other arguments.
157 * @deprecated Use
158 * <a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
159 * AndroidJUnitRunner</a> instead. New tests should be written using the
160 * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
161 */
162
163/* (not JavaDoc)
164 * Although not necessary in most case, another way to use this class is to extend it and have the
165 * derived class return the desired test suite from the {@link #getTestSuite()} method. The test
166 * suite returned from this method will be used if no target class is defined in the meta-data or
167 * command line argument parameters. If a derived class is used it needs to be added as an
168 * instrumentation to the AndroidManifest.xml and the command to run it would look like:
169 * <p/>
170 * adb shell am instrument -w com.android.foo/<i>com.android.FooInstrumentationTestRunner</i>
171 * <p/>
172 * Where <i>com.android.FooInstrumentationTestRunner</i> is the derived class.
173 *
174 * This model is used by many existing app tests, but can probably be deprecated.
175 */
176@Deprecated
177public class InstrumentationTestRunner extends Instrumentation implements TestSuiteProvider {
178
179    /** @hide */
180    static final String ARGUMENT_TEST_CLASS = "class";
181    /** @hide */
182    private static final String ARGUMENT_TEST_PACKAGE = "package";
183    /** @hide */
184    private static final String ARGUMENT_TEST_SIZE_PREDICATE = "size";
185    /** @hide */
186    static final String ARGUMENT_DELAY_MSEC = "delay_msec";
187
188    private static final String SMALL_SUITE = "small";
189    private static final String MEDIUM_SUITE = "medium";
190    private static final String LARGE_SUITE = "large";
191
192    private static final String ARGUMENT_LOG_ONLY = "log";
193    /** @hide */
194    static final String ARGUMENT_ANNOTATION = "annotation";
195    /** @hide */
196    static final String ARGUMENT_NOT_ANNOTATION = "notAnnotation";
197
198    private static final Predicate<TestMethod> SELECT_SMALL = hasAnnotation(SmallTest.class);
199
200    private static final Predicate<TestMethod> SELECT_MEDIUM = hasAnnotation(MediumTest.class);
201
202    private static final Predicate<TestMethod> SELECT_LARGE = hasAnnotation(LargeTest.class);
203
204    /**
205     * This constant defines the maximum allowed runtime (in ms) for a test included in the "small"
206     * suite. It is used to make an educated guess at what suite an unlabeled test belongs.
207     */
208    private static final float SMALL_SUITE_MAX_RUNTIME = 100;
209
210    /**
211     * This constant defines the maximum allowed runtime (in ms) for a test included in the
212     * "medium" suite. It is used to make an educated guess at what suite an unlabeled test belongs.
213     */
214    private static final float MEDIUM_SUITE_MAX_RUNTIME = 1000;
215
216    /*
217     * The following keys are used in the status bundle to provide structured reports to
218     * an IInstrumentationWatcher.
219     */
220
221    /**
222     * This value, if stored with key {@link android.app.Instrumentation#REPORT_KEY_IDENTIFIER},
223     * identifies InstrumentationTestRunner as the source of the report.  This is sent with all
224     * status messages.
225     */
226    public static final String REPORT_VALUE_ID = "InstrumentationTestRunner";
227    /**
228     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
229     * identifies the total number of tests that are being run.  This is sent with all status
230     * messages.
231     */
232    public static final String REPORT_KEY_NUM_TOTAL = "numtests";
233    /**
234     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
235     * identifies the sequence number of the current test.  This is sent with any status message
236     * describing a specific test being started or completed.
237     */
238    public static final String REPORT_KEY_NUM_CURRENT = "current";
239    /**
240     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
241     * identifies the name of the current test class.  This is sent with any status message
242     * describing a specific test being started or completed.
243     */
244    public static final String REPORT_KEY_NAME_CLASS = "class";
245    /**
246     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
247     * identifies the name of the current test.  This is sent with any status message
248     * describing a specific test being started or completed.
249     */
250    public static final String REPORT_KEY_NAME_TEST = "test";
251    /**
252     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
253     * reports the run time in seconds of the current test.
254     */
255    private static final String REPORT_KEY_RUN_TIME = "runtime";
256    /**
257     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
258     * reports the number of total iterations of the current test.
259     */
260    private static final String REPORT_KEY_NUM_ITERATIONS = "numiterations";
261    /**
262     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
263     * reports the guessed suite assignment for the current test.
264     */
265    private static final String REPORT_KEY_SUITE_ASSIGNMENT = "suiteassignment";
266    /**
267     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
268     * identifies the path to the generated code coverage file.
269     */
270    private static final String REPORT_KEY_COVERAGE_PATH = "coverageFilePath";
271
272    /**
273     * The test is starting.
274     */
275    public static final int REPORT_VALUE_RESULT_START = 1;
276    /**
277     * The test completed successfully.
278     */
279    public static final int REPORT_VALUE_RESULT_OK = 0;
280    /**
281     * The test completed with an error.
282     */
283    public static final int REPORT_VALUE_RESULT_ERROR = -1;
284    /**
285     * The test completed with a failure.
286     */
287    public static final int REPORT_VALUE_RESULT_FAILURE = -2;
288    /**
289     * If included in the status bundle sent to an IInstrumentationWatcher, this key
290     * identifies a stack trace describing an error or failure.  This is sent with any status
291     * message describing a specific test being completed.
292     */
293    public static final String REPORT_KEY_STACK = "stack";
294
295    // Default file name for code coverage
296    private static final String DEFAULT_COVERAGE_FILE_NAME = "coverage.ec";
297
298    private static final String LOG_TAG = "InstrumentationTestRunner";
299
300    private final Bundle mResults = new Bundle();
301    private Bundle mArguments;
302    private AndroidTestRunner mTestRunner;
303    private boolean mDebug;
304    private boolean mJustCount;
305    private boolean mSuiteAssignmentMode;
306    private int mTestCount;
307    private String mPackageOfTests;
308    private boolean mCoverage;
309    private String mCoverageFilePath;
310    private int mDelayMsec;
311
312    @Override
313    public void onCreate(Bundle arguments) {
314        super.onCreate(arguments);
315        mArguments = arguments;
316
317        // Apk paths used to search for test classes when using TestSuiteBuilders.
318        String[] apkPaths =
319                {getTargetContext().getPackageCodePath(), getContext().getPackageCodePath()};
320        ClassPathPackageInfoSource.setApkPaths(apkPaths);
321
322        Predicate<TestMethod> testSizePredicate = null;
323        Predicate<TestMethod> testAnnotationPredicate = null;
324        Predicate<TestMethod> testNotAnnotationPredicate = null;
325        String testClassesArg = null;
326        boolean logOnly = false;
327
328        if (arguments != null) {
329            // Test class name passed as an argument should override any meta-data declaration.
330            testClassesArg = arguments.getString(ARGUMENT_TEST_CLASS);
331            mDebug = getBooleanArgument(arguments, "debug");
332            mJustCount = getBooleanArgument(arguments, "count");
333            mSuiteAssignmentMode = getBooleanArgument(arguments, "suiteAssignment");
334            mPackageOfTests = arguments.getString(ARGUMENT_TEST_PACKAGE);
335            testSizePredicate = getSizePredicateFromArg(
336                    arguments.getString(ARGUMENT_TEST_SIZE_PREDICATE));
337            testAnnotationPredicate = getAnnotationPredicate(
338                    arguments.getString(ARGUMENT_ANNOTATION));
339            testNotAnnotationPredicate = getNotAnnotationPredicate(
340                    arguments.getString(ARGUMENT_NOT_ANNOTATION));
341
342            logOnly = getBooleanArgument(arguments, ARGUMENT_LOG_ONLY);
343            mCoverage = getBooleanArgument(arguments, "coverage");
344            mCoverageFilePath = arguments.getString("coverageFile");
345
346            try {
347                Object delay = arguments.get(ARGUMENT_DELAY_MSEC);  // Accept either string or int
348                if (delay != null) mDelayMsec = Integer.parseInt(delay.toString());
349            } catch (NumberFormatException e) {
350                Log.e(LOG_TAG, "Invalid delay_msec parameter", e);
351            }
352        }
353
354        TestSuiteBuilder testSuiteBuilder = new TestSuiteBuilder(getClass().getName(),
355                getTargetContext().getClassLoader());
356
357        if (testSizePredicate != null) {
358            testSuiteBuilder.addRequirements(testSizePredicate);
359        }
360        if (testAnnotationPredicate != null) {
361            testSuiteBuilder.addRequirements(testAnnotationPredicate);
362        }
363        if (testNotAnnotationPredicate != null) {
364            testSuiteBuilder.addRequirements(testNotAnnotationPredicate);
365        }
366
367        if (testClassesArg == null) {
368            if (mPackageOfTests != null) {
369                testSuiteBuilder.includePackages(mPackageOfTests);
370            } else {
371                TestSuite testSuite = getTestSuite();
372                if (testSuite != null) {
373                    testSuiteBuilder.addTestSuite(testSuite);
374                } else {
375                    // no package or class bundle arguments were supplied, and no test suite
376                    // provided so add all tests in application
377                    testSuiteBuilder.includePackages("");
378                }
379            }
380        } else {
381            parseTestClasses(testClassesArg, testSuiteBuilder);
382        }
383
384        testSuiteBuilder.addRequirements(getBuilderRequirements());
385
386        mTestRunner = getAndroidTestRunner();
387        mTestRunner.setContext(getTargetContext());
388        mTestRunner.setInstrumentation(this);
389        mTestRunner.setSkipExecution(logOnly);
390        mTestRunner.setTest(testSuiteBuilder.build());
391        mTestCount = mTestRunner.getTestCases().size();
392        if (mSuiteAssignmentMode) {
393            mTestRunner.addTestListener(new SuiteAssignmentPrinter());
394        } else {
395            WatcherResultPrinter resultPrinter = new WatcherResultPrinter(mTestCount);
396            mTestRunner.addTestListener(new TestPrinter("TestRunner", false));
397            mTestRunner.addTestListener(resultPrinter);
398        }
399        start();
400    }
401
402    /**
403     * Get the arguments passed to this instrumentation.
404     *
405     * @return the Bundle object
406     */
407    public Bundle getArguments() {
408        return mArguments;
409    }
410
411    /**
412     * Add a {@link TestListener}
413     * @hide
414     */
415    protected void addTestListener(TestListener listener){
416        if(mTestRunner!=null && listener!=null){
417            mTestRunner.addTestListener(listener);
418        }
419    }
420
421    List<Predicate<TestMethod>> getBuilderRequirements() {
422        return new ArrayList<Predicate<TestMethod>>();
423    }
424
425    /**
426     * Parses and loads the specified set of test classes
427     *
428     * @param testClassArg - comma-separated list of test classes and methods
429     * @param testSuiteBuilder - builder to add tests to
430     */
431    private void parseTestClasses(String testClassArg, TestSuiteBuilder testSuiteBuilder) {
432        String[] testClasses = testClassArg.split(",");
433        for (String testClass : testClasses) {
434            parseTestClass(testClass, testSuiteBuilder);
435        }
436    }
437
438    /**
439     * Parse and load the given test class and, optionally, method
440     *
441     * @param testClassName - full package name of test class and optionally method to add.
442     *        Expected format: com.android.TestClass#testMethod
443     * @param testSuiteBuilder - builder to add tests to
444     */
445    private void parseTestClass(String testClassName, TestSuiteBuilder testSuiteBuilder) {
446        int methodSeparatorIndex = testClassName.indexOf('#');
447        String testMethodName = null;
448
449        if (methodSeparatorIndex > 0) {
450            testMethodName = testClassName.substring(methodSeparatorIndex + 1);
451            testClassName = testClassName.substring(0, methodSeparatorIndex);
452        }
453        testSuiteBuilder.addTestClassByName(testClassName, testMethodName, getTargetContext());
454    }
455
456    protected AndroidTestRunner getAndroidTestRunner() {
457        return new AndroidTestRunner();
458    }
459
460    private boolean getBooleanArgument(Bundle arguments, String tag) {
461        String tagString = arguments.getString(tag);
462        return tagString != null && Boolean.parseBoolean(tagString);
463    }
464
465    /*
466     * Returns the size predicate object, corresponding to the "size" argument value.
467     */
468    private Predicate<TestMethod> getSizePredicateFromArg(String sizeArg) {
469
470        if (SMALL_SUITE.equals(sizeArg)) {
471            return SELECT_SMALL;
472        } else if (MEDIUM_SUITE.equals(sizeArg)) {
473            return SELECT_MEDIUM;
474        } else if (LARGE_SUITE.equals(sizeArg)) {
475            return SELECT_LARGE;
476        } else {
477            return null;
478        }
479    }
480
481   /**
482    * Returns the test predicate object, corresponding to the annotation class value provided via
483    * the {@link #ARGUMENT_ANNOTATION} argument.
484    *
485    * @return the predicate or <code>null</code>
486    */
487    private Predicate<TestMethod> getAnnotationPredicate(String annotationClassName) {
488        Class<? extends Annotation> annotationClass = getAnnotationClass(annotationClassName);
489        if (annotationClass != null) {
490            return hasAnnotation(annotationClass);
491        }
492        return null;
493    }
494
495    /**
496     * Returns the negative test predicate object, corresponding to the annotation class value
497     * provided via the {@link #ARGUMENT_NOT_ANNOTATION} argument.
498     *
499     * @return the predicate or <code>null</code>
500     */
501     private Predicate<TestMethod> getNotAnnotationPredicate(String annotationClassName) {
502         Class<? extends Annotation> annotationClass = getAnnotationClass(annotationClassName);
503         if (annotationClass != null) {
504             return TestPredicates.not(hasAnnotation(annotationClass));
505         }
506         return null;
507     }
508
509    /**
510     * Helper method to return the annotation class with specified name
511     *
512     * @param annotationClassName the fully qualified name of the class
513     * @return the annotation class or <code>null</code>
514     */
515    private Class<? extends Annotation> getAnnotationClass(String annotationClassName) {
516        if (annotationClassName == null) {
517            return null;
518        }
519        try {
520           Class<?> annotationClass = Class.forName(annotationClassName);
521           if (annotationClass.isAnnotation()) {
522               return (Class<? extends Annotation>)annotationClass;
523           } else {
524               Log.e(LOG_TAG, String.format("Provided annotation value %s is not an Annotation",
525                       annotationClassName));
526           }
527        } catch (ClassNotFoundException e) {
528            Log.e(LOG_TAG, String.format("Could not find class for specified annotation %s",
529                    annotationClassName));
530        }
531        return null;
532    }
533
534    /**
535     * Initialize the current thread as a looper.
536     * <p/>
537     * Exposed for unit testing.
538     */
539    void prepareLooper() {
540        Looper.prepare();
541    }
542
543    @Override
544    public void onStart() {
545        prepareLooper();
546
547        if (mJustCount) {
548            mResults.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
549            mResults.putInt(REPORT_KEY_NUM_TOTAL, mTestCount);
550            finish(Activity.RESULT_OK, mResults);
551        } else {
552            if (mDebug) {
553                Debug.waitForDebugger();
554            }
555
556            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
557            PrintStream writer = new PrintStream(byteArrayOutputStream);
558            try {
559                StringResultPrinter resultPrinter = new StringResultPrinter(writer);
560
561                mTestRunner.addTestListener(resultPrinter);
562
563                long startTime = System.currentTimeMillis();
564                mTestRunner.runTest();
565                long runTime = System.currentTimeMillis() - startTime;
566
567                resultPrinter.printResult(mTestRunner.getTestResult(), runTime);
568            } catch (Throwable t) {
569                // catch all exceptions so a more verbose error message can be outputted
570                writer.println(String.format("Test run aborted due to unexpected exception: %s",
571                                t.getMessage()));
572                t.printStackTrace(writer);
573            } finally {
574                mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
575                        String.format("\nTest results for %s=%s",
576                        mTestRunner.getTestClassName(),
577                        byteArrayOutputStream.toString()));
578
579                if (mCoverage) {
580                    generateCoverageReport();
581                }
582                writer.close();
583
584                finish(Activity.RESULT_OK, mResults);
585            }
586        }
587    }
588
589    public TestSuite getTestSuite() {
590        return getAllTests();
591    }
592
593    /**
594     * Override this to define all of the tests to run in your package.
595     */
596    public TestSuite getAllTests() {
597        return null;
598    }
599
600    /**
601     * Override this to provide access to the class loader of your package.
602     */
603    public ClassLoader getLoader() {
604        return null;
605    }
606
607    private void generateCoverageReport() {
608        // use reflection to call emma dump coverage method, to avoid
609        // always statically compiling against emma jar
610        String coverageFilePath = getCoverageFilePath();
611        java.io.File coverageFile = new java.io.File(coverageFilePath);
612        try {
613            Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
614            Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData",
615                    coverageFile.getClass(), boolean.class, boolean.class);
616
617            dumpCoverageMethod.invoke(null, coverageFile, false, false);
618            // output path to generated coverage file so it can be parsed by a test harness if
619            // needed
620            mResults.putString(REPORT_KEY_COVERAGE_PATH, coverageFilePath);
621            // also output a more user friendly msg
622            final String currentStream = mResults.getString(
623                    Instrumentation.REPORT_KEY_STREAMRESULT);
624            mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
625                String.format("%s\nGenerated code coverage data to %s", currentStream,
626                coverageFilePath));
627        } catch (ClassNotFoundException e) {
628            reportEmmaError("Is emma jar on classpath?", e);
629        } catch (SecurityException e) {
630            reportEmmaError(e);
631        } catch (NoSuchMethodException e) {
632            reportEmmaError(e);
633        } catch (IllegalArgumentException e) {
634            reportEmmaError(e);
635        } catch (IllegalAccessException e) {
636            reportEmmaError(e);
637        } catch (InvocationTargetException e) {
638            reportEmmaError(e);
639        }
640    }
641
642    private String getCoverageFilePath() {
643        if (mCoverageFilePath == null) {
644            return getTargetContext().getFilesDir().getAbsolutePath() + File.separator +
645                   DEFAULT_COVERAGE_FILE_NAME;
646        } else {
647            return mCoverageFilePath;
648        }
649    }
650
651    private void reportEmmaError(Exception e) {
652        reportEmmaError("", e);
653    }
654
655    private void reportEmmaError(String hint, Exception e) {
656        String msg = "Failed to generate emma coverage. " + hint;
657        Log.e(LOG_TAG, msg, e);
658        mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "\nError: " + msg);
659    }
660
661    // TODO kill this, use status() and prettyprint model for better output
662    private class StringResultPrinter extends ResultPrinter {
663
664        public StringResultPrinter(PrintStream writer) {
665            super(writer);
666        }
667
668        public synchronized void printResult(TestResult result, long runTime) {
669            printHeader(runTime);
670            printFooter(result);
671        }
672    }
673
674    /**
675     * This class sends status reports back to the IInstrumentationWatcher about
676     * which suite each test belongs.
677     */
678    private class SuiteAssignmentPrinter implements TestListener {
679
680        private Bundle mTestResult;
681        private long mStartTime;
682        private long mEndTime;
683        private boolean mTimingValid;
684
685        public SuiteAssignmentPrinter() {
686        }
687
688        /**
689         * send a status for the start of a each test, so long tests can be seen as "running"
690         */
691        public void startTest(Test test) {
692            mTimingValid = true;
693            mStartTime = System.currentTimeMillis();
694        }
695
696        /**
697         * @see junit.framework.TestListener#addError(Test, Throwable)
698         */
699        public void addError(Test test, Throwable t) {
700            mTimingValid = false;
701        }
702
703        /**
704         * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
705         */
706        public void addFailure(Test test, AssertionFailedError t) {
707            mTimingValid = false;
708        }
709
710        /**
711         * @see junit.framework.TestListener#endTest(Test)
712         */
713        public void endTest(Test test) {
714            float runTime;
715            String assignmentSuite;
716            mEndTime = System.currentTimeMillis();
717            mTestResult = new Bundle();
718
719            if (!mTimingValid || mStartTime < 0) {
720                assignmentSuite = "NA";
721                runTime = -1;
722            } else {
723                runTime = mEndTime - mStartTime;
724                if (runTime < SMALL_SUITE_MAX_RUNTIME
725                        && !InstrumentationTestCase.class.isAssignableFrom(test.getClass())) {
726                    assignmentSuite = SMALL_SUITE;
727                } else if (runTime < MEDIUM_SUITE_MAX_RUNTIME) {
728                    assignmentSuite = MEDIUM_SUITE;
729                } else {
730                    assignmentSuite = LARGE_SUITE;
731                }
732            }
733            // Clear mStartTime so that we can verify that it gets set next time.
734            mStartTime = -1;
735
736            mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
737                    test.getClass().getName() + "#" + ((TestCase) test).getName()
738                    + "\nin " + assignmentSuite + " suite\nrunTime: "
739                    + String.valueOf(runTime) + "\n");
740            mTestResult.putFloat(REPORT_KEY_RUN_TIME, runTime);
741            mTestResult.putString(REPORT_KEY_SUITE_ASSIGNMENT, assignmentSuite);
742
743            sendStatus(0, mTestResult);
744        }
745    }
746
747    /**
748     * This class sends status reports back to the IInstrumentationWatcher
749     */
750    private class WatcherResultPrinter implements TestListener {
751        private final Bundle mResultTemplate;
752        Bundle mTestResult;
753        int mTestNum = 0;
754        int mTestResultCode = 0;
755        String mTestClass = null;
756
757        public WatcherResultPrinter(int numTests) {
758            mResultTemplate = new Bundle();
759            mResultTemplate.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
760            mResultTemplate.putInt(REPORT_KEY_NUM_TOTAL, numTests);
761        }
762
763        /**
764         * send a status for the start of a each test, so long tests can be seen
765         * as "running"
766         */
767        public void startTest(Test test) {
768            String testClass = test.getClass().getName();
769            String testName = ((TestCase)test).getName();
770            mTestResult = new Bundle(mResultTemplate);
771            mTestResult.putString(REPORT_KEY_NAME_CLASS, testClass);
772            mTestResult.putString(REPORT_KEY_NAME_TEST, testName);
773            mTestResult.putInt(REPORT_KEY_NUM_CURRENT, ++mTestNum);
774            // pretty printing
775            if (testClass != null && !testClass.equals(mTestClass)) {
776                mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
777                        String.format("\n%s:", testClass));
778                mTestClass = testClass;
779            } else {
780                mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "");
781            }
782
783            Method testMethod = null;
784            try {
785                testMethod = test.getClass().getMethod(testName);
786                // Report total number of iterations, if test is repetitive
787                if (testMethod.isAnnotationPresent(RepetitiveTest.class)) {
788                    int numIterations = testMethod.getAnnotation(
789                        RepetitiveTest.class).numIterations();
790                    mTestResult.putInt(REPORT_KEY_NUM_ITERATIONS, numIterations);
791                }
792            } catch (NoSuchMethodException e) {
793                // ignore- the test with given name does not exist. Will be handled during test
794                // execution
795            }
796
797            // The delay_msec parameter is normally used to provide buffers of idle time
798            // for power measurement purposes. To make sure there is a delay before and after
799            // every test in a suite, we delay *after* every test (see endTest below) and also
800            // delay *before* the first test. So, delay test1 delay test2 delay.
801
802            try {
803                if (mTestNum == 1) Thread.sleep(mDelayMsec);
804            } catch (InterruptedException e) {
805                throw new IllegalStateException(e);
806            }
807
808            sendStatus(REPORT_VALUE_RESULT_START, mTestResult);
809            mTestResultCode = 0;
810        }
811
812        /**
813         * @see junit.framework.TestListener#addError(Test, Throwable)
814         */
815        public void addError(Test test, Throwable t) {
816            mTestResult.putString(REPORT_KEY_STACK, BaseTestRunner.getFilteredTrace(t));
817            mTestResultCode = REPORT_VALUE_RESULT_ERROR;
818            // pretty printing
819            mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
820                String.format("\nError in %s:\n%s",
821                    ((TestCase)test).getName(), BaseTestRunner.getFilteredTrace(t)));
822        }
823
824        /**
825         * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
826         */
827        public void addFailure(Test test, AssertionFailedError t) {
828            mTestResult.putString(REPORT_KEY_STACK, BaseTestRunner.getFilteredTrace(t));
829            mTestResultCode = REPORT_VALUE_RESULT_FAILURE;
830            // pretty printing
831            mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
832                String.format("\nFailure in %s:\n%s",
833                    ((TestCase)test).getName(), BaseTestRunner.getFilteredTrace(t)));
834        }
835
836        /**
837         * @see junit.framework.TestListener#endTest(Test)
838         */
839        public void endTest(Test test) {
840            if (mTestResultCode == 0) {
841                mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, ".");
842            }
843            sendStatus(mTestResultCode, mTestResult);
844
845            try { // Sleep after every test, if specified
846                Thread.sleep(mDelayMsec);
847            } catch (InterruptedException e) {
848                throw new IllegalStateException(e);
849            }
850        }
851
852        // TODO report the end of the cycle
853    }
854}
855