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