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 
17 package android.test;
18 
19 import android.app.Instrumentation;
20 import android.content.Context;
21 import android.os.PerformanceCollector.PerformanceResultsWriter;
22 
23 import com.google.android.collect.Lists;
24 
25 import junit.framework.Test;
26 import junit.framework.TestCase;
27 import junit.framework.TestListener;
28 import junit.framework.TestResult;
29 import junit.framework.TestSuite;
30 import junit.runner.BaseTestRunner;
31 
32 import java.lang.reflect.Constructor;
33 import java.lang.reflect.InvocationTargetException;
34 import java.util.List;
35 
36 public 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")
setTestClassName(String testClassName, String testMethodName)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 
setTest(Test test)61     public void setTest(Test test) {
62         setTest(test, test.getClass());
63     }
64 
setTest(Test test, Class<? extends Test> testClass)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 
clearTestListeners()74     public void clearTestListeners() {
75         mTestListeners.clear();
76     }
77 
addTestListener(TestListener testListener)78     public void addTestListener(TestListener testListener) {
79         if (testListener != null) {
80             mTestListeners.add(testListener);
81         }
82     }
83 
84     @SuppressWarnings("unchecked")
loadTestClass(String testClassName)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 
buildSingleTestMethod(Class testClass, String testMethodName)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 
newSingleTestMethod(Class testClass, String testMethodName, Constructor constructor, Object... args)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 
shouldRunSingleTestMethod(String testMethodName, Class<? extends Test> testClass)128     private boolean shouldRunSingleTestMethod(String testMethodName,
129             Class<? extends Test> testClass) {
130         return testMethodName != null && TestCase.class.isAssignableFrom(testClass);
131     }
132 
getTest(Class clazz)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 
createTestResult()152     protected TestResult createTestResult() {
153         if (mSkipExecution) {
154             return new NoExecTestResult();
155         }
156         return new TestResult();
157     }
158 
setSkipExecution(boolean skip)159     void setSkipExecution(boolean skip) {
160         mSkipExecution = skip;
161     }
162 
getTestCases()163     public List<TestCase> getTestCases() {
164         return mTestCases;
165     }
166 
getTestClassName()167     public String getTestClassName() {
168         return mTestClassName;
169     }
170 
getTestResult()171     public TestResult getTestResult() {
172         return mTestResult;
173     }
174 
runTest()175     public void runTest() {
176         runTest(createTestResult());
177     }
178 
runTest(TestResult testResult)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 
setContextIfAndroidTestCase(Test test, Context context, Context testContext)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 
setContext(Context context)202     public void setContext(Context context) {
203         mContext = context;
204     }
205 
setInstrumentationIfInstrumentationTestCase( Test test, Instrumentation instrumentation)206     private void setInstrumentationIfInstrumentationTestCase(
207             Test test, Instrumentation instrumentation) {
208         if (InstrumentationTestCase.class.isAssignableFrom(test.getClass())) {
209             ((InstrumentationTestCase) test).injectInstrumentation(instrumentation);
210         }
211     }
212 
setPerformanceWriterIfPerformanceCollectorTestCase( Test test, PerformanceResultsWriter writer)213     private void setPerformanceWriterIfPerformanceCollectorTestCase(
214             Test test, PerformanceResultsWriter writer) {
215         if (PerformanceCollectorTestCase.class.isAssignableFrom(test.getClass())) {
216             ((PerformanceCollectorTestCase) test).setPerformanceResultsWriter(writer);
217         }
218     }
219 
setInstrumentation(Instrumentation instrumentation)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
setInstrumentaiton(Instrumentation instrumentation)229     public void setInstrumentaiton(Instrumentation instrumentation) {
230         setInstrumentation(instrumentation);
231     }
232 
233     /**
234      * {@hide} Pending approval for public API.
235      */
setPerformanceResultsWriter(PerformanceResultsWriter writer)236     public void setPerformanceResultsWriter(PerformanceResultsWriter writer) {
237         mPerfWriter = writer;
238     }
239 
240     @Override
loadSuiteClass(String suiteClassName)241     protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
242         return mContext.getClassLoader().loadClass(suiteClassName);
243     }
244 
testStarted(String testName)245     public void testStarted(String testName) {
246     }
247 
testEnded(String testName)248     public void testEnded(String testName) {
249     }
250 
testFailed(int status, Test test, Throwable t)251     public void testFailed(int status, Test test, Throwable t) {
252     }
253 
runFailed(String message)254     protected void runFailed(String message) {
255         throw new RuntimeException(message);
256     }
257 }
258