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