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;
24import junit.framework.Test;
25import junit.framework.TestCase;
26import junit.framework.TestListener;
27import junit.framework.TestResult;
28import junit.framework.TestSuite;
29import junit.runner.BaseTestRunner;
30
31import java.lang.reflect.InvocationTargetException;
32import java.util.List;
33
34public class AndroidTestRunner extends BaseTestRunner {
35
36    private TestResult mTestResult;
37    private String mTestClassName;
38    private List<TestCase> mTestCases;
39    private Context mContext;
40    private boolean mSkipExecution = false;
41
42    private List<TestListener> mTestListeners = Lists.newArrayList();
43    private Instrumentation mInstrumentation;
44    private PerformanceResultsWriter mPerfWriter;
45
46    @SuppressWarnings("unchecked")
47    public void setTestClassName(String testClassName, String testMethodName) {
48        Class testClass = loadTestClass(testClassName);
49
50        if (shouldRunSingleTestMethod(testMethodName, testClass)) {
51            TestCase testCase = buildSingleTestMethod(testClass, testMethodName);
52            mTestCases = Lists.newArrayList(testCase);
53            mTestClassName = testClass.getSimpleName();
54        } else {
55            setTest(getTest(testClass), testClass);
56        }
57    }
58
59    public void setTest(Test test) {
60        setTest(test, test.getClass());
61    }
62
63    private void setTest(Test test, Class<? extends Test> testClass) {
64        mTestCases = (List<TestCase>) TestCaseUtil.getTests(test, true);
65        if (TestSuite.class.isAssignableFrom(testClass)) {
66            mTestClassName = TestCaseUtil.getTestName(test);
67        } else {
68            mTestClassName = testClass.getSimpleName();
69        }
70    }
71
72    public void clearTestListeners() {
73        mTestListeners.clear();
74    }
75
76    public void addTestListener(TestListener testListener) {
77        if (testListener != null) {
78            mTestListeners.add(testListener);
79        }
80    }
81
82    @SuppressWarnings("unchecked")
83    private Class<? extends Test> loadTestClass(String testClassName) {
84        try {
85            return (Class<? extends Test>) mContext.getClassLoader().loadClass(testClassName);
86        } catch (ClassNotFoundException e) {
87            runFailed("Could not find test class. Class: " + testClassName);
88        }
89        return null;
90    }
91
92    private TestCase buildSingleTestMethod(Class testClass, String testMethodName) {
93        try {
94            TestCase testCase = (TestCase) testClass.newInstance();
95            testCase.setName(testMethodName);
96            return testCase;
97        } catch (IllegalAccessException e) {
98            runFailed("Could not access test class. Class: " + testClass.getName());
99        } catch (InstantiationException e) {
100            runFailed("Could not instantiate test class. Class: " + testClass.getName());
101        }
102
103        return null;
104    }
105
106    private boolean shouldRunSingleTestMethod(String testMethodName,
107            Class<? extends Test> testClass) {
108        return testMethodName != null && TestCase.class.isAssignableFrom(testClass);
109    }
110
111    private Test getTest(Class clazz) {
112        if (TestSuiteProvider.class.isAssignableFrom(clazz)) {
113            try {
114                TestSuiteProvider testSuiteProvider =
115                        (TestSuiteProvider) clazz.getConstructor().newInstance();
116                return testSuiteProvider.getTestSuite();
117            } catch (InstantiationException e) {
118                runFailed("Could not instantiate test suite provider. Class: " + clazz.getName());
119            } catch (IllegalAccessException e) {
120                runFailed("Illegal access of test suite provider. Class: " + clazz.getName());
121            } catch (InvocationTargetException e) {
122                runFailed("Invocation exception test suite provider. Class: " + clazz.getName());
123            } catch (NoSuchMethodException e) {
124                runFailed("No such method on test suite provider. Class: " + clazz.getName());
125            }
126        }
127        return getTest(clazz.getName());
128    }
129
130    protected TestResult createTestResult() {
131        if (mSkipExecution) {
132            return new NoExecTestResult();
133        }
134        return new TestResult();
135    }
136
137    void setSkipExecution(boolean skip) {
138        mSkipExecution = skip;
139    }
140
141    public List<TestCase> getTestCases() {
142        return mTestCases;
143    }
144
145    public String getTestClassName() {
146        return mTestClassName;
147    }
148
149    public TestResult getTestResult() {
150        return mTestResult;
151    }
152
153    public void runTest() {
154        runTest(createTestResult());
155    }
156
157    public void runTest(TestResult testResult) {
158        mTestResult = testResult;
159
160        for (TestListener testListener : mTestListeners) {
161            mTestResult.addListener(testListener);
162        }
163
164        Context testContext = mInstrumentation == null ? mContext : mInstrumentation.getContext();
165        for (TestCase testCase : mTestCases) {
166            setContextIfAndroidTestCase(testCase, mContext, testContext);
167            setInstrumentationIfInstrumentationTestCase(testCase, mInstrumentation);
168            setPerformanceWriterIfPerformanceCollectorTestCase(testCase, mPerfWriter);
169            testCase.run(mTestResult);
170        }
171    }
172
173    private void setContextIfAndroidTestCase(Test test, Context context, Context testContext) {
174        if (AndroidTestCase.class.isAssignableFrom(test.getClass())) {
175            ((AndroidTestCase) test).setContext(context);
176            ((AndroidTestCase) test).setTestContext(testContext);
177        }
178    }
179
180    public void setContext(Context context) {
181        mContext = context;
182    }
183
184    private void setInstrumentationIfInstrumentationTestCase(
185            Test test, Instrumentation instrumentation) {
186        if (InstrumentationTestCase.class.isAssignableFrom(test.getClass())) {
187            ((InstrumentationTestCase) test).injectInstrumentation(instrumentation);
188        }
189    }
190
191    private void setPerformanceWriterIfPerformanceCollectorTestCase(
192            Test test, PerformanceResultsWriter writer) {
193        if (PerformanceCollectorTestCase.class.isAssignableFrom(test.getClass())) {
194            ((PerformanceCollectorTestCase) test).setPerformanceResultsWriter(writer);
195        }
196    }
197
198    public void setInstrumentation(Instrumentation instrumentation) {
199        mInstrumentation = instrumentation;
200    }
201
202    /**
203     * @deprecated Incorrect spelling,
204     * use {@link #setInstrumentation(android.app.Instrumentation)} instead.
205     */
206    @Deprecated
207    public void setInstrumentaiton(Instrumentation instrumentation) {
208        setInstrumentation(instrumentation);
209    }
210
211    /**
212     * {@hide} Pending approval for public API.
213     */
214    public void setPerformanceResultsWriter(PerformanceResultsWriter writer) {
215        mPerfWriter = writer;
216    }
217
218    @Override
219    protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
220        return mContext.getClassLoader().loadClass(suiteClassName);
221    }
222
223    public void testStarted(String testName) {
224    }
225
226    public void testEnded(String testName) {
227    }
228
229    public void testFailed(int status, Test test, Throwable t) {
230    }
231
232    protected void runFailed(String message) {
233        throw new RuntimeException(message);
234    }
235}
236