1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.test; 18 19import android.app.Instrumentation; 20import android.content.Context; 21import android.os.PerformanceCollector.PerformanceResultsWriter; 22 23import com.google.android.collect.Lists; 24 25import junit.framework.Test; 26import junit.framework.TestCase; 27import junit.framework.TestListener; 28import junit.framework.TestResult; 29import junit.framework.TestSuite; 30import junit.runner.BaseTestRunner; 31 32import java.lang.reflect.Constructor; 33import java.lang.reflect.InvocationTargetException; 34import java.util.List; 35 36public class AndroidTestRunner extends BaseTestRunner { 37 38 private TestResult mTestResult; 39 private String mTestClassName; 40 private List<TestCase> mTestCases; 41 private Context mContext; 42 private boolean mSkipExecution = false; 43 44 private List<TestListener> mTestListeners = Lists.newArrayList(); 45 private Instrumentation mInstrumentation; 46 private PerformanceResultsWriter mPerfWriter; 47 48 @SuppressWarnings("unchecked") 49 public void setTestClassName(String testClassName, String testMethodName) { 50 Class testClass = loadTestClass(testClassName); 51 52 if (shouldRunSingleTestMethod(testMethodName, testClass)) { 53 TestCase testCase = buildSingleTestMethod(testClass, testMethodName); 54 mTestCases = Lists.newArrayList(testCase); 55 mTestClassName = testClass.getSimpleName(); 56 } else { 57 setTest(getTest(testClass), testClass); 58 } 59 } 60 61 public void setTest(Test test) { 62 setTest(test, test.getClass()); 63 } 64 65 private void setTest(Test test, Class<? extends Test> testClass) { 66 mTestCases = (List<TestCase>) TestCaseUtil.getTests(test, true); 67 if (TestSuite.class.isAssignableFrom(testClass)) { 68 mTestClassName = TestCaseUtil.getTestName(test); 69 } else { 70 mTestClassName = testClass.getSimpleName(); 71 } 72 } 73 74 public void clearTestListeners() { 75 mTestListeners.clear(); 76 } 77 78 public void addTestListener(TestListener testListener) { 79 if (testListener != null) { 80 mTestListeners.add(testListener); 81 } 82 } 83 84 @SuppressWarnings("unchecked") 85 private Class<? extends Test> loadTestClass(String testClassName) { 86 try { 87 return (Class<? extends Test>) mContext.getClassLoader().loadClass(testClassName); 88 } catch (ClassNotFoundException e) { 89 runFailed("Could not find test class. Class: " + testClassName); 90 } 91 return null; 92 } 93 94 private TestCase buildSingleTestMethod(Class testClass, String testMethodName) { 95 try { 96 Constructor c = testClass.getConstructor(); 97 return newSingleTestMethod(testClass, testMethodName, c); 98 } catch (NoSuchMethodException e) { 99 } 100 101 try { 102 Constructor c = testClass.getConstructor(String.class); 103 return newSingleTestMethod(testClass, testMethodName, c, testMethodName); 104 } catch (NoSuchMethodException e) { 105 } 106 107 return null; 108 } 109 110 private TestCase newSingleTestMethod(Class testClass, String testMethodName, 111 Constructor constructor, Object... args) { 112 try { 113 TestCase testCase = (TestCase) constructor.newInstance(args); 114 testCase.setName(testMethodName); 115 return testCase; 116 } catch (IllegalAccessException e) { 117 runFailed("Could not access test class. Class: " + testClass.getName()); 118 } catch (InstantiationException e) { 119 runFailed("Could not instantiate test class. Class: " + testClass.getName()); 120 } catch (IllegalArgumentException e) { 121 runFailed("Illegal argument passed to constructor. Class: " + testClass.getName()); 122 } catch (InvocationTargetException e) { 123 runFailed("Constructor thew an exception. Class: " + testClass.getName()); 124 } 125 return null; 126 } 127 128 private boolean shouldRunSingleTestMethod(String testMethodName, 129 Class<? extends Test> testClass) { 130 return testMethodName != null && TestCase.class.isAssignableFrom(testClass); 131 } 132 133 private Test getTest(Class clazz) { 134 if (TestSuiteProvider.class.isAssignableFrom(clazz)) { 135 try { 136 TestSuiteProvider testSuiteProvider = 137 (TestSuiteProvider) clazz.getConstructor().newInstance(); 138 return testSuiteProvider.getTestSuite(); 139 } catch (InstantiationException e) { 140 runFailed("Could not instantiate test suite provider. Class: " + clazz.getName()); 141 } catch (IllegalAccessException e) { 142 runFailed("Illegal access of test suite provider. Class: " + clazz.getName()); 143 } catch (InvocationTargetException e) { 144 runFailed("Invocation exception test suite provider. Class: " + clazz.getName()); 145 } catch (NoSuchMethodException e) { 146 runFailed("No such method on test suite provider. Class: " + clazz.getName()); 147 } 148 } 149 return getTest(clazz.getName()); 150 } 151 152 protected TestResult createTestResult() { 153 if (mSkipExecution) { 154 return new NoExecTestResult(); 155 } 156 return new TestResult(); 157 } 158 159 void setSkipExecution(boolean skip) { 160 mSkipExecution = skip; 161 } 162 163 public List<TestCase> getTestCases() { 164 return mTestCases; 165 } 166 167 public String getTestClassName() { 168 return mTestClassName; 169 } 170 171 public TestResult getTestResult() { 172 return mTestResult; 173 } 174 175 public void runTest() { 176 runTest(createTestResult()); 177 } 178 179 public void runTest(TestResult testResult) { 180 mTestResult = testResult; 181 182 for (TestListener testListener : mTestListeners) { 183 mTestResult.addListener(testListener); 184 } 185 186 Context testContext = mInstrumentation == null ? mContext : mInstrumentation.getContext(); 187 for (TestCase testCase : mTestCases) { 188 setContextIfAndroidTestCase(testCase, mContext, testContext); 189 setInstrumentationIfInstrumentationTestCase(testCase, mInstrumentation); 190 setPerformanceWriterIfPerformanceCollectorTestCase(testCase, mPerfWriter); 191 testCase.run(mTestResult); 192 } 193 } 194 195 private void setContextIfAndroidTestCase(Test test, Context context, Context testContext) { 196 if (AndroidTestCase.class.isAssignableFrom(test.getClass())) { 197 ((AndroidTestCase) test).setContext(context); 198 ((AndroidTestCase) test).setTestContext(testContext); 199 } 200 } 201 202 public void setContext(Context context) { 203 mContext = context; 204 } 205 206 private void setInstrumentationIfInstrumentationTestCase( 207 Test test, Instrumentation instrumentation) { 208 if (InstrumentationTestCase.class.isAssignableFrom(test.getClass())) { 209 ((InstrumentationTestCase) test).injectInstrumentation(instrumentation); 210 } 211 } 212 213 private void setPerformanceWriterIfPerformanceCollectorTestCase( 214 Test test, PerformanceResultsWriter writer) { 215 if (PerformanceCollectorTestCase.class.isAssignableFrom(test.getClass())) { 216 ((PerformanceCollectorTestCase) test).setPerformanceResultsWriter(writer); 217 } 218 } 219 220 public void setInstrumentation(Instrumentation instrumentation) { 221 mInstrumentation = instrumentation; 222 } 223 224 /** 225 * @deprecated Incorrect spelling, 226 * use {@link #setInstrumentation(android.app.Instrumentation)} instead. 227 */ 228 @Deprecated 229 public void setInstrumentaiton(Instrumentation instrumentation) { 230 setInstrumentation(instrumentation); 231 } 232 233 /** 234 * {@hide} Pending approval for public API. 235 */ 236 public void setPerformanceResultsWriter(PerformanceResultsWriter writer) { 237 mPerfWriter = writer; 238 } 239 240 @Override 241 protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException { 242 return mContext.getClassLoader().loadClass(suiteClassName); 243 } 244 245 public void testStarted(String testName) { 246 } 247 248 public void testEnded(String testName) { 249 } 250 251 public void testFailed(int status, Test test, Throwable t) { 252 } 253 254 protected void runFailed(String message) { 255 throw new RuntimeException(message); 256 } 257} 258