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