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.tradefed.build.CtsBuildHelper; 20 import com.android.cts.tradefed.device.DeviceInfoCollector; 21 import com.android.cts.tradefed.result.CtsTestStatus; 22 import com.android.cts.tradefed.result.PlanCreator; 23 import com.android.cts.util.AbiUtils; 24 import com.android.ddmlib.Log; 25 import com.android.ddmlib.Log.LogLevel; 26 import com.android.ddmlib.testrunner.TestIdentifier; 27 import com.android.tradefed.build.IBuildInfo; 28 import com.android.tradefed.config.ConfigurationException; 29 import com.android.tradefed.config.Option; 30 import com.android.tradefed.config.Option.Importance; 31 import com.android.tradefed.config.OptionCopier; 32 import com.android.tradefed.device.DeviceNotAvailableException; 33 import com.android.tradefed.device.ITestDevice; 34 import com.android.tradefed.device.TestDeviceOptions; 35 import com.android.tradefed.log.LogUtil.CLog; 36 import com.android.tradefed.result.ITestInvocationListener; 37 import com.android.tradefed.result.InputStreamSource; 38 import com.android.tradefed.result.LogDataType; 39 import com.android.tradefed.result.ResultForwarder; 40 import com.android.tradefed.testtype.IAbi; 41 import com.android.tradefed.testtype.IBuildReceiver; 42 import com.android.tradefed.testtype.IDeviceTest; 43 import com.android.tradefed.testtype.IRemoteTest; 44 import com.android.tradefed.testtype.IResumableTest; 45 import com.android.tradefed.testtype.IShardableTest; 46 import com.android.tradefed.util.AbiFormatter; 47 import com.android.tradefed.util.RunUtil; 48 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException; 49 50 import junit.framework.Test; 51 52 import java.io.BufferedInputStream; 53 import java.io.File; 54 import java.io.FileInputStream; 55 import java.io.FileNotFoundException; 56 import java.io.InputStream; 57 import java.util.ArrayList; 58 import java.util.Arrays; 59 import java.util.Collection; 60 import java.util.Collections; 61 import java.util.HashMap; 62 import java.util.HashSet; 63 import java.util.LinkedHashSet; 64 import java.util.LinkedList; 65 import java.util.List; 66 import java.util.Map; 67 import java.util.Set; 68 69 70 /** 71 * A {@link Test} for running CTS tests. 72 * <p/> 73 * Supports running all the tests contained in a CTS plan, or individual test packages. 74 */ 75 public class CtsTest implements IDeviceTest, IResumableTest, IShardableTest, IBuildReceiver { 76 private static final String LOG_TAG = "CtsTest"; 77 78 public static final String PLAN_OPTION = "plan"; 79 private static final String PACKAGE_OPTION = "package"; 80 private static final String CLASS_OPTION = "class"; 81 private static final String METHOD_OPTION = "method"; 82 private static final String TEST_OPTION = "test"; 83 public static final String CONTINUE_OPTION = "continue-session"; 84 public static final String RUN_KNOWN_FAILURES_OPTION = "run-known-failures"; 85 86 public static final String PACKAGE_NAME_METRIC = "packageName"; 87 public static final String PACKAGE_ABI_METRIC = "packageAbi"; 88 public static final String PACKAGE_DIGEST_METRIC = "packageDigest"; 89 90 @Option(name = PLAN_OPTION, description = "the test plan to run.", 91 importance = Importance.IF_UNSET) 92 private String mPlanName = null; 93 94 @Option(name = PACKAGE_OPTION, shortName = 'p', description = "the test packages(s) to run.", 95 importance = Importance.IF_UNSET) 96 private Collection<String> mPackageNames = new ArrayList<String>(); 97 98 @Option(name = "exclude-package", description = "the test packages(s) to exclude from the run.") 99 private Collection<String> mExcludedPackageNames = new ArrayList<String>(); 100 101 @Option(name = CLASS_OPTION, shortName = 'c', description = "run a specific test class.", 102 importance = Importance.IF_UNSET) 103 private String mClassName = null; 104 105 @Option(name = METHOD_OPTION, shortName = 'm', 106 description = "run a specific test method, from given --class.", 107 importance = Importance.IF_UNSET) 108 private String mMethodName = null; 109 110 @Option(name = TEST_OPTION, shortName = 't', description = "run a specific test", 111 importance = Importance.IF_UNSET) 112 private String mTestName = null; 113 114 @Option(name = CONTINUE_OPTION, 115 description = "continue a previous test session.", 116 importance = Importance.IF_UNSET) 117 private Integer mContinueSessionId = null; 118 119 @Option(name = "skip-device-info", shortName = 'd', description = 120 "flag to control whether to collect info from device. Providing this flag will speed up " + 121 "test execution for short test runs but will result in required data being omitted from " + 122 "the test report.") 123 private boolean mSkipDeviceInfo = false; 124 125 @Option(name = "resume", description = 126 "flag to attempt to automatically resume aborted test run on another connected device. ") 127 private boolean mResume = false; 128 129 @Option(name = "shards", description = 130 "shard the tests to run into separately runnable chunks to execute on multiple devices " + 131 "concurrently.") 132 private int mShards = 1; 133 134 @Option(name = "screenshot", description = 135 "flag for taking a screenshot of the device when test execution is complete.") 136 private boolean mScreenshot = false; 137 138 @Option(name = "bugreport", shortName = 'b', description = 139 "take a bugreport after each failed test. " + 140 "Warning: can potentially use a lot of disk space.") 141 private boolean mBugreport = false; 142 143 @Option(name = RUN_KNOWN_FAILURES_OPTION, shortName = 'k', description = 144 "run tests including known failures") 145 private boolean mIncludeKnownFailures; 146 147 @Option(name = "disable-reboot", description = 148 "Do not reboot device after running some amount of tests. Default behavior is to reboot.") 149 private boolean mDisableReboot = false; 150 151 @Option(name = "reboot-wait-time", description = 152 "Additional wait time in ms after boot complete.") 153 private int mRebootWaitTimeMSec = 2 * 60 * 1000; 154 155 @Option(name = "reboot-interval", description = 156 "Interval between each reboot in min.") 157 private int mRebootIntervalMin = 30; 158 159 @Option(name = "screenshot-on-failure", description = 160 "take a screenshot on every test failure.") 161 private boolean mScreenshotOnFailures = false; 162 163 @Option(name = "logcat-on-failure", description = 164 "take a logcat snapshot on every test failure. Unlike --bugreport, this can capture" + 165 "logs even if connection with device has been lost, as well as being much more " + 166 "performant.") 167 private boolean mLogcatOnFailures = false; 168 169 @Option(name = AbiFormatter.FORCE_ABI_STRING, 170 description = AbiFormatter.FORCE_ABI_DESCRIPTION, 171 importance = Importance.IF_UNSET) 172 private String mForceAbi = null; 173 174 @Option(name = "logcat-on-failure-size", description = 175 "The max number of logcat data in bytes to capture when --logcat-on-failure is on. " + 176 "Should be an amount that can comfortably fit in memory.") 177 private int mMaxLogcatBytes = 500 * 1024; // 500K 178 179 @Option(name = "collect-deqp-logs", description = 180 "Collect dEQP logs from the device.") 181 private boolean mCollectDeqpLogs = false; 182 183 @Option(name = "min-pre-reboot-package-count", description = 184 "The minimum number of packages to require a pre test reboot") 185 186 private int mMinPreRebootPackageCount = 2; 187 private final int mShardAssignment; 188 private final int mTotalShards; 189 private ITestDevice mDevice = null; 190 private CtsBuildHelper mCtsBuild = null; 191 private IBuildInfo mBuildInfo = null; 192 // last reboot time 193 private long mPrevRebootTime; 194 // The list of packages to run. populated in {@code setupTestPackageList} 195 // This is a member variable so that run can be called more than once 196 // and the test run is resumed. 197 private List<TestPackage> mTestPackageList = new ArrayList<>(); 198 // The index in the pacakge list of the last test to complete 199 private int mLastTestPackageIndex = 0; 200 201 /** data structure for a {@link IRemoteTest} and its known tests */ 202 static class TestPackage { 203 private final IRemoteTest mTestForPackage; 204 private final ITestPackageDef mPackageDef; 205 private final Collection<TestIdentifier> mKnownTests; 206 207 TestPackage(ITestPackageDef packageDef, IRemoteTest testForPackage) { 208 mPackageDef = packageDef; 209 mTestForPackage = testForPackage; 210 mKnownTests = packageDef.getTests(); 211 } 212 213 IRemoteTest getTestForPackage() { 214 return mTestForPackage; 215 } 216 217 Collection<TestIdentifier> getKnownTests() { 218 return mKnownTests; 219 } 220 221 ITestPackageDef getPackageDef() { 222 return mPackageDef; 223 } 224 225 /** 226 * @return the test run name that should be used for the TestPackage. 227 */ 228 String getTestRunName() { 229 return mPackageDef.getId(); 230 } 231 232 /** 233 * @return the ABI on which the test will run. 234 */ 235 IAbi getAbi() { 236 return mPackageDef.getAbi(); 237 } 238 } 239 240 /** 241 * A {@link ResultForwarder} that will forward a bugreport on each failed test. 242 */ 243 private static class FailedTestBugreportGenerator extends ResultForwarder { 244 private ITestDevice mDevice; 245 246 public FailedTestBugreportGenerator(ITestInvocationListener listener, ITestDevice device) { 247 super(listener); 248 mDevice = device; 249 } 250 251 @Override 252 public void testFailed(TestIdentifier test, String trace) { 253 super.testFailed(test, trace); 254 InputStreamSource bugSource = mDevice.getBugreport(); 255 super.testLog(String.format("bug-%s_%s", test.getClassName(), test.getTestName()), 256 LogDataType.TEXT, bugSource); 257 bugSource.cancel(); 258 } 259 } 260 261 /** 262 * A {@link ResultForwarder} that will forward a logcat snapshot on each failed test. 263 */ 264 private static class FailedTestLogcatGenerator extends ResultForwarder { 265 private ITestDevice mDevice; 266 private int mNumLogcatBytes; 267 268 public FailedTestLogcatGenerator(ITestInvocationListener listener, ITestDevice device, 269 int maxLogcatBytes) { 270 super(listener); 271 mDevice = device; 272 mNumLogcatBytes = maxLogcatBytes; 273 } 274 275 @Override 276 public void testFailed(TestIdentifier test, String trace) { 277 super.testFailed(test, trace); 278 // sleep a small amount of time to ensure test failure stack trace makes it into logcat 279 // capture 280 RunUtil.getDefault().sleep(10); 281 InputStreamSource logSource = mDevice.getLogcat(mNumLogcatBytes); 282 super.testLog(String.format("logcat-%s_%s", test.getClassName(), test.getTestName()), 283 LogDataType.TEXT, logSource); 284 logSource.cancel(); 285 } 286 } 287 288 /** 289 * A {@link ResultForwarder} that will forward a screenshot on test failures. 290 */ 291 private static class FailedTestScreenshotGenerator extends ResultForwarder { 292 private ITestDevice mDevice; 293 294 public FailedTestScreenshotGenerator(ITestInvocationListener listener, 295 ITestDevice device) { 296 super(listener); 297 mDevice = device; 298 } 299 300 @Override 301 public void testFailed(TestIdentifier test, String trace) { 302 super.testFailed(test, trace); 303 304 try { 305 InputStreamSource screenSource = mDevice.getScreenshot(); 306 super.testLog(String.format("screenshot-%s_%s", test.getClassName(), 307 test.getTestName()), LogDataType.PNG, screenSource); 308 screenSource.cancel(); 309 } catch (DeviceNotAvailableException e) { 310 // TODO: rethrow this somehow 311 CLog.e("Device %s became unavailable while capturing screenshot, %s", 312 mDevice.getSerialNumber(), e.toString()); 313 } 314 } 315 } 316 317 /** 318 * Create a new {@link CtsTest} that will run the default list of {@link TestPackage}s. 319 */ 320 public CtsTest() { 321 this(0 /*shardAssignment*/, 1 /*totalShards*/); 322 } 323 324 /** 325 * Create a new {@link CtsTest} that will run the given {@link List} of {@link TestPackage}s. 326 */ 327 public CtsTest(int shardAssignment, int totalShards) { 328 if (shardAssignment < 0) { 329 throw new IllegalArgumentException( 330 "shardAssignment cannot be negative. found:" + shardAssignment); 331 } 332 if (totalShards < 1) { 333 throw new IllegalArgumentException( 334 "shardAssignment must be at least 1. found:" + totalShards); 335 } 336 this.mShardAssignment = shardAssignment; 337 this.mTotalShards = totalShards; 338 } 339 340 /** 341 * {@inheritDoc} 342 */ 343 @Override 344 public ITestDevice getDevice() { 345 return mDevice; 346 } 347 348 /** 349 * {@inheritDoc} 350 */ 351 @Override 352 public void setDevice(ITestDevice device) { 353 mDevice = device; 354 } 355 356 /** 357 * Set the plan name to run. 358 * <p/> 359 * Exposed for unit testing 360 */ 361 void setPlanName(String planName) { 362 mPlanName = planName; 363 } 364 365 /** 366 * Set the skip collect device info flag. 367 * <p/> 368 * Exposed for unit testing 369 */ 370 void setSkipDeviceInfo(boolean skipDeviceInfo) { 371 mSkipDeviceInfo = skipDeviceInfo; 372 } 373 374 /** 375 * Adds a package name to the list of test packages to run. 376 * <p/> 377 * Exposed for unit testing 378 */ 379 void addPackageName(String packageName) { 380 mPackageNames.add(packageName); 381 } 382 383 /** 384 * Adds a package name to the list of test packages to exclude. 385 * <p/> 386 * Exposed for unit testing 387 */ 388 void addExcludedPackageName(String packageName) { 389 mExcludedPackageNames.add(packageName); 390 } 391 392 /** 393 * Set the test class name to run. 394 * <p/> 395 * Exposed for unit testing 396 */ 397 void setClassName(String className) { 398 mClassName = className; 399 } 400 401 /** 402 * Set the test method name to run. 403 * <p/> 404 * Exposed for unit testing 405 */ 406 void setMethodName(String methodName) { 407 mMethodName = methodName; 408 } 409 410 /** 411 * Set the test name to run e.g. android.test.cts.SampleTest#testSample 412 * <p/> 413 * Exposed for unit testing 414 */ 415 void setTestName(String testName) { 416 mTestName = testName; 417 } 418 419 /** 420 * Sets the test session id to continue. 421 * <p/> 422 * Exposed for unit testing 423 */ 424 void setContinueSessionId(int sessionId) { 425 mContinueSessionId = sessionId; 426 } 427 428 /** 429 * {@inheritDoc} 430 */ 431 @Override 432 public boolean isResumable() { 433 return mResume; 434 } 435 436 /** 437 * {@inheritDoc} 438 */ 439 @Override 440 public void setBuild(IBuildInfo build) { 441 mCtsBuild = CtsBuildHelper.createBuildHelper(build); 442 mBuildInfo = build; 443 } 444 445 /** 446 * Set the CTS build container. 447 * <p/> 448 * Exposed so unit tests can mock the provided build. 449 */ 450 void setBuildHelper(CtsBuildHelper buildHelper) { 451 mCtsBuild = buildHelper; 452 } 453 454 /** 455 * {@inheritDoc} 456 */ 457 @Override 458 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 459 if (getDevice() == null) { 460 throw new IllegalArgumentException("missing device"); 461 } 462 463 Set<String> abiSet = getAbis(); 464 if (abiSet == null || abiSet.isEmpty()) { 465 throw new IllegalArgumentException("could not get device's ABIs"); 466 } 467 Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "ABIs: " + abiSet); 468 469 checkFields(); 470 setupTestPackageList(abiSet); 471 if (mBugreport) { 472 listener = new FailedTestBugreportGenerator(listener, getDevice()); 473 } 474 if (mScreenshotOnFailures) { 475 listener = new FailedTestScreenshotGenerator(listener, getDevice()); 476 } 477 if (mLogcatOnFailures) { 478 listener = new FailedTestLogcatGenerator(listener, getDevice(), mMaxLogcatBytes); 479 } 480 481 // Setup the a map of Test id to ResultFilter 482 Map<String, ResultFilter> filterMap = new HashMap<>(); 483 int totalTestCount = 0; 484 for (TestPackage testPackage : mTestPackageList) { 485 ResultFilter resultFilter = new ResultFilter(listener, testPackage); 486 totalTestCount += resultFilter.getKnownTestCount(); 487 filterMap.put(testPackage.getPackageDef().getId(), resultFilter); 488 } 489 490 // collect and install the prerequisiteApks first, to save time when multiple test 491 // packages are using the same prerequisite apk 492 Map<String, Set<String>> prerequisiteApks = getPrerequisiteApks(mTestPackageList, abiSet); 493 Collection<String> uninstallPackages = getPrerequisitePackageNames(mTestPackageList); 494 495 try { 496 // always collect the device info, even for resumed runs, since test will likely be 497 // running on a different device 498 collectDeviceInfo(getDevice(), mCtsBuild, listener); 499 preRebootIfNecessary(mTestPackageList); 500 501 mPrevRebootTime = System.currentTimeMillis(); 502 int remainingPackageCount = mTestPackageList.size(); 503 Log.logAndDisplay(LogLevel.INFO, LOG_TAG, 504 String.format("Start test run of %,d packages, containing %,d tests", 505 remainingPackageCount, totalTestCount)); 506 IAbi currentAbi = null; 507 508 for (int i = mLastTestPackageIndex; i < mTestPackageList.size(); i++) { 509 TestPackage testPackage = mTestPackageList.get(i); 510 511 if (currentAbi == null || 512 !currentAbi.getName().equals(testPackage.getAbi().getName())) { 513 currentAbi = testPackage.getAbi(); 514 installPrerequisiteApks( 515 prerequisiteApks.get(currentAbi.getName()), currentAbi); 516 } 517 518 IRemoteTest test = testPackage.getTestForPackage(); 519 if (test instanceof IBuildReceiver) { 520 ((IBuildReceiver) test).setBuild(mBuildInfo); 521 } 522 if (test instanceof IDeviceTest) { 523 ((IDeviceTest) test).setDevice(getDevice()); 524 } 525 if (test instanceof DeqpTestRunner) { 526 ((DeqpTestRunner)test).setCollectLogs(mCollectDeqpLogs); 527 } 528 529 forwardPackageDetails(testPackage.getPackageDef(), listener); 530 test.run(filterMap.get(testPackage.getPackageDef().getId())); 531 if (i < mTestPackageList.size() - 1) { 532 TestPackage nextPackage = mTestPackageList.get(i + 1); 533 rebootIfNecessary(testPackage, nextPackage); 534 changeToHomeScreen(); 535 } 536 // Track of the last complete test package index for resume 537 mLastTestPackageIndex = i; 538 } 539 540 if (mScreenshot) { 541 InputStreamSource screenshotSource = getDevice().getScreenshot(); 542 try { 543 listener.testLog("screenshot", LogDataType.PNG, screenshotSource); 544 } finally { 545 screenshotSource.cancel(); 546 } 547 } 548 549 uninstallPrequisiteApks(uninstallPackages); 550 551 } catch (RuntimeException e) { 552 CLog.e(e); 553 throw e; 554 } catch (Error e) { 555 CLog.e(e); 556 throw e; 557 } finally { 558 for (ResultFilter filter : filterMap.values()) { 559 filter.reportUnexecutedTests(); 560 } 561 } 562 } 563 564 /** 565 * @param allTestPackageDefList The package list to filter 566 * @param deviceAbiSet The ABIs supported by the device being tested 567 * @return A {@link List} of {@link ITestPackageDef}s that should be tested 568 */ 569 private static List<ITestPackageDef> filterByAbi( 570 List<ITestPackageDef> allTestPackageDefList, Set<String> deviceAbiSet) { 571 List<ITestPackageDef> filteredTestPackageDefList = new LinkedList<>(); 572 for (ITestPackageDef testPackageDef : allTestPackageDefList) { 573 if (deviceAbiSet.contains(testPackageDef.getAbi().getName())) { 574 // We only need test packages that are not empty and of matching ABIs 575 filteredTestPackageDefList.add(testPackageDef); 576 } 577 } 578 return filteredTestPackageDefList; 579 } 580 581 /** Reboot then the device iff the list of packages exceeds the minimum */ 582 private void preRebootIfNecessary(List<TestPackage> testPackageList) 583 throws DeviceNotAvailableException { 584 if (mDisableReboot) { 585 return; 586 } 587 588 Set<String> packageNameSet = new HashSet<>(); 589 for (TestPackage testPackage : testPackageList) { 590 // Parse the package name 591 packageNameSet.add(AbiUtils.parseTestName(testPackage.getPackageDef().getId())); 592 } 593 if (packageNameSet.size() < mMinPreRebootPackageCount) { 594 // There is actually only one unique package name. No need to reboot. 595 return; 596 } 597 598 // Reboot is needed 599 Log.logAndDisplay(LogLevel.INFO, LOG_TAG, 600 String.format("Pre-test reboot (%,d packages). Use --disable-reboot to skip", 601 packageNameSet.size())); 602 603 rebootDevice(); 604 } 605 606 private void rebootIfNecessary(TestPackage testFinished, TestPackage testToRun) 607 throws DeviceNotAvailableException { 608 // If there comes spurious failure like INJECT_EVENTS for a package, 609 // reboot it before running it. 610 // Also reboot after package which is know to leave pop-up behind 611 final List<String> rebootAfterList = Arrays.asList( 612 "CtsMediaTestCases", 613 "CtsAccessibilityTestCases"); 614 final List<String> rebootBeforeList = Arrays.asList( 615 "CtsAnimationTestCases", 616 "CtsGraphicsTestCases", 617 "CtsViewTestCases", 618 "CtsWidgetTestCases" ); 619 long intervalInMSec = mRebootIntervalMin * 60 * 1000; 620 if (mDisableReboot || mDevice.getSerialNumber().startsWith("emulator-")) { 621 return; 622 } 623 long currentTime = System.currentTimeMillis(); 624 if (((currentTime - mPrevRebootTime) > intervalInMSec) || 625 rebootAfterList.contains(testFinished.getPackageDef().getName()) || 626 rebootBeforeList.contains(testToRun.getPackageDef().getName()) ) { 627 Log.i(LOG_TAG, 628 String.format("Rebooting after running package %s, before package %s", 629 testFinished.getPackageDef().getName(), 630 testToRun.getPackageDef().getName())); 631 rebootDevice(); 632 mPrevRebootTime = System.currentTimeMillis(); 633 } 634 } 635 636 private void rebootDevice() throws DeviceNotAvailableException { 637 final int TIMEOUT_MS = 10 * 60 * 1000; 638 TestDeviceOptions options = mDevice.getOptions(); 639 // store default value and increase time-out for reboot 640 int rebootTimeout = options.getRebootTimeout(); 641 long onlineTimeout = options.getOnlineTimeout(); 642 options.setRebootTimeout(TIMEOUT_MS); 643 options.setOnlineTimeout(TIMEOUT_MS); 644 mDevice.setOptions(options); 645 646 mDevice.reboot(); 647 648 // restore default values 649 options.setRebootTimeout(rebootTimeout); 650 options.setOnlineTimeout(onlineTimeout); 651 mDevice.setOptions(options); 652 Log.i(LOG_TAG, "Rebooting done"); 653 try { 654 Thread.sleep(mRebootWaitTimeMSec); 655 } catch (InterruptedException e) { 656 Log.i(LOG_TAG, "Boot wait interrupted"); 657 } 658 } 659 660 /** 661 * Remove artifacts like status bar from the previous test. 662 * But this cannot dismiss dialog popped-up. 663 */ 664 private void changeToHomeScreen() throws DeviceNotAvailableException { 665 final String homeCmd = "input keyevent 3"; 666 667 mDevice.executeShellCommand(homeCmd); 668 try { 669 Thread.sleep(1000); 670 } catch (InterruptedException e) { 671 //ignore 672 } 673 } 674 675 /** 676 * Set {@code mTestPackageList} to the list of test packages to run filtered by ABI. 677 */ 678 private void setupTestPackageList(Set<String> abis) throws DeviceNotAvailableException { 679 if (!mTestPackageList.isEmpty()) { 680 Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Resume tests using existing package list"); 681 return; 682 } 683 try { 684 // Collect ALL tests 685 ITestPackageRepo testRepo = createTestCaseRepo(); 686 List<ITestPackageDef> testPkgDefs = new ArrayList<>(getAvailableTestPackages(testRepo)); 687 testPkgDefs = filterByAbi(testPkgDefs, abis); 688 // Note: run() relies on the fact that the list is reliably sorted for sharding purposes 689 Collections.sort(testPkgDefs); 690 // Create test package list. 691 List<TestPackage> testPackageList = new ArrayList<>(); 692 for (ITestPackageDef testPackageDef : testPkgDefs) { 693 // Note: createTest filters the test list inside of testPackageDef by exclusion list 694 IRemoteTest testForPackage = testPackageDef.createTest(mCtsBuild.getTestCasesDir()); 695 if (testPackageDef.getTests().size() > 0) { 696 testPackageList.add(new TestPackage(testPackageDef, testForPackage)); 697 } 698 } 699 700 // Filter by shard 701 int numTestPackages = testPackageList.size(); 702 int totalShards = Math.min(mTotalShards, numTestPackages); 703 704 List<TestPackage> shardTestPackageList = new ArrayList<>(); 705 for (int i = mShardAssignment; i < numTestPackages; i += totalShards) { 706 shardTestPackageList.add(testPackageList.get(i)); 707 } 708 mTestPackageList.addAll(shardTestPackageList); 709 } catch (FileNotFoundException e) { 710 throw new IllegalArgumentException("failed to find test plan file", e); 711 } catch (ParseException e) { 712 throw new IllegalArgumentException("failed to parse test plan file", e); 713 } catch (ConfigurationException e) { 714 throw new IllegalArgumentException("failed to process arguments", e); 715 } 716 } 717 718 /** 719 * Return the {@link Set} of {@link ITestPackageDef}s to run unfiltered by ABI 720 * 721 * @return the {@link Set} of {@link ITestPackageDef}s to run 722 * @throws ParseException 723 * @throws FileNotFoundException 724 * @throws ConfigurationException 725 */ 726 private Set<ITestPackageDef> getAvailableTestPackages(ITestPackageRepo testRepo) 727 throws ParseException, FileNotFoundException, ConfigurationException { 728 // use LinkedHashSet to have predictable iteration order 729 Set<ITestPackageDef> testPkgDefs = new LinkedHashSet<>(); 730 if (mPlanName != null) { 731 Log.i(LOG_TAG, String.format("Executing CTS test plan %s", mPlanName)); 732 File ctsPlanFile = mCtsBuild.getTestPlanFile(mPlanName); 733 ITestPlan plan = createPlan(mPlanName); 734 plan.parse(createXmlStream(ctsPlanFile)); 735 736 for (String testId : plan.getTestIds()) { 737 if (mExcludedPackageNames.contains(AbiUtils.parseTestName(testId))) { 738 continue; 739 } 740 ITestPackageDef testPackageDef = testRepo.getTestPackage(testId); 741 if (testPackageDef == null) { 742 CLog.e("Could not find test id %s referenced in plan %s", testId, mPlanName); 743 continue; 744 } 745 746 testPackageDef.setTestFilter(plan.getTestFilter(testId)); 747 testPkgDefs.add(testPackageDef); 748 } 749 } else if (mPackageNames.size() > 0){ 750 Log.i(LOG_TAG, String.format("Executing test packages %s", mPackageNames)); 751 752 Map<String, List<ITestPackageDef>> testPackageDefMap = 753 testRepo.getTestPackageDefsByName(); 754 755 for (String name : mPackageNames) { 756 if (!testPackageDefMap.containsKey(name)) { 757 throw new IllegalArgumentException(String.format( 758 "Could not find test package %s. " + 759 "Use 'list packages' to see available packages.", name)); 760 } 761 testPkgDefs.addAll(testPackageDefMap.get(name)); 762 } 763 } else if (mClassName != null) { 764 Log.i(LOG_TAG, String.format("Executing CTS test class %s", mClassName)); 765 testPkgDefs.addAll(buildTestPackageDefSet(testRepo, mClassName, mMethodName)); 766 } else if (mTestName != null) { 767 Log.i(LOG_TAG, String.format("Executing CTS test %s", mTestName)); 768 String [] split = mTestName.split("#"); 769 if (split.length != 2) { 770 Log.logAndDisplay(LogLevel.WARN, LOG_TAG, String.format( 771 "Could not parse class and method from test %s", mTestName)); 772 } else { 773 String className = split[0]; 774 String methodName = split[1]; 775 testPkgDefs.addAll(buildTestPackageDefSet(testRepo, className, methodName)); 776 } 777 } else if (mContinueSessionId != null) { 778 // create an in-memory derived plan that contains the notExecuted tests from previous 779 // session use timestamp as plan name so it will hopefully be unique 780 String uniquePlanName = Long.toString(System.currentTimeMillis()); 781 PlanCreator planCreator = new PlanCreator(uniquePlanName, mContinueSessionId, 782 CtsTestStatus.NOT_EXECUTED); 783 ITestPlan plan = createPlan(planCreator); 784 for (String testId : plan.getTestIds()) { 785 if (mExcludedPackageNames.contains(AbiUtils.parseTestName(testId))) { 786 continue; 787 } 788 ITestPackageDef testPackageDef = testRepo.getTestPackage(testId); 789 if (testPackageDef == null) { 790 CLog.e("Could not find test id %s referenced in plan %s", testId, mPlanName); 791 continue; 792 } 793 794 testPackageDef.setTestFilter(plan.getTestFilter(testId)); 795 testPkgDefs.add(testPackageDef); 796 } 797 } else { 798 // should never get here - was checkFields() not called? 799 throw new IllegalStateException("nothing to run?"); 800 } 801 return testPkgDefs; 802 } 803 804 /** 805 * Return the list of unique prerequisite Android package names 806 * 807 * @param testPackages The {@link TestPackage}s that contain prerequisites 808 */ 809 private Collection<String> getPrerequisitePackageNames(List<TestPackage> testPackages) { 810 Set<String> pkgNames = new HashSet<>(); 811 for (TestPackage testPkg : testPackages) { 812 String pkgName = testPkg.mPackageDef.getTargetPackageName(); 813 if (pkgName != null) { 814 pkgNames.add(pkgName); 815 } 816 } 817 return pkgNames; 818 } 819 820 /** 821 * @return a {@link Set} containing {@link ITestPackageDef}s pertaining to the given 822 * {@code className} and {@code methodName}. 823 */ 824 private static Set<ITestPackageDef> buildTestPackageDefSet( 825 ITestPackageRepo testRepo, String className, String methodName) { 826 Set<ITestPackageDef> testPkgDefs = new LinkedHashSet<>(); 827 // try to find packages to run from class name 828 List<String> packageIds = testRepo.findPackageIdsForTest(className); 829 if (packageIds.isEmpty()) { 830 Log.logAndDisplay(LogLevel.WARN, LOG_TAG, String.format( 831 "Could not find package for test class %s", className)); 832 } 833 for (String packageId: packageIds) { 834 ITestPackageDef testPackageDef = testRepo.getTestPackage(packageId); 835 if (testPackageDef != null) { 836 testPackageDef.setClassName(className, methodName); 837 testPkgDefs.add(testPackageDef); 838 } 839 } 840 return testPkgDefs; 841 } 842 843 /** 844 * Return the list (by abi) of unique prerequisite apks to install 845 * 846 * @param testPackages The {@link List} of {@link TestPackage} that contain prerequisite APKs 847 */ 848 private Map<String, Set<String>> getPrerequisiteApks( 849 List<TestPackage> testPackages, Set<String> abiSet) { 850 Map<String, Set<String>> abiToApkMap = new HashMap<>(); 851 for (TestPackage testPkg : testPackages) { 852 if (testPkg.getKnownTests().size() == 0) { 853 // No tests, no point in installing pre-reqs 854 continue; 855 } 856 String apkName = testPkg.mPackageDef.getTargetApkName(); 857 if (apkName == null) { 858 continue; 859 } 860 String abiName = testPkg.getAbi().getName(); 861 if (!abiSet.contains(abiName)) { 862 continue; 863 } 864 865 if (!abiToApkMap.containsKey(abiName)) { 866 abiToApkMap.put(abiName, new HashSet<String>()); 867 } 868 abiToApkMap.get(abiName).add(apkName); 869 } 870 return abiToApkMap; 871 } 872 873 /** 874 * FIXME eventually this should be removed once we get rid of CtsTestStubs, any other 875 * prerequisite apks should be installed by the test runner 876 * 877 * Install the collection of test apk file names 878 * 879 * @param prerequisiteApks The APKs that must be installed 880 * @throws DeviceNotAvailableException 881 */ 882 private void installPrerequisiteApks(Collection<String> prerequisiteApks, IAbi abi) 883 throws DeviceNotAvailableException { 884 if (prerequisiteApks == null) { 885 return; 886 } 887 Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Installing prerequisites"); 888 for (String apkName : prerequisiteApks) { 889 try { 890 File apkFile = mCtsBuild.getTestApp(apkName); 891 String[] options = {AbiUtils.createAbiFlag(abi.getName())}; 892 String errorCode = getDevice().installPackage(apkFile, true, options); 893 if (errorCode != null) { 894 CLog.e("Failed to install %s. Reason: %s", apkName, errorCode); 895 } 896 } catch (FileNotFoundException e) { 897 CLog.e("Could not find test apk %s", apkName); 898 } 899 } 900 } 901 902 /** 903 * Uninstalls the collection of android package names from device. 904 * 905 * @param uninstallPackages The packages that must be uninstalled 906 */ 907 private void uninstallPrequisiteApks(Collection<String> uninstallPackages) 908 throws DeviceNotAvailableException { 909 for (String pkgName : uninstallPackages) { 910 getDevice().uninstallPackage(pkgName); 911 } 912 } 913 914 /** 915 * {@inheritDoc} 916 */ 917 @Override 918 public Collection<IRemoteTest> split() { 919 if (mShards <= 1) { 920 return null; 921 } 922 checkFields(); 923 924 List<IRemoteTest> shardQueue = new LinkedList<>(); 925 for (int shardAssignment = 0; shardAssignment < mShards; shardAssignment++) { 926 CtsTest ctsTest = new CtsTest(shardAssignment, mShards /* totalShards */); 927 OptionCopier.copyOptionsNoThrow(this, ctsTest); 928 // Set the shard count because the copy option on the previous line copies 929 // over the mShard value 930 ctsTest.mShards = 0; 931 shardQueue.add(ctsTest); 932 } 933 934 return shardQueue; 935 } 936 937 /** 938 * Runs the device info collector instrumentation on device, and forwards it to test listeners 939 * as run metrics. 940 * <p/> 941 * Exposed so unit tests can mock. 942 * 943 * @throws DeviceNotAvailableException 944 */ 945 void collectDeviceInfo(ITestDevice device, CtsBuildHelper ctsBuild, 946 ITestInvocationListener listener) throws DeviceNotAvailableException { 947 if (!mSkipDeviceInfo) { 948 String abi = AbiFormatter.getDefaultAbi(device, ""); 949 DeviceInfoCollector.collectDeviceInfo(device, abi, ctsBuild.getTestCasesDir(), listener); 950 } 951 } 952 953 /** 954 * Factory method for creating a {@link ITestPackageRepo}. 955 * <p/> 956 * Exposed for unit testing 957 */ 958 ITestPackageRepo createTestCaseRepo() { 959 return new TestPackageRepo(mCtsBuild.getTestCasesDir(), mIncludeKnownFailures); 960 } 961 962 /** 963 * Factory method for creating a {@link TestPlan}. 964 * <p/> 965 * Exposed for unit testing 966 */ 967 ITestPlan createPlan(String planName) { 968 return new TestPlan(planName, AbiUtils.getAbisSupportedByCts()); 969 } 970 971 /** 972 * Gets the set of ABIs supported by both CTS and the device under test 973 * <p/> 974 * Exposed for unit testing 975 * @return The set of ABIs to run the tests on 976 * @throws DeviceNotAvailableException 977 */ 978 Set<String> getAbis() throws DeviceNotAvailableException { 979 String bitness = (mForceAbi == null) ? "" : mForceAbi; 980 Set<String> abis = new HashSet<>(); 981 for (String abi : AbiFormatter.getSupportedAbis(mDevice, bitness)) { 982 if (AbiUtils.isAbiSupportedByCts(abi)) { 983 abis.add(abi); 984 } 985 } 986 return abis; 987 } 988 989 /** 990 * Factory method for creating a {@link TestPlan} from a {@link PlanCreator}. 991 * <p/> 992 * Exposed for unit testing 993 * @throws ConfigurationException 994 */ 995 ITestPlan createPlan(PlanCreator planCreator) 996 throws ConfigurationException { 997 return planCreator.createDerivedPlan(mCtsBuild, AbiUtils.getAbisSupportedByCts()); 998 } 999 1000 /** 1001 * Factory method for creating a {@link InputStream} from a plan xml file. 1002 * <p/> 1003 * Exposed for unit testing 1004 */ 1005 InputStream createXmlStream(File xmlFile) throws FileNotFoundException { 1006 return new BufferedInputStream(new FileInputStream(xmlFile)); 1007 } 1008 1009 private void checkFields() { 1010 // for simplicity of command line usage, make --plan, --package, --test and --class mutually 1011 // exclusive 1012 boolean mutualExclusiveArgs = xor(mPlanName != null, mPackageNames.size() > 0, 1013 mClassName != null, mContinueSessionId != null, mTestName != null); 1014 1015 if (!mutualExclusiveArgs) { 1016 throw new IllegalArgumentException(String.format( 1017 "Ambiguous or missing arguments. " + 1018 "One and only one of --%s --%s(s), --%s or --%s to run can be specified", 1019 PLAN_OPTION, PACKAGE_OPTION, CLASS_OPTION, CONTINUE_OPTION)); 1020 } 1021 if (mMethodName != null && mClassName == null) { 1022 throw new IllegalArgumentException(String.format( 1023 "Must specify --%s when --%s is used", CLASS_OPTION, METHOD_OPTION)); 1024 } 1025 if (mCtsBuild == null) { 1026 throw new IllegalArgumentException("missing CTS build"); 1027 } 1028 } 1029 1030 /** 1031 * Helper method to perform exclusive or on list of boolean arguments 1032 * 1033 * @param args set of booleans on which to perform exclusive or 1034 * @return <code>true</code> if one and only one of <var>args</code> is <code>true</code>. 1035 * Otherwise return <code>false</code>. 1036 */ 1037 private static boolean xor(boolean... args) { 1038 boolean currentVal = args[0]; 1039 for (int i=1; i < args.length; i++) { 1040 if (currentVal && args[i]) { 1041 return false; 1042 } 1043 currentVal |= args[i]; 1044 } 1045 return currentVal; 1046 } 1047 1048 /** 1049 * Forward the digest and package name to the listener as a metric 1050 * 1051 * @param listener Handles test results 1052 */ 1053 private static void forwardPackageDetails(ITestPackageDef def, ITestInvocationListener listener) { 1054 Map<String, String> metrics = new HashMap<>(3); 1055 metrics.put(PACKAGE_NAME_METRIC, def.getName()); 1056 metrics.put(PACKAGE_ABI_METRIC, def.getAbi().getName()); 1057 metrics.put(PACKAGE_DIGEST_METRIC, def.getDigest()); 1058 listener.testRunStarted(def.getId(), 0); 1059 listener.testRunEnded(0, metrics); 1060 } 1061 } 1062