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.ddmlib.testrunner.TestIdentifier; 20 import com.android.tradefed.log.LogUtil.CLog; 21 import com.android.tradefed.testtype.IRemoteTest; 22 import com.android.tradefed.testtype.InstrumentationTest; 23 import com.android.tradefed.util.StreamUtil; 24 25 import java.io.BufferedInputStream; 26 import java.io.File; 27 import java.io.FileInputStream; 28 import java.io.FileNotFoundException; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.security.DigestInputStream; 32 import java.security.MessageDigest; 33 import java.security.NoSuchAlgorithmException; 34 import java.util.Collection; 35 import java.util.LinkedHashSet; 36 37 /** 38 * Container for CTS test info. 39 * <p/> 40 * Knows how to translate this info into a runnable {@link IRemoteTest}. 41 */ 42 class TestPackageDef implements ITestPackageDef { 43 44 public static final String HOST_SIDE_ONLY_TEST = "hostSideOnly"; 45 public static final String NATIVE_TEST = "native"; 46 public static final String VM_HOST_TEST = "vmHostTest"; 47 public static final String ACCESSIBILITY_TEST = 48 "com.android.cts.tradefed.testtype.AccessibilityTestRunner"; 49 public static final String ACCESSIBILITYSERVICE_TEST = 50 "com.android.cts.tradefed.testtype.AccessibilityServiceTestRunner"; 51 52 private static final String SIGNATURE_TEST_METHOD = "testSignature"; 53 private static final String SIGNATURE_TEST_CLASS = "android.tests.sigtest.SimpleSignatureTest"; 54 55 private String mUri = null; 56 private String mAppNameSpace = null; 57 private String mName = null; 58 private String mRunner = null; 59 private boolean mIsVMHostTest = false; 60 private String mTestType = null; 61 private String mJarPath = null; 62 private boolean mIsSignatureTest = false; 63 private String mTestPackageName = null; 64 private String mDigest = null; 65 66 // use a LinkedHashSet for predictable iteration insertion-order, and fast 67 // lookups 68 private Collection<TestIdentifier> mTests = new LinkedHashSet<TestIdentifier>(); 69 // also maintain an index of known test classes 70 private Collection<String> mTestClasses = new LinkedHashSet<String>(); 71 72 // dynamic options, not parsed from package xml 73 private String mClassName; 74 private String mMethodName; 75 private TestFilter mExcludedTestFilter = new TestFilter(); 76 private String mTargetBinaryName; 77 private String mTargetNameSpace; 78 setUri(String uri)79 void setUri(String uri) { 80 mUri = uri; 81 } 82 83 /** 84 * {@inheritDoc} 85 */ 86 @Override getUri()87 public String getUri() { 88 return mUri; 89 } 90 setAppNameSpace(String appNameSpace)91 void setAppNameSpace(String appNameSpace) { 92 mAppNameSpace = appNameSpace; 93 } 94 getAppNameSpace()95 String getAppNameSpace() { 96 return mAppNameSpace; 97 } 98 setName(String name)99 void setName(String name) { 100 mName = name; 101 } 102 103 /** 104 * {@inheritDoc} 105 */ 106 @Override getName()107 public String getName() { 108 return mName; 109 } 110 setRunner(String runnerName)111 void setRunner(String runnerName) { 112 mRunner = runnerName; 113 } 114 getRunner()115 String getRunner() { 116 return mRunner; 117 } 118 setTestType(String testType)119 void setTestType(String testType) { 120 mTestType = testType; 121 } 122 getTestType()123 String getTestType() { 124 return mTestType; 125 } 126 setJarPath(String jarPath)127 void setJarPath(String jarPath) { 128 mJarPath = jarPath; 129 } 130 getJarPath()131 String getJarPath() { 132 return mJarPath; 133 } 134 setIsSignatureCheck(boolean isSignatureCheckTest)135 void setIsSignatureCheck(boolean isSignatureCheckTest) { 136 mIsSignatureTest = isSignatureCheckTest; 137 } 138 isSignatureCheck()139 boolean isSignatureCheck() { 140 return mIsSignatureTest; 141 } 142 setTestPackageName(String testPackageName)143 void setTestPackageName(String testPackageName) { 144 mTestPackageName = testPackageName; 145 } 146 setTargetBinaryName(String targetBinaryName)147 void setTargetBinaryName(String targetBinaryName) { 148 mTargetBinaryName = targetBinaryName; 149 } 150 setTargetNameSpace(String targetNameSpace)151 void setTargetNameSpace(String targetNameSpace) { 152 mTargetNameSpace = targetNameSpace; 153 } 154 155 @Override getTargetApkName()156 public String getTargetApkName() { 157 if (mTargetBinaryName != null && !mTargetBinaryName.isEmpty()) { 158 return String.format("%s.apk", mTargetBinaryName); 159 } 160 return null; 161 } 162 163 @Override getTargetPackageName()164 public String getTargetPackageName() { 165 if (mTargetNameSpace != null && mTargetNameSpace.isEmpty()) { 166 return null; 167 } 168 return mTargetNameSpace; 169 } 170 171 /** 172 * {@inheritDoc} 173 */ 174 @Override setExcludedTestFilter(TestFilter excludeFilter)175 public void setExcludedTestFilter(TestFilter excludeFilter) { 176 mExcludedTestFilter = excludeFilter; 177 } 178 179 /** 180 * {@inheritDoc} 181 */ 182 @Override setClassName(String className, String methodName)183 public void setClassName(String className, String methodName) { 184 mClassName = className; 185 mMethodName = methodName; 186 } 187 188 /** 189 * {@inheritDoc} 190 */ 191 @Override createTest(File testCaseDir)192 public IRemoteTest createTest(File testCaseDir) { 193 mExcludedTestFilter.setTestInclusion(mClassName, mMethodName); 194 mTests = filterTests(); 195 196 if (HOST_SIDE_ONLY_TEST.equals(mTestType)) { 197 CLog.d("Creating host test for %s", mName); 198 JarHostTest hostTest = new JarHostTest(); 199 hostTest.setRunName(getUri()); 200 hostTest.setJarFileName(mJarPath); 201 hostTest.setTests(mTests); 202 mDigest = generateDigest(testCaseDir, mJarPath); 203 return hostTest; 204 } else if (VM_HOST_TEST.equals(mTestType)) { 205 CLog.d("Creating vm host test for %s", mName); 206 VMHostTest vmHostTest = new VMHostTest(); 207 vmHostTest.setRunName(getUri()); 208 vmHostTest.setJarFileName(mJarPath); 209 vmHostTest.setTests(mTests); 210 mDigest = generateDigest(testCaseDir, mJarPath); 211 return vmHostTest; 212 } else if (NATIVE_TEST.equals(mTestType)) { 213 return new GeeTest(mUri, mName); 214 } else if (ACCESSIBILITY_TEST.equals(mTestType)) { 215 AccessibilityTestRunner test = new AccessibilityTestRunner(); 216 return setInstrumentationTest(test, testCaseDir); 217 } else if (ACCESSIBILITYSERVICE_TEST.equals(mTestType)) { 218 AccessibilityServiceTestRunner test = new AccessibilityServiceTestRunner(); 219 return setInstrumentationTest(test, testCaseDir); 220 } else if (mIsSignatureTest) { 221 // TODO: hardcode the runner/class/method for now, since current package xml points to 222 // specialized instrumentation. Eventually this special case for signatureTest can be 223 // removed, and it can be treated just like a normal InstrumentationTest 224 CLog.d("Creating signature test %s", mName); 225 InstrumentationApkTest instrTest = new InstrumentationApkTest(); 226 instrTest.setPackageName(mAppNameSpace); 227 instrTest.setRunnerName("android.test.InstrumentationTestRunner"); 228 instrTest.setClassName(SIGNATURE_TEST_CLASS); 229 instrTest.setMethodName(SIGNATURE_TEST_METHOD); 230 // set expected tests to the single signature test 231 TestIdentifier t = new TestIdentifier(SIGNATURE_TEST_CLASS, SIGNATURE_TEST_METHOD); 232 mTests.clear(); 233 mTests.add(t); 234 // mName means 'apk file name' for instrumentation tests 235 instrTest.addInstallApk(String.format("%s.apk", mName), mAppNameSpace); 236 mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName)); 237 return instrTest; 238 } else { 239 CLog.d("Creating instrumentation test for %s", mName); 240 InstrumentationApkTest instrTest = new InstrumentationApkTest(); 241 return setInstrumentationTest(instrTest, testCaseDir); 242 } 243 } 244 245 /** 246 * Populates given {@link InstrumentationApkTest} with data from the package xml. 247 * 248 * @param testCaseDir 249 * @param instrTest 250 * @return the populated {@link InstrumentationTest} or <code>null</code> 251 */ setInstrumentationTest(InstrumentationApkTest instrTest, File testCaseDir)252 private InstrumentationTest setInstrumentationTest(InstrumentationApkTest instrTest, 253 File testCaseDir) { 254 instrTest.setRunName(getUri()); 255 instrTest.setPackageName(mAppNameSpace); 256 instrTest.setRunnerName(mRunner); 257 instrTest.setTestPackageName(mTestPackageName); 258 instrTest.setClassName(mClassName); 259 instrTest.setMethodName(mMethodName); 260 instrTest.setTestsToRun(mTests, 261 !mExcludedTestFilter.hasExclusion() 262 /* only force batch mode if no tests are excluded */); 263 // mName means 'apk file name' for instrumentation tests 264 instrTest.addInstallApk(String.format("%s.apk", mName), mAppNameSpace); 265 mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName)); 266 if (mTests.size() > 1000) { 267 // TODO: hack, large test suites can take longer to collect tests, increase timeout 268 instrTest.setCollectsTestsShellTimeout(10 * 60 * 1000); 269 } 270 return instrTest; 271 } 272 273 /** 274 * Filter the tests to run based on list of excluded tests, class and method name. 275 * 276 * @return the filtered collection of tests 277 */ filterTests()278 private Collection<TestIdentifier> filterTests() { 279 mExcludedTestFilter.setTestInclusion(mClassName, mMethodName); 280 return mExcludedTestFilter.filter(mTests); 281 } 282 283 /** 284 * {@inheritDoc} 285 */ 286 @Override isKnownTest(TestIdentifier testDef)287 public boolean isKnownTest(TestIdentifier testDef) { 288 return mTests.contains(testDef); 289 } 290 291 /** 292 * {@inheritDoc} 293 */ 294 @Override isKnownTestClass(String className)295 public boolean isKnownTestClass(String className) { 296 return mTestClasses.contains(className); 297 } 298 299 /** 300 * Add a {@link TestIdentifier} to the list of tests in this package. 301 * 302 * @param testDef 303 */ addTest(TestIdentifier testDef)304 void addTest(TestIdentifier testDef) { 305 mTests.add(testDef); 306 mTestClasses.add(testDef.getClassName()); 307 } 308 309 /** 310 * Get the collection of tests in this test package. 311 */ 312 @Override getTests()313 public Collection<TestIdentifier> getTests() { 314 return mTests; 315 } 316 317 /** 318 * {@inheritDoc} 319 */ 320 @Override getDigest()321 public String getDigest() { 322 return mDigest; 323 } 324 325 /** 326 * Generate a sha1sum digest for a file. 327 * <p/> 328 * Exposed for unit testing. 329 * 330 * @param fileDir the directory of the file 331 * @param fileName the name of the file 332 * @return a hex {@link String} of the digest 333 */ generateDigest(File fileDir, String fileName)334 String generateDigest(File fileDir, String fileName) { 335 final String algorithm = "SHA-1"; 336 InputStream fileStream = null; 337 DigestInputStream d = null; 338 try { 339 fileStream = getFileStream(fileDir, fileName); 340 MessageDigest md = MessageDigest.getInstance(algorithm); 341 d = new DigestInputStream(fileStream, md); 342 byte[] buffer = new byte[8196]; 343 while (d.read(buffer) != -1) { 344 } 345 return toHexString(md.digest()); 346 } catch (NoSuchAlgorithmException e) { 347 return algorithm + " not found"; 348 } catch (IOException e) { 349 CLog.e(e); 350 } finally { 351 StreamUtil.closeStream(d); 352 StreamUtil.closeStream(fileStream); 353 } 354 return "failed to generate digest"; 355 } 356 357 /** 358 * Retrieve an input stream for given file 359 * <p/> 360 * Exposed so unit tests can mock. 361 */ getFileStream(File fileDir, String fileName)362 InputStream getFileStream(File fileDir, String fileName) throws FileNotFoundException { 363 InputStream fileStream; 364 fileStream = new BufferedInputStream(new FileInputStream(new File(fileDir, fileName))); 365 return fileStream; 366 } 367 368 /** 369 * Convert the given byte array into a lowercase hex string. 370 * 371 * @param arr The array to convert. 372 * @return The hex encoded string. 373 */ toHexString(byte[] arr)374 private String toHexString(byte[] arr) { 375 StringBuffer buf = new StringBuffer(arr.length * 2); 376 for (byte b : arr) { 377 buf.append(String.format("%02x", b & 0xFF)); 378 } 379 return buf.toString(); 380 } 381 } 382