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