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