• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 package com.android.cts.tradefed.testtype;
17 
18 import com.android.cts.tradefed.build.CtsBuildHelper;
19 import com.android.ddmlib.Log;
20 import com.android.ddmlib.testrunner.TestIdentifier;
21 import com.android.tradefed.build.IBuildInfo;
22 import com.android.tradefed.device.DeviceNotAvailableException;
23 import com.android.tradefed.device.ITestDevice;
24 import com.android.tradefed.result.ITestInvocationListener;
25 import com.android.tradefed.testtype.DeviceTestResult.RuntimeDeviceNotAvailableException;
26 import com.android.tradefed.testtype.IBuildReceiver;
27 import com.android.tradefed.testtype.IDeviceTest;
28 import com.android.tradefed.testtype.IRemoteTest;
29 import com.android.tradefed.testtype.JUnitRunUtil;
30 import com.android.tradefed.util.CommandStatus;
31 import com.android.tradefed.util.IRunUtil.IRunnableResult;
32 import com.android.tradefed.util.RunUtil;
33 
34 import junit.framework.Test;
35 import junit.framework.TestCase;
36 import junit.framework.TestResult;
37 
38 import java.io.File;
39 import java.io.FileNotFoundException;
40 import java.io.IOException;
41 import java.net.MalformedURLException;
42 import java.net.URL;
43 import java.net.URLClassLoader;
44 import java.util.Collection;
45 
46 /**
47  * A {@link IRemoteTest} that can run a set of JUnit tests from a CTS jar.
48  */
49 public class JarHostTest implements IDeviceTest, IRemoteTest, IBuildReceiver, Test {
50 
51     private static final String LOG_TAG = "JarHostTest";
52 
53     private ITestDevice mDevice;
54     private String mJarFileName;
55     private Collection<TestIdentifier> mTests;
56     private long mTimeoutMs = 10 * 60 * 1000;
57     private String mRunName;
58     private CtsBuildHelper mCtsBuild = null;
59     private IBuildInfo mBuildInfo = null;
60 
61     private ClassLoader mClassLoader;
62 
63     /**
64      * {@inheritDoc}
65      */
66     @Override
setBuild(IBuildInfo buildInfo)67     public void setBuild(IBuildInfo buildInfo) {
68         mBuildInfo = buildInfo;
69         mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
70     }
71 
72     /**
73      * Set the CTS build container.
74      * <p/>
75      * Exposed so unit tests can mock the provided build.
76      *
77      * @param buildHelper
78      */
setBuildHelper(CtsBuildHelper buildHelper)79     void setBuildHelper(CtsBuildHelper buildHelper) {
80         mCtsBuild = buildHelper;
81     }
82 
83     /**
84      * Get the CTS build container.
85      *
86      * @return {@link CtsBuildHelper}
87      */
getBuildHelper()88     CtsBuildHelper getBuildHelper() {
89         return mCtsBuild;
90     }
91 
92     /**
93      * Set the jar file to load tests from.
94      *
95      * @param jarFileName the file name of the CTS host test jar to use
96      */
setJarFileName(String jarFileName)97     void setJarFileName(String jarFileName) {
98         mJarFileName = jarFileName;
99     }
100 
101     /**
102      * Gets the jar file to load tests from.
103      *
104      * @return jarFileName the file name of the CTS host test jar to use
105      */
getJarFileName()106     String getJarFileName() {
107         return mJarFileName;
108     }
109 
110     /**
111      * Sets the collection of tests to run
112      *
113      * @param tests
114      */
setTests(Collection<TestIdentifier> tests)115     void setTests(Collection<TestIdentifier> tests) {
116         mTests = tests;
117     }
118 
119     /**
120      * Gets the collection of tests to run
121      *
122      * @return Collection<{@link TestIdentifier}>
123      */
getTests()124     Collection<TestIdentifier> getTests() {
125         return mTests;
126     }
127 
128     /**
129      * Set the maximum time in ms each test should run.
130      * <p/>
131      * Tests that take longer than this amount will be failed with a {@link TestTimeoutException}
132      * as the cause.
133      *
134      * @param testTimeout
135      */
setTimeout(long testTimeoutMs)136     void setTimeout(long testTimeoutMs) {
137         mTimeoutMs = testTimeoutMs;
138     }
139 
140     /**
141      * Set the run name to report to {@link ITestInvocationListener#testRunStarted(String, int)}
142      *
143      * @param runName
144      */
setRunName(String runName)145     void setRunName(String runName) {
146         mRunName = runName;
147     }
148 
149     /**
150      * {@inheritDoc}
151      */
152     @Override
getDevice()153     public ITestDevice getDevice() {
154         return mDevice;
155     }
156 
157     /**
158      * {@inheritDoc}
159      */
160     @Override
setDevice(ITestDevice device)161     public void setDevice(ITestDevice device) {
162         mDevice = device;
163     }
164 
165     /**
166      * {@inheritDoc}
167      */
168     @SuppressWarnings("unchecked")
169     @Override
run(ITestInvocationListener listener)170     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
171         checkFields();
172         Log.i(LOG_TAG, String.format("Running %s test package from jar, contains %d tests.",
173                 mRunName, mTests.size()));
174         JUnitRunUtil.runTest(listener, this, mRunName);
175     }
176 
177     /**
178      * {@inheritDoc}
179      */
180     @Override
run(TestResult junitResult)181     public void run(TestResult junitResult) {
182         for (TestIdentifier testId : mTests) {
183             Test junitTest = loadTest(testId.getClassName(), testId.getTestName());
184             if (junitTest != null) {
185                 runTest(testId, junitTest, junitResult);
186             }
187         }
188     }
189 
190     /**
191      * Run test with timeout support.
192      */
runTest(TestIdentifier testId, final Test junitTest, final TestResult junitResult)193     private void runTest(TestIdentifier testId, final Test junitTest, final TestResult junitResult) {
194         if (junitTest instanceof IDeviceTest) {
195             ((IDeviceTest)junitTest).setDevice(getDevice());
196         } else if (junitTest instanceof com.android.hosttest.DeviceTest) {
197             // legacy check - see if test uses hosttestlib. This check should go away once
198             // all host tests are converted to use tradefed
199             com.android.hosttest.DeviceTest deviceTest = (com.android.hosttest.DeviceTest)junitTest;
200             deviceTest.setDevice(getDevice().getIDevice());
201             deviceTest.setTestAppPath(mCtsBuild.getTestCasesDir().getAbsolutePath());
202         }
203         if (junitTest instanceof IBuildReceiver) {
204             ((IBuildReceiver)junitTest).setBuild(mBuildInfo);
205         }
206         TestRunnable testRunnable = new TestRunnable(junitTest, junitResult);
207 
208         CommandStatus status = RunUtil.getDefault().runTimed(mTimeoutMs, testRunnable, true);
209         if (status.equals(CommandStatus.TIMED_OUT)) {
210             junitResult.addError(junitTest, new TestTimeoutException());
211             junitResult.endTest(junitTest);
212         }
213         if (testRunnable.getException() != null) {
214             throw testRunnable.getException();
215         }
216     }
217 
218     private static class TestRunnable implements IRunnableResult {
219 
220         private final Test mJunitTest;
221         private RuntimeDeviceNotAvailableException mException = null;
222         private TestResult mJunitResult;
223 
TestRunnable(Test junitTest, TestResult junitResult)224         TestRunnable(Test junitTest, TestResult junitResult) {
225             mJunitTest = junitTest;
226             mJunitResult = junitResult;
227         }
228 
229         /**
230          * {@inheritDoc}
231          */
232         @Override
run()233         public boolean run() throws Exception {
234             try {
235                 mJunitTest.run(mJunitResult);
236             } catch (RuntimeDeviceNotAvailableException e) {
237                 mException = e;
238             }
239             return true;
240         }
241 
getException()242         public RuntimeDeviceNotAvailableException getException() {
243             return mException;
244         }
245 
246         /**
247          * {@inheritDoc}
248          */
249         @Override
cancel()250         public void cancel() {
251         }
252 
253     }
254 
255     /**
256      * Load the test with given names from the jar.
257      *
258      * @param className
259      * @param testName
260      * @return the loaded {@link Test} or <code>null</code> if test could not be loaded.
261      */
loadTest(String className, String testName)262     private Test loadTest(String className, String testName) {
263         try {
264             Class<?> testClass = loadClass(className);
265             if (testClass == null) {
266                 return null;
267             }
268             if (TestCase.class.isAssignableFrom(testClass)) {
269                 TestCase testCase = (TestCase)testClass.newInstance();
270                 testCase.setName(testName);
271                 return testCase;
272             } else if (Test.class.isAssignableFrom(testClass)) {
273                 Test test = (Test)testClass.newInstance();
274                 return test;
275             } else {
276                 Log.e(LOG_TAG, String.format("Class '%s' from jar '%s' is not a Test",
277                         className, mJarFileName));
278             }
279         } catch (IllegalAccessException e) {
280             reportLoadError(mJarFileName, className, e);
281         } catch (InstantiationException e) {
282             reportLoadError(mJarFileName, className, e);
283         }
284         return null;
285     }
286 
loadClass(String className)287     private Class<?> loadClass(String className) {
288         try {
289             if (mClassLoader == null) {
290                 File jarFile = mCtsBuild.getTestApp(mJarFileName);
291                 URL urls[] = {jarFile.getCanonicalFile().toURI().toURL()};
292                 mClassLoader = new URLClassLoader(urls);
293             }
294             return mClassLoader.loadClass(className);
295         } catch (FileNotFoundException fnfe) {
296             reportLoadError(mJarFileName, className, fnfe);
297         } catch (MalformedURLException mue) {
298             reportLoadError(mJarFileName, className, mue);
299         } catch (IOException ioe) {
300             reportLoadError(mJarFileName, className, ioe);
301         } catch (ClassNotFoundException cnfe) {
302             reportLoadError(mJarFileName, className, cnfe);
303         }
304         return null;
305     }
306 
307     /**
308      * Loads a class from given URLs.
309      * <p/>
310      * Exposed so unit tests can mock
311      *
312      * @param className
313      * @param urls
314      * @return
315      * @throws ClassNotFoundException
316      */
loadClass(String className, URL[] urls)317     Class<?> loadClass(String className, URL[] urls) throws ClassNotFoundException {
318         URLClassLoader cl = new URLClassLoader(urls);
319         Class<?> testClass = cl.loadClass(className);
320         return testClass;
321     }
322 
reportLoadError(String jarFileName, String className, Exception e)323     private void reportLoadError(String jarFileName, String className, Exception e) {
324         Log.e(LOG_TAG, String.format("Failed to load test class '%s' from jar '%s'",
325                 className, jarFileName));
326         Log.e(LOG_TAG, e);
327     }
328 
329     /**
330      * Checks that all mandatory member fields has been set.
331      */
checkFields()332     protected void checkFields() {
333         if (mRunName == null) {
334             throw new IllegalArgumentException("run name has not been set");
335         }
336         if (mDevice == null) {
337             throw new IllegalArgumentException("Device has not been set");
338         }
339         if (mJarFileName == null) {
340             throw new IllegalArgumentException("jar file name has not been set");
341         }
342         if (mTests == null) {
343             throw new IllegalArgumentException("tests has not been set");
344         }
345         if (mCtsBuild == null) {
346             throw new IllegalArgumentException("build has not been set");
347         }
348         try {
349             mCtsBuild.getTestApp(mJarFileName);
350         } catch (FileNotFoundException e) {
351             throw new IllegalArgumentException(String.format(
352                     "Could not find jar %s in CTS build %s", mJarFileName,
353                     mCtsBuild.getRootDir().getAbsolutePath()));
354         }
355     }
356 
357     /**
358      * {@inheritDoc}
359      */
360     @Override
countTestCases()361     public int countTestCases() {
362         return mTests.size();
363     }
364 }
365