summaryrefslogtreecommitdiffstats
path: root/test-runner/android/test/TestRunner.java
diff options
context:
space:
mode:
Diffstat (limited to 'test-runner/android/test/TestRunner.java')
-rw-r--r--test-runner/android/test/TestRunner.java725
1 files changed, 725 insertions, 0 deletions
diff --git a/test-runner/android/test/TestRunner.java b/test-runner/android/test/TestRunner.java
new file mode 100644
index 0000000..012df35
--- /dev/null
+++ b/test-runner/android/test/TestRunner.java
@@ -0,0 +1,725 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test;
+
+import android.content.Context;
+import android.util.Log;
+import android.os.Debug;
+import android.os.SystemClock;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestSuite;
+import junit.framework.TestListener;
+import junit.framework.Test;
+import junit.framework.TestResult;
+import com.google.android.collect.Lists;
+
+/**
+ * Support class that actually runs a test. Android uses this class,
+ * and you probably will not need to instantiate, extend, or call this
+ * class yourself. See the full {@link android.test} package description
+ * to learn more about testing Android applications.
+ *
+ * {@hide} Not needed for 1.0 SDK.
+ */
+public class TestRunner implements PerformanceTestCase.Intermediates {
+ public static final int REGRESSION = 0;
+ public static final int PERFORMANCE = 1;
+ public static final int PROFILING = 2;
+
+ public static final int CLEARSCREEN = 0;
+ private static final String TAG = "TestHarness";
+ private Context mContext;
+
+ private int mMode = REGRESSION;
+
+ private List<Listener> mListeners = Lists.newArrayList();
+ private int mPassed;
+ private int mFailed;
+
+ private int mInternalIterations;
+ private long mStartTime;
+ private long mEndTime;
+
+ private String mClassName;
+
+ List<IntermediateTime> mIntermediates = null;
+
+ private static Class mRunnableClass;
+ private static Class mJUnitClass;
+
+ static {
+ try {
+ mRunnableClass = Class.forName("java.lang.Runnable", false, null);
+ mJUnitClass = Class.forName("junit.framework.TestCase", false, null);
+ } catch (ClassNotFoundException ex) {
+ throw new RuntimeException("shouldn't happen", ex);
+ }
+ }
+
+ public class JunitTestSuite extends TestSuite implements TestListener {
+ boolean mError = false;
+
+ public JunitTestSuite() {
+ super();
+ }
+
+ @Override
+ public void run(TestResult result) {
+ result.addListener(this);
+ super.run(result);
+ result.removeListener(this);
+ }
+
+ /**
+ * Implemented method of the interface TestListener which will listen for the
+ * start of a test.
+ *
+ * @param test
+ */
+ public void startTest(Test test) {
+ started(test.toString());
+ }
+
+ /**
+ * Implemented method of the interface TestListener which will listen for the
+ * end of the test.
+ *
+ * @param test
+ */
+ public void endTest(Test test) {
+ finished(test.toString());
+ if (!mError) {
+ passed(test.toString());
+ }
+ }
+
+ /**
+ * Implemented method of the interface TestListener which will listen for an
+ * mError while running the test.
+ *
+ * @param test
+ */
+ public void addError(Test test, Throwable t) {
+ mError = true;
+ failed(test.toString(), t);
+ }
+
+ public void addFailure(Test test, junit.framework.AssertionFailedError t) {
+ mError = true;
+ failed(test.toString(), t);
+ }
+ }
+
+ /**
+ * Listener.performance() 'intermediates' argument is a list of these.
+ */
+ public static class IntermediateTime {
+ public IntermediateTime(String name, long timeInNS) {
+ this.name = name;
+ this.timeInNS = timeInNS;
+ }
+
+ public String name;
+ public long timeInNS;
+ }
+
+ /**
+ * Support class that receives status on test progress. You should not need to
+ * extend this interface yourself.
+ */
+ public interface Listener {
+ void started(String className);
+ void finished(String className);
+ void performance(String className,
+ long itemTimeNS, int iterations,
+ List<IntermediateTime> itermediates);
+ void passed(String className);
+ void failed(String className, Throwable execption);
+ }
+
+ public TestRunner(Context context) {
+ mContext = context;
+ }
+
+ public void addListener(Listener listener) {
+ mListeners.add(listener);
+ }
+
+ public void startProfiling() {
+ File file = new File("/tmp/trace");
+ file.mkdir();
+ String base = "/tmp/trace/" + mClassName + ".dmtrace";
+ Debug.startMethodTracing(base, 8 * 1024 * 1024);
+ }
+
+ public void finishProfiling() {
+ Debug.stopMethodTracing();
+ }
+
+ private void started(String className) {
+
+ int count = mListeners.size();
+ for (int i = 0; i < count; i++) {
+ mListeners.get(i).started(className);
+ }
+ }
+
+ private void finished(String className) {
+ int count = mListeners.size();
+ for (int i = 0; i < count; i++) {
+ mListeners.get(i).finished(className);
+ }
+ }
+
+ private void performance(String className,
+ long itemTimeNS,
+ int iterations,
+ List<IntermediateTime> intermediates) {
+ int count = mListeners.size();
+ for (int i = 0; i < count; i++) {
+ mListeners.get(i).performance(className,
+ itemTimeNS,
+ iterations,
+ intermediates);
+ }
+ }
+
+ public void passed(String className) {
+ mPassed++;
+ int count = mListeners.size();
+ for (int i = 0; i < count; i++) {
+ mListeners.get(i).passed(className);
+ }
+ }
+
+ public void failed(String className, Throwable exception) {
+ mFailed++;
+ int count = mListeners.size();
+ for (int i = 0; i < count; i++) {
+ mListeners.get(i).failed(className, exception);
+ }
+ }
+
+ public int passedCount() {
+ return mPassed;
+ }
+
+ public int failedCount() {
+ return mFailed;
+ }
+
+ public void run(String[] classes) {
+ for (String cl : classes) {
+ run(cl);
+ }
+ }
+
+ public void setInternalIterations(int count) {
+ mInternalIterations = count;
+ }
+
+ public void startTiming(boolean realTime) {
+ if (realTime) {
+ mStartTime = System.currentTimeMillis();
+ } else {
+ mStartTime = SystemClock.currentThreadTimeMillis();
+ }
+ }
+
+ public void addIntermediate(String name) {
+ addIntermediate(name, (System.currentTimeMillis() - mStartTime) * 1000000);
+ }
+
+ public void addIntermediate(String name, long timeInNS) {
+ mIntermediates.add(new IntermediateTime(name, timeInNS));
+ }
+
+ public void finishTiming(boolean realTime) {
+ if (realTime) {
+ mEndTime = System.currentTimeMillis();
+ } else {
+ mEndTime = SystemClock.currentThreadTimeMillis();
+ }
+ }
+
+ public void setPerformanceMode(int mode) {
+ mMode = mode;
+ }
+
+ private void missingTest(String className, Throwable e) {
+ started(className);
+ finished(className);
+ failed(className, e);
+ }
+
+ /*
+ This class determines if more suites are added to this class then adds all individual
+ test classes to a test suite for run
+ */
+ public void run(String className) {
+ try {
+ mClassName = className;
+ Class clazz = mContext.getClassLoader().loadClass(className);
+ Method method = getChildrenMethod(clazz);
+ if (method != null) {
+ String[] children = getChildren(method);
+ run(children);
+ } else if (mRunnableClass.isAssignableFrom(clazz)) {
+ Runnable test = (Runnable) clazz.newInstance();
+ TestCase testcase = null;
+ if (test instanceof TestCase) {
+ testcase = (TestCase) test;
+ }
+ Throwable e = null;
+ boolean didSetup = false;
+ started(className);
+ try {
+ if (testcase != null) {
+ testcase.setUp(mContext);
+ didSetup = true;
+ }
+ if (mMode == PERFORMANCE) {
+ runInPerformanceMode(test, className, false, className);
+ } else if (mMode == PROFILING) {
+ //Need a way to mark a test to be run in profiling mode or not.
+ startProfiling();
+ test.run();
+ finishProfiling();
+ } else {
+ test.run();
+ }
+ } catch (Throwable ex) {
+ e = ex;
+ }
+ if (testcase != null && didSetup) {
+ try {
+ testcase.tearDown();
+ } catch (Throwable ex) {
+ e = ex;
+ }
+ }
+ finished(className);
+ if (e == null) {
+ passed(className);
+ } else {
+ failed(className, e);
+ }
+ } else if (mJUnitClass.isAssignableFrom(clazz)) {
+ Throwable e = null;
+ //Create a Junit Suite.
+ JunitTestSuite suite = new JunitTestSuite();
+ Method[] methods = getAllTestMethods(clazz);
+ for (Method m : methods) {
+ junit.framework.TestCase test = (junit.framework.TestCase) clazz.newInstance();
+ test.setName(m.getName());
+
+ if (test instanceof AndroidTestCase) {
+ AndroidTestCase testcase = (AndroidTestCase) test;
+ try {
+ testcase.setContext(mContext);
+ testcase.setTestContext(mContext);
+ } catch (Exception ex) {
+ Log.i("TestHarness", ex.toString());
+ }
+ }
+ suite.addTest(test);
+ }
+ if (mMode == PERFORMANCE) {
+ final int testCount = suite.testCount();
+
+ for (int j = 0; j < testCount; j++) {
+ Test test = suite.testAt(j);
+ started(test.toString());
+ try {
+ runInPerformanceMode(test, className, true, test.toString());
+ } catch (Throwable ex) {
+ e = ex;
+ }
+ finished(test.toString());
+ if (e == null) {
+ passed(test.toString());
+ } else {
+ failed(test.toString(), e);
+ }
+ }
+ } else if (mMode == PROFILING) {
+ //Need a way to mark a test to be run in profiling mode or not.
+ startProfiling();
+ junit.textui.TestRunner.run(suite);
+ finishProfiling();
+ } else {
+ junit.textui.TestRunner.run(suite);
+ }
+ } else {
+ System.out.println("Test wasn't Runnable and didn't have a"
+ + " children method: " + className);
+ }
+ } catch (ClassNotFoundException e) {
+ Log.e("ClassNotFoundException for " + className, e.toString());
+ if (isJunitTest(className)) {
+ runSingleJunitTest(className);
+ } else {
+ missingTest(className, e);
+ }
+ } catch (InstantiationException e) {
+ System.out.println("InstantiationException for " + className);
+ missingTest(className, e);
+ } catch (IllegalAccessException e) {
+ System.out.println("IllegalAccessException for " + className);
+ missingTest(className, e);
+ }
+ }
+
+ public void runInPerformanceMode(Object testCase, String className, boolean junitTest,
+ String testNameInDb) throws Exception {
+ boolean increaseIterations = true;
+ int iterations = 1;
+ long duration = 0;
+ mIntermediates = null;
+
+ mInternalIterations = 1;
+ Class clazz = mContext.getClassLoader().loadClass(className);
+ Object perftest = clazz.newInstance();
+
+ PerformanceTestCase perftestcase = null;
+ if (perftest instanceof PerformanceTestCase) {
+ perftestcase = (PerformanceTestCase) perftest;
+ // only run the test if it is not marked as a performance only test
+ if (mMode == REGRESSION && perftestcase.isPerformanceOnly()) return;
+ }
+
+ // First force GCs, to avoid GCs happening during out
+ // test and skewing its time.
+ Runtime.getRuntime().runFinalization();
+ Runtime.getRuntime().gc();
+
+ if (perftestcase != null) {
+ mIntermediates = new ArrayList<IntermediateTime>();
+ iterations = perftestcase.startPerformance(this);
+ if (iterations > 0) {
+ increaseIterations = false;
+ } else {
+ iterations = 1;
+ }
+ }
+
+ // Pause briefly to let things settle down...
+ Thread.sleep(1000);
+ do {
+ mEndTime = 0;
+ if (increaseIterations) {
+ // Test case does not implement
+ // PerformanceTestCase or returned 0 iterations,
+ // so we take care of measure the whole test time.
+ mStartTime = SystemClock.currentThreadTimeMillis();
+ } else {
+ // Try to make it obvious if the test case
+ // doesn't call startTiming().
+ mStartTime = 0;
+ }
+
+ if (junitTest) {
+ for (int i = 0; i < iterations; i++) {
+ junit.textui.TestRunner.run((junit.framework.Test) testCase);
+ }
+ } else {
+ Runnable test = (Runnable) testCase;
+ for (int i = 0; i < iterations; i++) {
+ test.run();
+ }
+ }
+
+ long endTime = mEndTime;
+ if (endTime == 0) {
+ endTime = SystemClock.currentThreadTimeMillis();
+ }
+
+ duration = endTime - mStartTime;
+ if (!increaseIterations) {
+ break;
+ }
+ if (duration <= 1) {
+ iterations *= 1000;
+ } else if (duration <= 10) {
+ iterations *= 100;
+ } else if (duration < 100) {
+ iterations *= 10;
+ } else if (duration < 1000) {
+ iterations *= (int) ((1000 / duration) + 2);
+ } else {
+ break;
+ }
+ } while (true);
+
+ if (duration != 0) {
+ iterations *= mInternalIterations;
+ performance(testNameInDb, (duration * 1000000) / iterations,
+ iterations, mIntermediates);
+ }
+ }
+
+ public void runSingleJunitTest(String className) {
+ Throwable excep = null;
+ int index = className.lastIndexOf('$');
+ String testName = "";
+ String originalClassName = className;
+ if (index >= 0) {
+ className = className.substring(0, index);
+ testName = originalClassName.substring(index + 1);
+ }
+ try {
+ Class clazz = mContext.getClassLoader().loadClass(className);
+ if (mJUnitClass.isAssignableFrom(clazz)) {
+ junit.framework.TestCase test = (junit.framework.TestCase) clazz.newInstance();
+ JunitTestSuite newSuite = new JunitTestSuite();
+ test.setName(testName);
+
+ if (test instanceof AndroidTestCase) {
+ AndroidTestCase testcase = (AndroidTestCase) test;
+ try {
+ testcase.setContext(mContext);
+ } catch (Exception ex) {
+ Log.w(TAG, "Exception encountered while trying to set the context.", ex);
+ }
+ }
+ newSuite.addTest(test);
+
+ if (mMode == PERFORMANCE) {
+ try {
+ started(test.toString());
+ runInPerformanceMode(test, className, true, test.toString());
+ finished(test.toString());
+ if (excep == null) {
+ passed(test.toString());
+ } else {
+ failed(test.toString(), excep);
+ }
+ } catch (Throwable ex) {
+ excep = ex;
+ }
+
+ } else if (mMode == PROFILING) {
+ startProfiling();
+ junit.textui.TestRunner.run(newSuite);
+ finishProfiling();
+ } else {
+ junit.textui.TestRunner.run(newSuite);
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ Log.e("TestHarness", "No test case to run", e);
+ } catch (IllegalAccessException e) {
+ Log.e("TestHarness", "Illegal Access Exception", e);
+ } catch (InstantiationException e) {
+ Log.e("TestHarness", "Instantiation Exception", e);
+ }
+ }
+
+ public static Method getChildrenMethod(Class clazz) {
+ try {
+ return clazz.getMethod("children", (Class[]) null);
+ } catch (NoSuchMethodException e) {
+ }
+
+ return null;
+ }
+
+ public static Method getChildrenMethod(Context c, String className) {
+ try {
+ return getChildrenMethod(c.getClassLoader().loadClass(className));
+ } catch (ClassNotFoundException e) {
+ }
+ return null;
+ }
+
+ public static String[] getChildren(Context c, String className) {
+ Method m = getChildrenMethod(c, className);
+ String[] testChildren = getTestChildren(c, className);
+ if (m == null & testChildren == null) {
+ throw new RuntimeException("couldn't get children method for "
+ + className);
+ }
+ if (m != null) {
+ String[] children = getChildren(m);
+ if (testChildren != null) {
+ String[] allChildren = new String[testChildren.length + children.length];
+ System.arraycopy(children, 0, allChildren, 0, children.length);
+ System.arraycopy(testChildren, 0, allChildren, children.length, testChildren.length);
+ return allChildren;
+ } else {
+ return children;
+ }
+ } else {
+ if (testChildren != null) {
+ return testChildren;
+ }
+ }
+ return null;
+ }
+
+ public static String[] getChildren(Method m) {
+ try {
+ if (!Modifier.isStatic(m.getModifiers())) {
+ throw new RuntimeException("children method is not static");
+ }
+ return (String[]) m.invoke(null, (Object[]) null);
+ } catch (IllegalAccessException e) {
+ } catch (InvocationTargetException e) {
+ }
+ return new String[0];
+ }
+
+ public static String[] getTestChildren(Context c, String className) {
+ try {
+ Class clazz = c.getClassLoader().loadClass(className);
+
+ if (mJUnitClass.isAssignableFrom(clazz)) {
+ return getTestChildren(clazz);
+ }
+ } catch (ClassNotFoundException e) {
+ Log.e("TestHarness", "No class found", e);
+ }
+ return null;
+ }
+
+ public static String[] getTestChildren(Class clazz) {
+ Method[] methods = getAllTestMethods(clazz);
+
+ String[] onScreenTestNames = new String[methods.length];
+ int index = 0;
+ for (Method m : methods) {
+ onScreenTestNames[index] = clazz.getName() + "$" + m.getName();
+ index++;
+ }
+ return onScreenTestNames;
+ }
+
+ public static Method[] getAllTestMethods(Class clazz) {
+ Method[] allMethods = clazz.getDeclaredMethods();
+ int numOfMethods = 0;
+ for (Method m : allMethods) {
+ boolean mTrue = isTestMethod(m);
+ if (mTrue) {
+ numOfMethods++;
+ }
+ }
+ int index = 0;
+ Method[] testMethods = new Method[numOfMethods];
+ for (Method m : allMethods) {
+ boolean mTrue = isTestMethod(m);
+ if (mTrue) {
+ testMethods[index] = m;
+ index++;
+ }
+ }
+ return testMethods;
+ }
+
+ private static boolean isTestMethod(Method m) {
+ return m.getName().startsWith("test") &&
+ m.getReturnType() == void.class &&
+ m.getParameterTypes().length == 0;
+ }
+
+ public static int countJunitTests(Class clazz) {
+ Method[] allTestMethods = getAllTestMethods(clazz);
+ int numberofMethods = allTestMethods.length;
+
+ return numberofMethods;
+ }
+
+ public static boolean isTestSuite(Context c, String className) {
+ boolean childrenMethods = getChildrenMethod(c, className) != null;
+
+ try {
+ Class clazz = c.getClassLoader().loadClass(className);
+ if (mJUnitClass.isAssignableFrom(clazz)) {
+ int numTests = countJunitTests(clazz);
+ if (numTests > 0)
+ childrenMethods = true;
+ }
+ } catch (ClassNotFoundException e) {
+ }
+ return childrenMethods;
+ }
+
+
+ public boolean isJunitTest(String className) {
+ int index = className.lastIndexOf('$');
+ if (index >= 0) {
+ className = className.substring(0, index);
+ }
+ try {
+ Class clazz = mContext.getClassLoader().loadClass(className);
+ if (mJUnitClass.isAssignableFrom(clazz)) {
+ return true;
+ }
+ } catch (ClassNotFoundException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Returns the number of tests that will be run if you try to do this.
+ */
+ public static int countTests(Context c, String className) {
+ try {
+ Class clazz = c.getClassLoader().loadClass(className);
+ Method method = getChildrenMethod(clazz);
+ if (method != null) {
+
+ String[] children = getChildren(method);
+ int rv = 0;
+ for (String child : children) {
+ rv += countTests(c, child);
+ }
+ return rv;
+ } else if (mRunnableClass.isAssignableFrom(clazz)) {
+ return 1;
+ } else if (mJUnitClass.isAssignableFrom(clazz)) {
+ return countJunitTests(clazz);
+ }
+ } catch (ClassNotFoundException e) {
+ return 1; // this gets the count right, because either this test
+ // is missing, and it will fail when run or it is a single Junit test to be run.
+ }
+ return 0;
+ }
+
+ /**
+ * Returns a title to display given the className of a test.
+ * <p/>
+ * <p>Currently this function just returns the portion of the
+ * class name after the last '.'
+ */
+ public static String getTitle(String className) {
+ int indexDot = className.lastIndexOf('.');
+ int indexDollar = className.lastIndexOf('$');
+ int index = indexDot > indexDollar ? indexDot : indexDollar;
+ if (index >= 0) {
+ className = className.substring(index + 1);
+ }
+ return className;
+ }
+}