• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
JunitTestSuite()84         public JunitTestSuite() {
85             super();
86         }
87 
88         @Override
run(TestResult result)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          */
startTest(Test test)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          */
endTest(Test test)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          */
addError(Test test, Throwable t)124         public void addError(Test test, Throwable t) {
125             mError = true;
126             failed(test.toString(), t);
127         }
128 
addFailure(Test test, junit.framework.AssertionFailedError t)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 {
IntermediateTime(String name, long timeInNS)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 {
started(String className)153         void started(String className);
finished(String className)154         void finished(String className);
performance(String className, long itemTimeNS, int iterations, List<IntermediateTime> itermediates)155         void performance(String className,
156                 long itemTimeNS, int iterations,
157                 List<IntermediateTime> itermediates);
passed(String className)158         void passed(String className);
failed(String className, Throwable execption)159         void failed(String className, Throwable execption);
160     }
161 
TestRunner(Context context)162     public TestRunner(Context context) {
163         mContext = context;
164     }
165 
addListener(Listener listener)166     public void addListener(Listener listener) {
167         mListeners.add(listener);
168     }
169 
startProfiling()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 
finishProfiling()177     public void finishProfiling() {
178         Debug.stopMethodTracing();
179     }
180 
started(String className)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 
finished(String className)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 
performance(String className, long itemTimeNS, int iterations, List<IntermediateTime> intermediates)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 
passed(String className)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 
failed(String className, Throwable exception)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 
passedCount()225     public int passedCount() {
226         return mPassed;
227     }
228 
failedCount()229     public int failedCount() {
230         return mFailed;
231     }
232 
run(String[] classes)233     public void run(String[] classes) {
234         for (String cl : classes) {
235             run(cl);
236         }
237     }
238 
setInternalIterations(int count)239     public void setInternalIterations(int count) {
240         mInternalIterations = count;
241     }
242 
startTiming(boolean realTime)243     public void startTiming(boolean realTime) {
244         if (realTime) {
245             mStartTime = System.currentTimeMillis();
246         } else {
247             mStartTime = SystemClock.currentThreadTimeMillis();
248         }
249     }
250 
addIntermediate(String name)251     public void addIntermediate(String name) {
252         addIntermediate(name, (System.currentTimeMillis() - mStartTime) * 1000000);
253     }
254 
addIntermediate(String name, long timeInNS)255     public void addIntermediate(String name, long timeInNS) {
256         mIntermediates.add(new IntermediateTime(name, timeInNS));
257     }
258 
finishTiming(boolean realTime)259     public void finishTiming(boolean realTime) {
260         if (realTime) {
261             mEndTime = System.currentTimeMillis();
262         } else {
263             mEndTime = SystemClock.currentThreadTimeMillis();
264         }
265     }
266 
setPerformanceMode(int mode)267     public void setPerformanceMode(int mode) {
268         mMode = mode;
269     }
270 
missingTest(String className, Throwable e)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      */
run(String className)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 
runInPerformanceMode(Object testCase, String className, boolean junitTest, String testNameInDb)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 
runSingleJunitTest(String className)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 
getChildrenMethod(Class clazz)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 
getChildrenMethod(Context c, String className)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 
getChildren(Context c, String className)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 
getChildren(Method m)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 
getTestChildren(Context c, String className)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 
getTestChildren(Class clazz)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 
getAllTestMethods(Class clazz)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 
isTestMethod(Method m)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 
countJunitTests(Class clazz)646     public static int countJunitTests(Class clazz) {
647         Method[] allTestMethods = getAllTestMethods(clazz);
648         int numberofMethods = allTestMethods.length;
649 
650         return numberofMethods;
651     }
652 
isTestSuite(Context c, String className)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 
isJunitTest(String className)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      */
countTests(Context c, String className)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      */
getTitle(String className)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