Home | History | Annotate | Download | only in test
      1 /*
      2  * Copyright (C) 2006 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 android.content.Context;
     20 import android.util.Log;
     21 import android.os.Debug;
     22 import android.os.SystemClock;
     23 
     24 import java.io.File;
     25 import java.lang.reflect.InvocationTargetException;
     26 import java.lang.reflect.Method;
     27 import java.lang.reflect.Modifier;
     28 import java.util.ArrayList;
     29 import java.util.List;
     30 
     31 import junit.framework.TestSuite;
     32 import junit.framework.TestListener;
     33 import junit.framework.Test;
     34 import junit.framework.TestResult;
     35 import com.google.android.collect.Lists;
     36 
     37 /**
     38  * Support class that actually runs a test. Android uses this class,
     39  * and you probably will not need to instantiate, extend, or call this
     40  * class yourself. See the full {@link android.test} package description
     41  * to learn more about testing Android applications.
     42  *
     43  * {@hide} Not needed for 1.0 SDK.
     44  */
     45 @Deprecated
     46 public class TestRunner implements PerformanceTestCase.Intermediates {
     47     public static final int REGRESSION = 0;
     48     public static final int PERFORMANCE = 1;
     49     public static final int PROFILING = 2;
     50 
     51     public static final int CLEARSCREEN = 0;
     52     private static final String TAG = "TestHarness";
     53     private Context mContext;
     54 
     55     private int mMode = REGRESSION;
     56 
     57     private List<Listener> mListeners = Lists.newArrayList();
     58     private int mPassed;
     59     private int mFailed;
     60 
     61     private int mInternalIterations;
     62     private long mStartTime;
     63     private long mEndTime;
     64 
     65     private String mClassName;
     66 
     67     List<IntermediateTime> mIntermediates = null;
     68 
     69     private static Class mRunnableClass;
     70     private static Class mJUnitClass;
     71 
     72     static {
     73         try {
     74             mRunnableClass = Class.forName("java.lang.Runnable", false, null);
     75             mJUnitClass = Class.forName("junit.framework.TestCase", false, null);
     76         } catch (ClassNotFoundException ex) {
     77             throw new RuntimeException("shouldn't happen", ex);
     78         }
     79     }
     80 
     81     public class JunitTestSuite extends TestSuite implements TestListener {
     82         boolean mError = false;
     83 
     84         public JunitTestSuite() {
     85             super();
     86         }
     87 
     88         @Override
     89         public void run(TestResult result) {
     90             result.addListener(this);
     91             super.run(result);
     92             result.removeListener(this);
     93         }
     94 
     95         /**
     96          * Implemented method of the interface TestListener which will listen for the
     97          * start of a test.
     98          *
     99          * @param test
    100          */
    101         public void startTest(Test test) {
    102             started(test.toString());
    103         }
    104 
    105         /**
    106          * Implemented method of the interface TestListener which will listen for the
    107          * end of the test.
    108          *
    109          * @param test
    110          */
    111         public void endTest(Test test) {
    112             finished(test.toString());
    113             if (!mError) {
    114                 passed(test.toString());
    115             }
    116         }
    117 
    118         /**
    119          * Implemented method of the interface TestListener which will listen for an
    120          * mError while running the test.
    121          *
    122          * @param test
    123          */
    124         public void addError(Test test, Throwable t) {
    125             mError = true;
    126             failed(test.toString(), t);
    127         }
    128 
    129         public void addFailure(Test test, junit.framework.AssertionFailedError t) {
    130             mError = true;
    131             failed(test.toString(), t);
    132         }
    133     }
    134 
    135     /**
    136      * Listener.performance() 'intermediates' argument is a list of these.
    137      */
    138     public static class IntermediateTime {
    139         public IntermediateTime(String name, long timeInNS) {
    140             this.name = name;
    141             this.timeInNS = timeInNS;
    142         }
    143 
    144         public String name;
    145         public long timeInNS;
    146     }
    147 
    148     /**
    149      * Support class that receives status on test progress. You should not need to
    150      * extend this interface yourself.
    151      */
    152     public interface Listener {
    153         void started(String className);
    154         void finished(String className);
    155         void performance(String className,
    156                 long itemTimeNS, int iterations,
    157                 List<IntermediateTime> itermediates);
    158         void passed(String className);
    159         void failed(String className, Throwable execption);
    160     }
    161 
    162     public TestRunner(Context context) {
    163         mContext = context;
    164     }
    165 
    166     public void addListener(Listener listener) {
    167         mListeners.add(listener);
    168     }
    169 
    170     public void startProfiling() {
    171         File file = new File("/tmp/trace");
    172         file.mkdir();
    173         String base = "/tmp/trace/" + mClassName + ".dmtrace";
    174         Debug.startMethodTracing(base, 8 * 1024 * 1024);
    175     }
    176 
    177     public void finishProfiling() {
    178         Debug.stopMethodTracing();
    179     }
    180 
    181     private void started(String className) {
    182 
    183         int count = mListeners.size();
    184         for (int i = 0; i < count; i++) {
    185             mListeners.get(i).started(className);
    186         }
    187     }
    188 
    189     private void finished(String className) {
    190         int count = mListeners.size();
    191         for (int i = 0; i < count; i++) {
    192             mListeners.get(i).finished(className);
    193         }
    194     }
    195 
    196     private void performance(String className,
    197             long itemTimeNS,
    198             int iterations,
    199             List<IntermediateTime> intermediates) {
    200         int count = mListeners.size();
    201         for (int i = 0; i < count; i++) {
    202             mListeners.get(i).performance(className,
    203                     itemTimeNS,
    204                     iterations,
    205                     intermediates);
    206         }
    207     }
    208 
    209     public void passed(String className) {
    210         mPassed++;
    211         int count = mListeners.size();
    212         for (int i = 0; i < count; i++) {
    213             mListeners.get(i).passed(className);
    214         }
    215     }
    216 
    217     public void failed(String className, Throwable exception) {
    218         mFailed++;
    219         int count = mListeners.size();
    220         for (int i = 0; i < count; i++) {
    221             mListeners.get(i).failed(className, exception);
    222         }
    223     }
    224 
    225     public int passedCount() {
    226         return mPassed;
    227     }
    228 
    229     public int failedCount() {
    230         return mFailed;
    231     }
    232 
    233     public void run(String[] classes) {
    234         for (String cl : classes) {
    235             run(cl);
    236         }
    237     }
    238 
    239     public void setInternalIterations(int count) {
    240         mInternalIterations = count;
    241     }
    242 
    243     public void startTiming(boolean realTime) {
    244         if (realTime) {
    245             mStartTime = System.currentTimeMillis();
    246         } else {
    247             mStartTime = SystemClock.currentThreadTimeMillis();
    248         }
    249     }
    250 
    251     public void addIntermediate(String name) {
    252         addIntermediate(name, (System.currentTimeMillis() - mStartTime) * 1000000);
    253     }
    254 
    255     public void addIntermediate(String name, long timeInNS) {
    256         mIntermediates.add(new IntermediateTime(name, timeInNS));
    257     }
    258 
    259     public void finishTiming(boolean realTime) {
    260         if (realTime) {
    261             mEndTime = System.currentTimeMillis();
    262         } else {
    263             mEndTime = SystemClock.currentThreadTimeMillis();
    264         }
    265     }
    266 
    267     public void setPerformanceMode(int mode) {
    268         mMode = mode;
    269     }
    270 
    271     private void missingTest(String className, Throwable e) {
    272         started(className);
    273         finished(className);
    274         failed(className, e);
    275     }
    276 
    277     /*
    278     This class determines if more suites are added to this class then adds all individual
    279     test classes to a test suite for run
    280      */
    281     public void run(String className) {
    282         try {
    283             mClassName = className;
    284             Class clazz = mContext.getClassLoader().loadClass(className);
    285             Method method = getChildrenMethod(clazz);
    286             if (method != null) {
    287                 String[] children = getChildren(method);
    288                 run(children);
    289             } else if (mRunnableClass.isAssignableFrom(clazz)) {
    290                 Runnable test = (Runnable) clazz.newInstance();
    291                 TestCase testcase = null;
    292                 if (test instanceof TestCase) {
    293                     testcase = (TestCase) test;
    294                 }
    295                 Throwable e = null;
    296                 boolean didSetup = false;
    297                 started(className);
    298                 try {
    299                     if (testcase != null) {
    300                         testcase.setUp(mContext);
    301                         didSetup = true;
    302                     }
    303                     if (mMode == PERFORMANCE) {
    304                         runInPerformanceMode(test, className, false, className);
    305                     } else if (mMode == PROFILING) {
    306                         //Need a way to mark a test to be run in profiling mode or not.
    307                         startProfiling();
    308                         test.run();
    309                         finishProfiling();
    310                     } else {
    311                         test.run();
    312                     }
    313                 } catch (Throwable ex) {
    314                     e = ex;
    315                 }
    316                 if (testcase != null && didSetup) {
    317                     try {
    318                         testcase.tearDown();
    319                     } catch (Throwable ex) {
    320                         e = ex;
    321                     }
    322                 }
    323                 finished(className);
    324                 if (e == null) {
    325                     passed(className);
    326                 } else {
    327                     failed(className, e);
    328                 }
    329             } else if (mJUnitClass.isAssignableFrom(clazz)) {
    330                 Throwable e = null;
    331                 //Create a Junit Suite.
    332                 JunitTestSuite suite = new JunitTestSuite();
    333                 Method[] methods = getAllTestMethods(clazz);
    334                 for (Method m : methods) {
    335                     junit.framework.TestCase test = (junit.framework.TestCase) clazz.newInstance();
    336                     test.setName(m.getName());
    337 
    338                     if (test instanceof AndroidTestCase) {
    339                         AndroidTestCase testcase = (AndroidTestCase) test;
    340                         try {
    341                             testcase.setContext(mContext);
    342                             testcase.setTestContext(mContext);
    343                         } catch (Exception ex) {
    344                             Log.i("TestHarness", ex.toString());
    345                         }
    346                     }
    347                     suite.addTest(test);
    348                 }
    349                 if (mMode == PERFORMANCE) {
    350                     final int testCount = suite.testCount();
    351 
    352                     for (int j = 0; j < testCount; j++) {
    353                         Test test = suite.testAt(j);
    354                         started(test.toString());
    355                         try {
    356                             runInPerformanceMode(test, className, true, test.toString());
    357                         } catch (Throwable ex) {
    358                             e = ex;
    359                         }
    360                         finished(test.toString());
    361                         if (e == null) {
    362                             passed(test.toString());
    363                         } else {
    364                             failed(test.toString(), e);
    365                         }
    366                     }
    367                 } else if (mMode == PROFILING) {
    368                     //Need a way to mark a test to be run in profiling mode or not.
    369                     startProfiling();
    370                     junit.textui.TestRunner.run(suite);
    371                     finishProfiling();
    372                 } else {
    373                     junit.textui.TestRunner.run(suite);
    374                 }
    375             } else {
    376                 System.out.println("Test wasn't Runnable and didn't have a"
    377                         + " children method: " + className);
    378             }
    379         } catch (ClassNotFoundException e) {
    380             Log.e("ClassNotFoundException for " + className, e.toString());
    381             if (isJunitTest(className)) {
    382                 runSingleJunitTest(className);
    383             } else {
    384                 missingTest(className, e);
    385             }
    386         } catch (InstantiationException e) {
    387             System.out.println("InstantiationException for " + className);
    388             missingTest(className, e);
    389         } catch (IllegalAccessException e) {
    390             System.out.println("IllegalAccessException for " + className);
    391             missingTest(className, e);
    392         }
    393     }
    394 
    395     public void runInPerformanceMode(Object testCase, String className, boolean junitTest,
    396             String testNameInDb) throws Exception {
    397         boolean increaseIterations = true;
    398         int iterations = 1;
    399         long duration = 0;
    400         mIntermediates = null;
    401 
    402         mInternalIterations = 1;
    403         Class clazz = mContext.getClassLoader().loadClass(className);
    404         Object perftest = clazz.newInstance();
    405 
    406         PerformanceTestCase perftestcase = null;
    407         if (perftest instanceof PerformanceTestCase) {
    408             perftestcase = (PerformanceTestCase) perftest;
    409             // only run the test if it is not marked as a performance only test
    410             if (mMode == REGRESSION && perftestcase.isPerformanceOnly()) return;
    411         }
    412 
    413         // First force GCs, to avoid GCs happening during out
    414         // test and skewing its time.
    415         Runtime.getRuntime().runFinalization();
    416         Runtime.getRuntime().gc();
    417 
    418         if (perftestcase != null) {
    419             mIntermediates = new ArrayList<IntermediateTime>();
    420             iterations = perftestcase.startPerformance(this);
    421             if (iterations > 0) {
    422                 increaseIterations = false;
    423             } else {
    424                 iterations = 1;
    425             }
    426         }
    427 
    428         // Pause briefly to let things settle down...
    429         Thread.sleep(1000);
    430         do {
    431             mEndTime = 0;
    432             if (increaseIterations) {
    433                 // Test case does not implement
    434                 // PerformanceTestCase or returned 0 iterations,
    435                 // so we take care of measure the whole test time.
    436                 mStartTime = SystemClock.currentThreadTimeMillis();
    437             } else {
    438                 // Try to make it obvious if the test case
    439                 // doesn't call startTiming().
    440                 mStartTime = 0;
    441             }
    442 
    443             if (junitTest) {
    444                 for (int i = 0; i < iterations; i++) {
    445                     junit.textui.TestRunner.run((junit.framework.Test) testCase);
    446                 }
    447             } else {
    448                 Runnable test = (Runnable) testCase;
    449                 for (int i = 0; i < iterations; i++) {
    450                     test.run();
    451                 }
    452             }
    453 
    454             long endTime = mEndTime;
    455             if (endTime == 0) {
    456                 endTime = SystemClock.currentThreadTimeMillis();
    457             }
    458 
    459             duration = endTime - mStartTime;
    460             if (!increaseIterations) {
    461                 break;
    462             }
    463             if (duration <= 1) {
    464                 iterations *= 1000;
    465             } else if (duration <= 10) {
    466                 iterations *= 100;
    467             } else if (duration < 100) {
    468                 iterations *= 10;
    469             } else if (duration < 1000) {
    470                 iterations *= (int) ((1000 / duration) + 2);
    471             } else {
    472                 break;
    473             }
    474         } while (true);
    475 
    476         if (duration != 0) {
    477             iterations *= mInternalIterations;
    478             performance(testNameInDb, (duration * 1000000) / iterations,
    479                     iterations, mIntermediates);
    480         }
    481     }
    482 
    483     public void runSingleJunitTest(String className) {
    484         Throwable excep = null;
    485         int index = className.lastIndexOf('$');
    486         String testName = "";
    487         String originalClassName = className;
    488         if (index >= 0) {
    489             className = className.substring(0, index);
    490             testName = originalClassName.substring(index + 1);
    491         }
    492         try {
    493             Class clazz = mContext.getClassLoader().loadClass(className);
    494             if (mJUnitClass.isAssignableFrom(clazz)) {
    495                 junit.framework.TestCase test = (junit.framework.TestCase) clazz.newInstance();
    496                 JunitTestSuite newSuite = new JunitTestSuite();
    497                 test.setName(testName);
    498 
    499                 if (test instanceof AndroidTestCase) {
    500                     AndroidTestCase testcase = (AndroidTestCase) test;
    501                     try {
    502                         testcase.setContext(mContext);
    503                     } catch (Exception ex) {
    504                         Log.w(TAG, "Exception encountered while trying to set the context.", ex);
    505                     }
    506                 }
    507                 newSuite.addTest(test);
    508 
    509                 if (mMode == PERFORMANCE) {
    510                     try {
    511                         started(test.toString());
    512                         runInPerformanceMode(test, className, true, test.toString());
    513                         finished(test.toString());
    514                         if (excep == null) {
    515                             passed(test.toString());
    516                         } else {
    517                             failed(test.toString(), excep);
    518                         }
    519                     } catch (Throwable ex) {
    520                         excep = ex;
    521                     }
    522 
    523                 } else if (mMode == PROFILING) {
    524                     startProfiling();
    525                     junit.textui.TestRunner.run(newSuite);
    526                     finishProfiling();
    527                 } else {
    528                     junit.textui.TestRunner.run(newSuite);
    529                 }
    530             }
    531         } catch (ClassNotFoundException e) {
    532             Log.e("TestHarness", "No test case to run", e);
    533         } catch (IllegalAccessException e) {
    534             Log.e("TestHarness", "Illegal Access Exception", e);
    535         } catch (InstantiationException e) {
    536             Log.e("TestHarness", "Instantiation Exception", e);
    537         }
    538     }
    539 
    540     public static Method getChildrenMethod(Class clazz) {
    541         try {
    542             return clazz.getMethod("children", (Class[]) null);
    543         } catch (NoSuchMethodException e) {
    544         }
    545 
    546         return null;
    547     }
    548 
    549     public static Method getChildrenMethod(Context c, String className) {
    550         try {
    551             return getChildrenMethod(c.getClassLoader().loadClass(className));
    552         } catch (ClassNotFoundException e) {
    553         }
    554         return null;
    555     }
    556 
    557     public static String[] getChildren(Context c, String className) {
    558         Method m = getChildrenMethod(c, className);
    559         String[] testChildren = getTestChildren(c, className);
    560         if (m == null & testChildren == null) {
    561             throw new RuntimeException("couldn't get children method for "
    562                     + className);
    563         }
    564         if (m != null) {
    565             String[] children = getChildren(m);
    566             if (testChildren != null) {
    567                 String[] allChildren = new String[testChildren.length + children.length];
    568                 System.arraycopy(children, 0, allChildren, 0, children.length);
    569                 System.arraycopy(testChildren, 0, allChildren, children.length, testChildren.length);
    570                 return allChildren;
    571             } else {
    572                 return children;
    573             }
    574         } else {
    575             if (testChildren != null) {
    576                 return testChildren;
    577             }
    578         }
    579         return null;
    580     }
    581 
    582     public static String[] getChildren(Method m) {
    583         try {
    584             if (!Modifier.isStatic(m.getModifiers())) {
    585                 throw new RuntimeException("children method is not static");
    586             }
    587             return (String[]) m.invoke(null, (Object[]) null);
    588         } catch (IllegalAccessException e) {
    589         } catch (InvocationTargetException e) {
    590         }
    591         return new String[0];
    592     }
    593 
    594     public static String[] getTestChildren(Context c, String className) {
    595         try {
    596             Class clazz = c.getClassLoader().loadClass(className);
    597 
    598             if (mJUnitClass.isAssignableFrom(clazz)) {
    599                 return getTestChildren(clazz);
    600             }
    601         } catch (ClassNotFoundException e) {
    602             Log.e("TestHarness", "No class found", e);
    603         }
    604         return null;
    605     }
    606 
    607     public static String[] getTestChildren(Class clazz) {
    608         Method[] methods = getAllTestMethods(clazz);
    609 
    610         String[] onScreenTestNames = new String[methods.length];
    611         int index = 0;
    612         for (Method m : methods) {
    613             onScreenTestNames[index] = clazz.getName() + "$" + m.getName();
    614             index++;
    615         }
    616         return onScreenTestNames;
    617     }
    618 
    619     public static Method[] getAllTestMethods(Class clazz) {
    620         Method[] allMethods = clazz.getDeclaredMethods();
    621         int numOfMethods = 0;
    622         for (Method m : allMethods) {
    623             boolean mTrue = isTestMethod(m);
    624             if (mTrue) {
    625                 numOfMethods++;
    626             }
    627         }
    628         int index = 0;
    629         Method[] testMethods = new Method[numOfMethods];
    630         for (Method m : allMethods) {
    631             boolean mTrue = isTestMethod(m);
    632             if (mTrue) {
    633                 testMethods[index] = m;
    634                 index++;
    635             }
    636         }
    637         return testMethods;
    638     }
    639 
    640     private static boolean isTestMethod(Method m) {
    641         return m.getName().startsWith("test") &&
    642                 m.getReturnType() == void.class &&
    643                 m.getParameterTypes().length == 0;
    644     }
    645 
    646     public static int countJunitTests(Class clazz) {
    647         Method[] allTestMethods = getAllTestMethods(clazz);
    648         int numberofMethods = allTestMethods.length;
    649 
    650         return numberofMethods;
    651     }
    652 
    653     public static boolean isTestSuite(Context c, String className) {
    654         boolean childrenMethods = getChildrenMethod(c, className) != null;
    655 
    656         try {
    657             Class clazz = c.getClassLoader().loadClass(className);
    658             if (mJUnitClass.isAssignableFrom(clazz)) {
    659                 int numTests = countJunitTests(clazz);
    660                 if (numTests > 0)
    661                     childrenMethods = true;
    662             }
    663         } catch (ClassNotFoundException e) {
    664         }
    665         return childrenMethods;
    666     }
    667 
    668 
    669     public boolean isJunitTest(String className) {
    670         int index = className.lastIndexOf('$');
    671         if (index >= 0) {
    672             className = className.substring(0, index);
    673         }
    674         try {
    675             Class clazz = mContext.getClassLoader().loadClass(className);
    676             if (mJUnitClass.isAssignableFrom(clazz)) {
    677                 return true;
    678             }
    679         } catch (ClassNotFoundException e) {
    680         }
    681         return false;
    682     }
    683 
    684     /**
    685      * Returns the number of tests that will be run if you try to do this.
    686      */
    687     public static int countTests(Context c, String className) {
    688         try {
    689             Class clazz = c.getClassLoader().loadClass(className);
    690             Method method = getChildrenMethod(clazz);
    691             if (method != null) {
    692 
    693                 String[] children = getChildren(method);
    694                 int rv = 0;
    695                 for (String child : children) {
    696                     rv += countTests(c, child);
    697                 }
    698                 return rv;
    699             } else if (mRunnableClass.isAssignableFrom(clazz)) {
    700                 return 1;
    701             } else if (mJUnitClass.isAssignableFrom(clazz)) {
    702                 return countJunitTests(clazz);
    703             }
    704         } catch (ClassNotFoundException e) {
    705             return 1; // this gets the count right, because either this test
    706             // is missing, and it will fail when run or it is a single Junit test to be run.
    707         }
    708         return 0;
    709     }
    710 
    711     /**
    712      * Returns a title to display given the className of a test.
    713      * <p/>
    714      * <p>Currently this function just returns the portion of the
    715      * class name after the last '.'
    716      */
    717     public static String getTitle(String className) {
    718         int indexDot = className.lastIndexOf('.');
    719         int indexDollar = className.lastIndexOf('$');
    720         int index = indexDot > indexDollar ? indexDot : indexDollar;
    721         if (index >= 0) {
    722             className = className.substring(index + 1);
    723         }
    724         return className;
    725     }
    726 }
    727