blob: beecc6fdd9ded495265a6338f5a7ccf250fa9646 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.test;
18
19import android.content.Context;
20import android.util.Log;
21import android.os.Debug;
22import android.os.SystemClock;
23
24import java.io.File;
25import java.lang.reflect.InvocationTargetException;
26import java.lang.reflect.Method;
27import java.lang.reflect.Modifier;
28import java.util.ArrayList;
29import java.util.List;
30
31import junit.framework.TestSuite;
32import junit.framework.TestListener;
33import junit.framework.Test;
34import junit.framework.TestResult;
35import 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.
Dmitri Plotnikov44a29dd2009-08-03 14:27:20 -070042 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043 * {@hide} Not needed for 1.0 SDK.
44 */
Stephan Linznerb51617f2016-01-27 18:09:50 -080045@Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046public 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
Dmitri Plotnikov44a29dd2009-08-03 14:27:20 -070088 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 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) {
Dmitri Plotnikov44a29dd2009-08-03 14:27:20 -0700306 //Need a way to mark a test to be run in profiling mode or not.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 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);
Dmitri Plotnikov44a29dd2009-08-03 14:27:20 -0700342 testcase.setTestContext(mContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 } 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
Dmitri Plotnikov44a29dd2009-08-03 14:27:20 -0700706 // is missing, and it will fail when run or it is a single Junit test to be run.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 }
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}