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