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 17 package com.android.cts.tradefed.testtype; 18 19 import com.android.cts.util.AbiUtils; 20 import com.android.ddmlib.Log.LogLevel; 21 import com.android.ddmlib.testrunner.TestIdentifier; 22 import com.android.tradefed.log.LogUtil.CLog; 23 import com.android.tradefed.testtype.IAbi; 24 import com.android.tradefed.testtype.IRemoteTest; 25 import com.android.tradefed.testtype.InstrumentationTest; 26 import com.android.tradefed.util.StreamUtil; 27 28 import java.io.BufferedInputStream; 29 import java.io.File; 30 import java.io.FileInputStream; 31 import java.io.FileNotFoundException; 32 import java.io.IOException; 33 import java.io.InputStream; 34 import java.security.DigestInputStream; 35 import java.security.MessageDigest; 36 import java.security.NoSuchAlgorithmException; 37 import java.util.Collection; 38 import java.util.LinkedHashSet; 39 40 /** 41 * Container for CTS test info. 42 * <p/> 43 * Knows how to translate this info into a runnable {@link IRemoteTest}. 44 */ 45 class TestPackageDef implements ITestPackageDef { 46 47 public static final String HOST_SIDE_ONLY_TEST = "hostSideOnly"; 48 public static final String NATIVE_TEST = "native"; 49 public static final String WRAPPED_NATIVE_TEST = "wrappednative"; 50 public static final String VM_HOST_TEST = "vmHostTest"; 51 public static final String DEQP_TEST = "deqpTest"; 52 public static final String ACCESSIBILITY_TEST = 53 "com.android.cts.tradefed.testtype.AccessibilityTestRunner"; 54 public static final String ACCESSIBILITY_SERVICE_TEST = 55 "com.android.cts.tradefed.testtype.AccessibilityServiceTestRunner"; 56 public static final String PRINT_TEST = 57 "com.android.cts.tradefed.testtype.PrintTestRunner"; 58 public static final String DISPLAY_TEST = 59 "com.android.cts.tradefed.testtype.DisplayTestRunner"; 60 public static final String UIAUTOMATOR_TEST = "uiAutomator"; 61 public static final String JUNIT_DEVICE_TEST = "jUnitDeviceTest"; 62 63 private String mAppPackageName = null; 64 private String mAppNameSpace = null; 65 private String mName = null; 66 private String mRunner = null; 67 private String mTestType = null; 68 private String mJarPath = null; 69 private String mRunTimeArgs = null; 70 private String mTestPackageName = null; 71 private String mDigest = null; 72 private IAbi mAbi = null; 73 74 // use a LinkedHashSet for predictable iteration insertion-order, and fast 75 // lookups 76 private Collection<TestIdentifier> mTests = new LinkedHashSet<TestIdentifier>(); 77 // also maintain an index of known test classes 78 private Collection<String> mTestClasses = new LinkedHashSet<String>(); 79 80 // dynamic options, not parsed from package xml 81 private String mClassName; 82 private String mMethodName; 83 private TestFilter mTestFilter = new TestFilter(); 84 private String mTargetBinaryName; 85 private String mTargetNameSpace; 86 // only timeout per package is supported. To change this to method granularity, 87 // test invocation should be done in method level. 88 // So for now, only max timeout for the package is used. 89 private int mTimeoutInMins = -1; 90 91 @Override 92 public IAbi getAbi() { 93 return mAbi; 94 } 95 96 /** 97 * @param abi the ABI to run this package on 98 */ 99 public void setAbi(IAbi abi) { 100 mAbi = abi; 101 } 102 103 /** 104 * @return unique id representing this test package for this ABI. 105 */ 106 @Override 107 public String getId() { 108 return AbiUtils.createId(getAbi().getName(), getAppPackageName()); 109 } 110 111 void setAppPackageName(String appPackageName) { 112 mAppPackageName = appPackageName; 113 } 114 115 String getAppPackageName() { 116 return mAppPackageName; 117 } 118 119 void setRunTimeArgs(String runTimeArgs) { 120 mRunTimeArgs = runTimeArgs; 121 } 122 123 void setAppNameSpace(String appNameSpace) { 124 mAppNameSpace = appNameSpace; 125 } 126 127 String getAppNameSpace() { 128 return mAppNameSpace; 129 } 130 131 void setName(String name) { 132 mName = name; 133 } 134 135 /** 136 * {@inheritDoc} 137 */ 138 @Override 139 public String getName() { 140 return mName; 141 } 142 143 void setRunner(String runnerName) { 144 mRunner = runnerName; 145 } 146 147 String getRunner() { 148 return mRunner; 149 } 150 151 void setTestType(String testType) { 152 mTestType = testType; 153 } 154 155 String getTestType() { 156 return mTestType; 157 } 158 159 void setJarPath(String jarPath) { 160 mJarPath = jarPath; 161 } 162 163 String getJarPath() { 164 return mJarPath; 165 } 166 167 void setTestPackageName(String testPackageName) { 168 mTestPackageName = testPackageName; 169 } 170 171 void setTargetBinaryName(String targetBinaryName) { 172 mTargetBinaryName = targetBinaryName; 173 } 174 175 void setTargetNameSpace(String targetNameSpace) { 176 mTargetNameSpace = targetNameSpace; 177 } 178 179 @Override 180 public String getTargetApkName() { 181 if (mTargetBinaryName != null && !mTargetBinaryName.isEmpty()) { 182 return String.format("%s.apk", mTargetBinaryName); 183 } 184 return null; 185 } 186 187 @Override 188 public String getTargetPackageName() { 189 if (mTargetNameSpace != null && mTargetNameSpace.isEmpty()) { 190 return null; 191 } 192 return mTargetNameSpace; 193 } 194 195 /** 196 * {@inheritDoc} 197 */ 198 @Override 199 public void setTestFilter(TestFilter testFilter) { 200 mTestFilter = testFilter; 201 } 202 203 /** 204 * {@inheritDoc} 205 */ 206 @Override 207 public void setClassName(String className, String methodName) { 208 mClassName = className; 209 mMethodName = methodName; 210 } 211 212 /** 213 * {@inheritDoc} 214 */ 215 @Override 216 public IRemoteTest createTest(File testCaseDir) { 217 mTestFilter.setTestInclusion(mClassName, mMethodName); 218 mTests = filterTests(); 219 220 if (HOST_SIDE_ONLY_TEST.equals(mTestType)) { 221 CLog.d("Creating host test for %s", mName); 222 JarHostTest hostTest = new JarHostTest(); 223 if (mTimeoutInMins >= 0) { 224 CLog.d("Setting new timeout to " + mTimeoutInMins + " mins"); 225 hostTest.setTimeout(mTimeoutInMins * 60 * 1000); 226 } 227 hostTest.setRunName(mAppPackageName); 228 hostTest.setJarFileName(mJarPath); 229 hostTest.setTests(mTests); 230 hostTest.setAbi(mAbi); 231 mDigest = generateDigest(testCaseDir, mJarPath); 232 return hostTest; 233 } else if (VM_HOST_TEST.equals(mTestType)) { 234 CLog.d("Creating vm host test for %s", mName); 235 VMHostTest vmHostTest = new VMHostTest(); 236 vmHostTest.setRunName(mAppPackageName); 237 vmHostTest.setJarFileName(mJarPath); 238 vmHostTest.setTests(mTests); 239 vmHostTest.setAbi(mAbi); 240 mDigest = generateDigest(testCaseDir, mJarPath); 241 return vmHostTest; 242 } else if (DEQP_TEST.equals(mTestType)) { 243 DeqpTestRunner deqpTest = new DeqpTestRunner(mAppPackageName, mName, mTests); 244 deqpTest.setAbi(mAbi); 245 return deqpTest; 246 } else if (NATIVE_TEST.equals(mTestType)) { 247 GeeTest geeTest = new GeeTest(mAppPackageName, mName); 248 geeTest.setAbi(mAbi); 249 return geeTest; 250 } else if (WRAPPED_NATIVE_TEST.equals(mTestType)) { 251 CLog.d("Creating new wrapped native test for %s", mName); 252 WrappedGTest wrappedGeeTest = new WrappedGTest(mAppNameSpace, mAppPackageName, mName, mRunner); 253 wrappedGeeTest.setAbi(mAbi); 254 return wrappedGeeTest; 255 } else if (ACCESSIBILITY_TEST.equals(mTestType)) { 256 AccessibilityTestRunner test = new AccessibilityTestRunner(); 257 return setInstrumentationTest(test, testCaseDir); 258 } else if (PRINT_TEST.equals(mTestType)) { 259 PrintTestRunner test = new PrintTestRunner(); 260 return setPrintTest(test, testCaseDir); 261 } else if (ACCESSIBILITY_SERVICE_TEST.equals(mTestType)) { 262 @SuppressWarnings("deprecation") 263 AccessibilityServiceTestRunner test = new AccessibilityServiceTestRunner(); 264 return setInstrumentationTest(test, testCaseDir); 265 } else if (DISPLAY_TEST.equals(mTestType)) { 266 DisplayTestRunner test = new DisplayTestRunner(); 267 return setInstrumentationTest(test, testCaseDir); 268 } else if (UIAUTOMATOR_TEST.equals(mTestType)) { 269 UiAutomatorJarTest uiautomatorTest = new UiAutomatorJarTest(); 270 return setUiAutomatorTest(uiautomatorTest); 271 } else if (JUNIT_DEVICE_TEST.equals(mTestType)){ 272 CLog.d("Creating JUnit device test %s", mName); 273 JUnitDeviceTest jUnitDeviceTest = new JUnitDeviceTest(); 274 jUnitDeviceTest.setRunName(mAppPackageName); 275 jUnitDeviceTest.addTestJarFileName(mJarPath); 276 jUnitDeviceTest.addRunTimeArgs(mRunTimeArgs); 277 jUnitDeviceTest.setTests(mTests); 278 jUnitDeviceTest.setAbi(mAbi); 279 mDigest = generateDigest(testCaseDir, mJarPath); 280 return jUnitDeviceTest; 281 } else { 282 CLog.d("Creating instrumentation test for %s", mName); 283 CtsInstrumentationApkTest instrTest = new CtsInstrumentationApkTest(); 284 if (mTimeoutInMins >= 0) { 285 // as timeout cannot be set for each test, 286 // increase the time-out of the whole package 287 CLog.d("Setting new timeout to " + mTimeoutInMins + " mins"); 288 instrTest.setTestTimeout(mTimeoutInMins * 60 * 1000); 289 } 290 return setInstrumentationTest(instrTest, testCaseDir); 291 } 292 } 293 294 private PrintTestRunner setPrintTest(PrintTestRunner printTest, 295 File testCaseDir) { 296 printTest.setRunName(mAppPackageName); 297 printTest.setPackageName(mAppNameSpace); 298 printTest.setRunnerName(mRunner); 299 printTest.setTestPackageName(mTestPackageName); 300 printTest.setClassName(mClassName); 301 printTest.setMethodName(mMethodName); 302 printTest.setAbi(mAbi); 303 mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName)); 304 return printTest; 305 } 306 307 /** 308 * Populates given {@link CtsInstrumentationApkTest} with data from the package xml. 309 * 310 * @param testCaseDir 311 * @param instrTest 312 * @return the populated {@link InstrumentationTest} or <code>null</code> 313 */ 314 private InstrumentationTest setInstrumentationTest(CtsInstrumentationApkTest instrTest, 315 File testCaseDir) { 316 instrTest.setRunName(mAppPackageName); 317 instrTest.setPackageName(mAppNameSpace); 318 instrTest.setRunnerName(mRunner); 319 instrTest.setTestPackageName(mTestPackageName); 320 instrTest.setClassName(mClassName); 321 instrTest.setMethodName(mMethodName); 322 instrTest.setAbi(mAbi); 323 instrTest.setTestsToRun(mTests, false 324 /* force batch mode off to always run using testFile */); 325 instrTest.setReRunUsingTestFile(true); 326 // mName means 'apk file name' for instrumentation tests 327 instrTest.addInstallApk(String.format("%s.apk", mName), mAppNameSpace); 328 mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName)); 329 if (mTests.size() > 1000) { 330 // TODO: hack, large test suites can take longer to collect tests, increase timeout 331 instrTest.setCollectsTestsShellTimeout(10 * 60 * 1000); 332 } 333 return instrTest; 334 } 335 336 /** 337 * Populates given {@link UiAutomatorJarTest} with data from the package xml. 338 * 339 * @param uiautomatorTest 340 * @return the populated {@link UiAutomatorJarTest} or <code>null</code> 341 */ 342 @SuppressWarnings("deprecation") 343 private IRemoteTest setUiAutomatorTest(UiAutomatorJarTest uiautomatorTest) { 344 uiautomatorTest.setInstallArtifacts(getJarPath()); 345 if (mClassName != null) { 346 if (mMethodName != null) { 347 CLog.logAndDisplay(LogLevel.WARN, "ui automator tests don't currently support" + 348 "running individual methods"); 349 } 350 uiautomatorTest.addClassName(mClassName); 351 } else { 352 uiautomatorTest.addClassNames(mTestClasses); 353 } 354 uiautomatorTest.setRunName(mAppPackageName); 355 uiautomatorTest.setCaptureLogs(false); 356 return uiautomatorTest; 357 } 358 359 /** 360 * Filter the tests to run based on list of included/excluded tests, class and method name. 361 * 362 * @return the filtered collection of tests 363 */ 364 private Collection<TestIdentifier> filterTests() { 365 mTestFilter.setTestInclusion(mClassName, mMethodName); 366 return mTestFilter.filter(mTests); 367 } 368 369 boolean isKnownTestClass(String className) { 370 return mTestClasses.contains(className); 371 } 372 373 /** 374 * Add a {@link TestIdentifier} to the list of tests in this package. 375 * 376 * @param testDef 377 * @param timeout in mins 378 */ 379 void addTest(TestIdentifier testDef, int timeout) { 380 mTests.add(testDef); 381 mTestClasses.add(testDef.getClassName()); 382 // 0 means no timeout, so keep 0 if already is. 383 if ((timeout > mTimeoutInMins) && (mTimeoutInMins != 0)) { 384 mTimeoutInMins = timeout; 385 } 386 } 387 388 /** 389 * {@inheritDoc} 390 */ 391 @Override 392 public Collection<TestIdentifier> getTests() { 393 return mTests; 394 } 395 396 /** 397 * {@inheritDoc} 398 */ 399 @Override 400 public String getDigest() { 401 return mDigest; 402 } 403 404 /** 405 * Generate a sha1sum digest for a file. 406 * <p/> 407 * Exposed for unit testing. 408 * 409 * @param fileDir the directory of the file 410 * @param fileName the name of the file 411 * @return a hex {@link String} of the digest 412 */ 413 String generateDigest(File fileDir, String fileName) { 414 final String algorithm = "SHA-1"; 415 InputStream fileStream = null; 416 DigestInputStream d = null; 417 try { 418 fileStream = getFileStream(fileDir, fileName); 419 MessageDigest md = MessageDigest.getInstance(algorithm); 420 d = new DigestInputStream(fileStream, md); 421 byte[] buffer = new byte[8196]; 422 while (d.read(buffer) != -1) { 423 } 424 return toHexString(md.digest()); 425 } catch (NoSuchAlgorithmException e) { 426 return algorithm + " not found"; 427 } catch (IOException e) { 428 CLog.e(e); 429 } finally { 430 StreamUtil.closeStream(d); 431 StreamUtil.closeStream(fileStream); 432 } 433 return "failed to generate digest"; 434 } 435 436 /** 437 * Retrieve an input stream for given file 438 * <p/> 439 * Exposed so unit tests can mock. 440 */ 441 InputStream getFileStream(File fileDir, String fileName) throws FileNotFoundException { 442 InputStream fileStream; 443 fileStream = new BufferedInputStream(new FileInputStream(new File(fileDir, fileName))); 444 return fileStream; 445 } 446 447 /** 448 * Convert the given byte array into a lowercase hex string. 449 * 450 * @param arr The array to convert. 451 * @return The hex encoded string. 452 */ 453 private String toHexString(byte[] arr) { 454 StringBuilder buf = new StringBuilder(arr.length * 2); 455 for (byte b : arr) { 456 buf.append(String.format("%02x", b & 0xFF)); 457 } 458 return buf.toString(); 459 } 460 461 @Override 462 public int compareTo(ITestPackageDef testPackageDef) { 463 return getId().compareTo(testPackageDef.getId()); 464 } 465 } 466