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.tradefed.testtype; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkState; 21 22 import com.android.ddmlib.IDevice; 23 import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner; 24 import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner.TestSize; 25 import com.android.tradefed.config.ConfigurationException; 26 import com.android.tradefed.config.IConfiguration; 27 import com.android.tradefed.config.IConfigurationReceiver; 28 import com.android.tradefed.config.Option; 29 import com.android.tradefed.config.Option.Importance; 30 import com.android.tradefed.config.OptionClass; 31 import com.android.tradefed.device.DeviceNotAvailableException; 32 import com.android.tradefed.device.ITestDevice; 33 import com.android.tradefed.device.metric.CountTestCasesCollector; 34 import com.android.tradefed.device.metric.DeviceTraceCollector; 35 import com.android.tradefed.device.metric.GcovCodeCoverageCollector; 36 import com.android.tradefed.device.metric.IMetricCollector; 37 import com.android.tradefed.device.metric.IMetricCollectorReceiver; 38 import com.android.tradefed.error.HarnessRuntimeException; 39 import com.android.tradefed.invoker.TestInformation; 40 import com.android.tradefed.invoker.logger.InvocationMetricLogger; 41 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey; 42 import com.android.tradefed.invoker.tracing.CloseableTraceScope; 43 import com.android.tradefed.log.LogUtil.CLog; 44 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; 45 import com.android.tradefed.result.CollectingTestListener; 46 import com.android.tradefed.result.ITestInvocationListener; 47 import com.android.tradefed.result.TestDescription; 48 import com.android.tradefed.result.TestResult; 49 import com.android.tradefed.result.TestRunResult; 50 import com.android.tradefed.result.TestStatus; 51 import com.android.tradefed.result.ddmlib.AndroidTestOrchestratorRemoteTestRunner; 52 import com.android.tradefed.result.ddmlib.DefaultRemoteAndroidTestRunner; 53 import com.android.tradefed.result.ddmlib.RemoteAndroidTestRunner; 54 import com.android.tradefed.result.error.DeviceErrorIdentifier; 55 import com.android.tradefed.result.error.InfraErrorIdentifier; 56 import com.android.tradefed.result.proto.TestRecordProto.FailureStatus; 57 import com.android.tradefed.retry.IRetryDecision; 58 import com.android.tradefed.retry.RetryStrategy; 59 import com.android.tradefed.targetprep.BuildError; 60 import com.android.tradefed.targetprep.TargetSetupError; 61 import com.android.tradefed.targetprep.TestAppInstallSetup; 62 import com.android.tradefed.util.AbiFormatter; 63 import com.android.tradefed.util.ArrayUtil; 64 import com.android.tradefed.util.FileUtil; 65 import com.android.tradefed.util.ListInstrumentationParser; 66 import com.android.tradefed.util.ListInstrumentationParser.InstrumentationTarget; 67 import com.android.tradefed.util.ResourceUtil; 68 import com.android.tradefed.util.StringEscapeUtils; 69 70 import com.google.common.annotations.VisibleForTesting; 71 import com.google.common.collect.Sets; 72 73 import java.io.File; 74 import java.io.IOException; 75 import java.util.ArrayList; 76 import java.util.Collection; 77 import java.util.HashMap; 78 import java.util.LinkedHashMap; 79 import java.util.LinkedHashSet; 80 import java.util.List; 81 import java.util.Map; 82 import java.util.Map.Entry; 83 import java.util.Optional; 84 import java.util.Set; 85 import java.util.concurrent.TimeUnit; 86 87 /** A Test that runs an instrumentation test package on given device. */ 88 @OptionClass(alias = "instrumentation") 89 public class InstrumentationTest 90 implements IDeviceTest, 91 IRemoteTest, 92 ITestCollector, 93 IAbiReceiver, 94 IConfigurationReceiver, 95 IMetricCollectorReceiver { 96 97 /** max number of attempts to collect list of tests in package */ 98 private static final int COLLECT_TESTS_ATTEMPTS = 3; 99 100 /** instrumentation test runner argument key used for test execution using a file */ 101 private static final String TEST_FILE_INST_ARGS_KEY = "testFile"; 102 103 /** instrumentation test runner argument key used for individual test timeout */ 104 static final String TEST_TIMEOUT_INST_ARGS_KEY = "timeout_msec"; 105 106 /** default timeout for tests collection */ 107 static final long TEST_COLLECTION_TIMEOUT_MS = 2 * 60 * 1000; 108 109 public static final String RUN_TESTS_AS_USER_KEY = "RUN_TESTS_AS_USER"; 110 public static final String RUN_TESTS_ON_SDK_SANDBOX = "RUN_TESTS_ON_SDK_SANDBOX"; 111 private static final String SKIP_TESTS_REASON_KEY = "skip-tests-reason"; 112 113 @Option( 114 name = "package", 115 shortName = 'p', 116 description = "The manifest package name of the Android test application to run.", 117 importance = Importance.IF_UNSET) 118 private String mPackageName = null; 119 120 @Option( 121 name = "runner", 122 description = 123 "The instrumentation test runner class name to use. Will try to determine " 124 + "automatically if it is not specified.") 125 private String mRunnerName = null; 126 127 @Option(name = "class", shortName = 'c', description = "The test class name to run.") 128 private String mTestClassName = null; 129 130 @Option(name = "method", shortName = 'm', description = "The test method name to run.") 131 private String mTestMethodName = null; 132 133 @Option( 134 name = "test-package", 135 description = 136 "Only run tests within this specific java package. " 137 + "Will be ignored if --class is set.") 138 private String mTestPackageName = null; 139 140 /** 141 * See <a * 142 * href="https://developer.android.com/training/testing/instrumented-tests/androidx-test-libraries/runner#use-android">AndroidX-Orchestrator 143 * * </a> documentation for more details on how AndroidX-Orchestrator works and the 144 * functionality which it provides. 145 */ 146 @Option( 147 name = "orchestrator", 148 description = 149 "Each test will be executed in its own individual process through" 150 + " androidx-orchestrator.") 151 private boolean mOrchestrator = false; 152 153 /** 154 * @deprecated use shell-timeout or test-timeout option instead. 155 */ 156 @Deprecated 157 @Option( 158 name = "timeout", 159 description = "Deprecated - Use \"shell-timeout\" or \"test-timeout\" instead.") 160 private Integer mTimeout = null; 161 162 @Option( 163 name = "shell-timeout", 164 description = 165 "The defined timeout (in milliseconds) is used as a maximum waiting time when" 166 + " expecting the command output from the device. At any time, if the shell" 167 + " command does not output anything for a period longer than defined" 168 + " timeout the TF run terminates. For no timeout, set to 0.", 169 isTimeVal = true) 170 private long mShellTimeout = 10 * 60 * 1000L; // default to 10 minutes 171 172 @Option( 173 name = "test-timeout", 174 description = 175 "Sets timeout (in milliseconds) that will be applied to each test. In the event" 176 + " of a test timeout, it will log the results and proceed with executing" 177 + " the next test. For no timeout, set to 0.", 178 isTimeVal = true) 179 private long mTestTimeout = 5 * 60 * 1000L; // default to 5 minutes 180 181 @Option( 182 name = "max-timeout", 183 description = 184 "Sets the max timeout for the instrumentation to terminate. " 185 + "For no timeout, set to 0.", 186 isTimeVal = true) 187 private long mMaxTimeout = 0L; 188 189 @Option(name = "size", description = "Restrict test to a specific test size.") 190 private String mTestSize = null; 191 192 @Option( 193 name = "rerun", 194 description = 195 "Rerun unexecuted tests individually on same device if test run " 196 + "fails to complete.") 197 private boolean mIsRerunMode = true; 198 199 @Option( 200 name = "install-file", 201 description = "Optional file path to apk file that contains the tests.") 202 private File mInstallFile = null; 203 204 @Option( 205 name = "run-name", 206 description = 207 "Optional custom test run name to pass to listener. " 208 + "If unspecified, will use package name.") 209 private String mRunName = null; 210 211 @Option( 212 name = "instrumentation-arg", 213 description = "Additional instrumentation arguments to provide.", 214 requiredForRerun = true) 215 private final Map<String, String> mInstrArgMap = new LinkedHashMap<String, String>(); 216 217 @Option( 218 name = "rerun-from-file", 219 description = 220 "Use test file instead of separate adb commands for each test when re-running" 221 + " instrumentations for tests that failed to run in previous attempts. ") 222 private boolean mReRunUsingTestFile = false; 223 224 @Option( 225 name = "rerun-from-file-attempts", 226 description = 227 "Max attempts to rerun tests from file. -1 means rerun from file infinitely.") 228 private int mReRunUsingTestFileAttempts = 3; 229 230 @Option( 231 name = AbiFormatter.FORCE_ABI_STRING, 232 description = AbiFormatter.FORCE_ABI_DESCRIPTION, 233 importance = Importance.IF_UNSET) 234 private String mForceAbi = null; 235 236 @Option( 237 name = "collect-tests-only", 238 description = 239 "Only invoke the instrumentation to collect list of applicable test cases. All" 240 + " test run callbacks will be triggered, but test execution will not be" 241 + " actually carried out.") 242 private boolean mCollectTestsOnly = false; 243 244 @Option( 245 name = "collect-tests-timeout", 246 description = "Timeout for the tests collection operation.", 247 isTimeVal = true) 248 private long mCollectTestTimeout = TEST_COLLECTION_TIMEOUT_MS; 249 250 @Option( 251 name = "debug", 252 description = 253 "Wait for debugger before instrumentation starts. Note that this should only" 254 + " be used for local debugging, not suitable for automated runs.") 255 protected boolean mDebug = false; 256 257 /** 258 * @deprecated Use the --coverage option in CoverageOptions instead. 259 */ 260 @Deprecated 261 @Option( 262 name = "coverage", 263 description = 264 "Collect code coverage for this test run. Note that the build under test must" 265 + " be a coverage build or else this will fail.") 266 private boolean mCoverage = false; 267 268 @Deprecated 269 @Option( 270 name = "merge-coverage-measurements", 271 description = 272 "Merge coverage measurements from all test runs into a single measurement" 273 + " before logging.") 274 private boolean mMergeCoverageMeasurements = false; 275 276 @Deprecated 277 @Option( 278 name = "enforce-ajur-format", 279 description = "Whether or not enforcing the AJUR instrumentation output format") 280 private boolean mShouldEnforceFormat = false; 281 282 @Option( 283 name = "hidden-api-checks", 284 description = 285 "If set to false, the '--no-hidden-api-checks' flag will be passed to the am " 286 + "instrument command. Only works for P or later.") 287 private boolean mHiddenApiChecks = true; 288 289 @Option( 290 name = "test-api-access", 291 description = 292 "If set to false and hidden API checks are enabled, the '--no-test-api-access'" 293 + " flag will be passed to the am instrument command." 294 + " Only works for R or later.") 295 private boolean mTestApiAccess = true; 296 297 @Option( 298 name = "isolated-storage", 299 description = 300 "If set to false, the '--no-isolated-storage' flag will be passed to the am " 301 + "instrument command. Only works for Q or later.") 302 private boolean mIsolatedStorage = true; 303 304 @Option( 305 name = "window-animation", 306 description = 307 "If set to false, the '--no-window-animation' flag will be passed to the am " 308 + "instrument command. Only works for ICS or later.") 309 private boolean mWindowAnimation = true; 310 311 @Option( 312 name = "disable-duplicate-test-check", 313 description = 314 "If set to true, it will not check that a method is only run once by a " 315 + "given instrumentation.") 316 private boolean mDisableDuplicateCheck = false; 317 318 @Option( 319 name = "enable-soft-restart-check", 320 description = 321 "Whether or not to enable checking whether instrumentation crash is due to " 322 + "a system_server restart.") 323 private boolean mEnableSoftRestartCheck = false; 324 325 @Option( 326 name = "report-unexecuted-tests", 327 description = 328 "Whether or not to enable reporting all unexecuted tests from instrumentation.") 329 private boolean mReportUnexecuted = true; 330 331 @Option( 332 name = "restart", 333 description = 334 "If set to false, the '--no-restart' flag will be passed to the am " 335 + "instrument command. Only works for S or later.") 336 private boolean mRestart = true; 337 338 @Option( 339 name = "instrument-sdk-sandbox", 340 description = "If set to true, the test will run exclusively in an SDK sandbox.") 341 protected boolean mInstrumentSdkSandbox = false; 342 343 @Option( 344 name = "instrument-sdk-in-sandbox", 345 description = 346 "If set to true, will exclusively run the test as an SDK in an SDK sandbox with" 347 + "an SDK context instead of the sandbox context.") 348 protected boolean mInstrumentSdkInSandbox = false; 349 350 private IAbi mAbi = null; 351 352 private Collection<String> mInstallArgs = new ArrayList<>(); 353 354 private ITestDevice mDevice = null; 355 356 private IRemoteAndroidTestRunner mRunner; 357 358 private TestAppInstallSetup mOrchestratorSetup; 359 360 private TestAppInstallSetup mTestServicesSetup; 361 362 private Collection<TestDescription> mTestsToRun = null; 363 364 private String mCoverageTarget = null; 365 366 private String mTestFilePathOnDevice = null; 367 368 private ListInstrumentationParser mListInstrumentationParser = null; 369 private GcovCodeCoverageCollector mNativeCoverageListener = null; 370 371 private Set<String> mExtraDeviceListener = new LinkedHashSet<>(); 372 373 private boolean mIsRerun = false; 374 375 private IConfiguration mConfiguration = null; 376 private List<IMetricCollector> mCollectors = new ArrayList<>(); 377 378 /** {@inheritDoc} */ 379 @Override setConfiguration(IConfiguration config)380 public void setConfiguration(IConfiguration config) { 381 mConfiguration = config; 382 } 383 384 /** Gets the {@link IConfiguration} for this test. */ getConfiguration()385 public IConfiguration getConfiguration() { 386 return mConfiguration; 387 } 388 389 /** {@inheritDoc} */ 390 @Override setDevice(ITestDevice device)391 public void setDevice(ITestDevice device) { 392 mDevice = device; 393 } 394 395 /** Set the Android manifest package to run. */ setPackageName(String packageName)396 public void setPackageName(String packageName) { 397 mPackageName = packageName; 398 } 399 400 /** Optionally, set the Android instrumentation runner to use. */ setRunnerName(String runnerName)401 public void setRunnerName(String runnerName) { 402 mRunnerName = runnerName; 403 } 404 405 /** Gets the Android instrumentation runner to be used. */ getRunnerName()406 public String getRunnerName() { 407 return mRunnerName; 408 } 409 410 /** Optionally, set the test class name to run. */ setClassName(String testClassName)411 public void setClassName(String testClassName) { 412 mTestClassName = testClassName; 413 } 414 415 /** Optionally, set the test method to run. */ setMethodName(String testMethodName)416 public void setMethodName(String testMethodName) { 417 mTestMethodName = StringEscapeUtils.escapeShell(testMethodName); 418 } 419 420 /** 421 * Optionally, set the path to a file located on the device that should contain a list of line 422 * separated test classes and methods (format: com.foo.Class#method) to be run. If set, will 423 * automatically attempt to re-run tests using this test file via {@link 424 * InstrumentationFileTest} instead of executing separate adb commands for each remaining test 425 * via rerun. 426 */ setTestFilePathOnDevice(String testFilePathOnDevice)427 public void setTestFilePathOnDevice(String testFilePathOnDevice) { 428 mTestFilePathOnDevice = testFilePathOnDevice; 429 } 430 431 /** Optionally, set the test size to run. */ setTestSize(String size)432 public void setTestSize(String size) { 433 mTestSize = size; 434 } 435 436 /** Get the Android manifest package to run. */ getPackageName()437 public String getPackageName() { 438 return mPackageName; 439 } 440 441 /** Get the custom test run name that will be provided to listener */ getRunName()442 public String getRunName() { 443 return mRunName; 444 } 445 446 /** Set the custom test run name that will be provided to listener */ setRunName(String runName)447 public void setRunName(String runName) { 448 mRunName = runName; 449 } 450 451 /** 452 * Set the collection of tests that should be executed by this InstrumentationTest. 453 * 454 * @param tests the tests to run 455 */ setTestsToRun(Collection<TestDescription> tests)456 public void setTestsToRun(Collection<TestDescription> tests) { 457 mTestsToRun = tests; 458 } 459 460 /** Get the class name to run. */ getClassName()461 protected String getClassName() { 462 return mTestClassName; 463 } 464 465 /** Get the test method to run. */ getMethodName()466 protected String getMethodName() { 467 return mTestMethodName; 468 } 469 470 /** Get the path to a file that contains class#method combinations to be run */ getTestFilePathOnDevice()471 String getTestFilePathOnDevice() { 472 return mTestFilePathOnDevice; 473 } 474 475 /** Get the test java package to run. */ getTestPackageName()476 protected String getTestPackageName() { 477 return mTestPackageName; 478 } 479 setWindowAnimation(boolean windowAnimation)480 public void setWindowAnimation(boolean windowAnimation) { 481 mWindowAnimation = windowAnimation; 482 } 483 484 /** 485 * Sets the test package filter. 486 * 487 * <p>If non-null, only tests within the given java package will be executed. 488 * 489 * <p>Will be ignored if a non-null value has been provided to {@link #setClassName(String)} 490 */ setTestPackageName(String testPackageName)491 public void setTestPackageName(String testPackageName) { 492 mTestPackageName = testPackageName; 493 } 494 495 /** Get the test size to run. Returns <code>null</code> if no size has been set. */ getTestSize()496 String getTestSize() { 497 return mTestSize; 498 } 499 500 /** 501 * Optionally, set the maximum time (in milliseconds) expecting shell output from the device. 502 */ setShellTimeout(long timeout)503 public void setShellTimeout(long timeout) { 504 mShellTimeout = timeout; 505 } 506 507 /** Optionally, set the maximum time (in milliseconds) for each individual test run. */ setTestTimeout(long timeout)508 public void setTestTimeout(long timeout) { 509 mTestTimeout = timeout; 510 } 511 512 /** 513 * Set the coverage target of this test. 514 * 515 * <p>Currently unused. This method is just present so coverageTarget can be later retrieved via 516 * {@link #getCoverageTarget()} 517 */ setCoverageTarget(String coverageTarget)518 public void setCoverageTarget(String coverageTarget) { 519 mCoverageTarget = coverageTarget; 520 } 521 522 /** Get the coverageTarget previously set via {@link #setCoverageTarget(String)}. */ getCoverageTarget()523 public String getCoverageTarget() { 524 return mCoverageTarget; 525 } 526 527 /** Return <code>true</code> if rerun mode is on. */ isRerunMode()528 boolean isRerunMode() { 529 return mIsRerunMode; 530 } 531 532 /** Sets whether this is a test rerun. Reruns do not create new listeners or merge coverage. */ setIsRerun(boolean isRerun)533 void setIsRerun(boolean isRerun) { 534 mIsRerun = isRerun; 535 } 536 537 /** Optionally, set the rerun mode. */ setRerunMode(boolean rerun)538 public void setRerunMode(boolean rerun) { 539 mIsRerunMode = rerun; 540 } 541 542 /** Get the shell timeout in ms. */ getShellTimeout()543 long getShellTimeout() { 544 return mShellTimeout; 545 } 546 547 /** Get the test timeout in ms. */ getTestTimeout()548 long getTestTimeout() { 549 return mTestTimeout; 550 } 551 552 /** Returns the max timeout set for the instrumentation. */ getMaxTimeout()553 public long getMaxTimeout() { 554 return mMaxTimeout; 555 } 556 557 /** 558 * Set the optional file to install that contains the tests. 559 * 560 * @param installFile the installable {@link File} 561 */ setInstallFile(File installFile)562 public void setInstallFile(File installFile) { 563 mInstallFile = installFile; 564 } 565 566 /** {@inheritDoc} */ 567 @Override getDevice()568 public ITestDevice getDevice() { 569 return mDevice; 570 } 571 572 /** 573 * Set the max time in ms to allow for the 'max time to shell output response' when collecting 574 * tests. 575 * 576 * <p> 577 * 578 * @deprecated This method is a no-op 579 */ 580 @Deprecated 581 @SuppressWarnings("unused") setCollectsTestsShellTimeout(int timeout)582 public void setCollectsTestsShellTimeout(int timeout) { 583 // no-op 584 } 585 586 /** 587 * Add an argument to provide when running the instrumentation tests. 588 * 589 * @param key the argument name 590 * @param value the argument value 591 */ addInstrumentationArg(String key, String value)592 public void addInstrumentationArg(String key, String value) { 593 mInstrArgMap.put(key, value); 594 } 595 596 /** Allows to remove an entry from the instrumentation-arg. */ removeFromInstrumentationArg(String key)597 void removeFromInstrumentationArg(String key) { 598 mInstrArgMap.remove(key); 599 } 600 601 /** 602 * Retrieve the value of an argument to provide when running the instrumentation tests. 603 * 604 * @param key the argument name 605 * <p>Exposed for testing 606 */ getInstrumentationArg(String key)607 String getInstrumentationArg(String key) { 608 if (mInstrArgMap.containsKey(key)) { 609 return mInstrArgMap.get(key); 610 } 611 return null; 612 } 613 614 /** 615 * Sets force-abi option. 616 * 617 * @param abi 618 */ setForceAbi(String abi)619 public void setForceAbi(String abi) { 620 mForceAbi = abi; 621 } 622 getForceAbi()623 public String getForceAbi() { 624 return mForceAbi; 625 } 626 627 /** Returns the value of {@link InstrumentationTest#mOrchestrator} */ isOrchestrator()628 public boolean isOrchestrator() { 629 return mOrchestrator; 630 } 631 632 /** Sets the --orchestrator option */ setOrchestrator(boolean useOrchestrator)633 public void setOrchestrator(boolean useOrchestrator) { 634 mOrchestrator = useOrchestrator; 635 } 636 637 /** Sets the --rerun-from-file option. */ setReRunUsingTestFile(boolean reRunUsingTestFile)638 public void setReRunUsingTestFile(boolean reRunUsingTestFile) { 639 mReRunUsingTestFile = reRunUsingTestFile; 640 } 641 642 /** Allows to add more custom listeners to the runner */ addDeviceListeners(Set<String> extraListeners)643 public void addDeviceListeners(Set<String> extraListeners) { 644 mExtraDeviceListener.addAll(extraListeners); 645 } 646 getRunOptions(TestInformation testInformation)647 private List<String> getRunOptions(TestInformation testInformation) 648 throws DeviceNotAvailableException { 649 String abiName = resolveAbiName(); 650 List<String> runOptions = new ArrayList<>(); 651 // hidden-api-checks flag only exists in P and after. 652 // Using a temp variable to consolidate the dynamic checks 653 int apiLevel = !mHiddenApiChecks || !mWindowAnimation ? getDevice().getApiLevel() : 0; 654 if (!mHiddenApiChecks && apiLevel >= 28) { 655 runOptions.add("--no-hidden-api-checks"); 656 } 657 // test-api-access flag only exists in R and after. 658 // Test API checks are subset of hidden API checks, so only make sense if hidden API 659 // checks are enabled. 660 if (mHiddenApiChecks 661 && !mTestApiAccess 662 && getDevice().checkApiLevelAgainstNextRelease(30)) { 663 runOptions.add("--no-test-api-access"); 664 } 665 // isolated-storage flag only exists in Q and after. 666 if (!mIsolatedStorage && getDevice().checkApiLevelAgainstNextRelease(29)) { 667 runOptions.add("--no-isolated-storage"); 668 } 669 // window-animation flag only exists in ICS and after 670 if (!mWindowAnimation && apiLevel >= 14) { 671 runOptions.add("--no-window-animation"); 672 } 673 if (!mRestart && getDevice().checkApiLevelAgainstNextRelease(31)) { 674 runOptions.add("--no-restart"); 675 } 676 if (mInstrumentSdkInSandbox || testHasRunOnSdkSandboxProperty(testInformation)) { 677 runOptions.add("--instrument-sdk-in-sandbox"); 678 } 679 if (mInstrumentSdkSandbox) { 680 runOptions.add("--instrument-sdk-sandbox"); 681 } 682 683 if (abiName != null && getDevice().getApiLevel() > 20) { 684 mInstallArgs.add(String.format("--abi %s", abiName)); 685 runOptions.add(String.format("--abi %s", abiName)); 686 } 687 return runOptions; 688 } 689 testHasRunOnSdkSandboxProperty(TestInformation testInformation)690 private boolean testHasRunOnSdkSandboxProperty(TestInformation testInformation) 691 throws DeviceNotAvailableException { 692 return getDevice().checkApiLevelAgainstNextRelease(33) 693 && Optional.ofNullable(testInformation) 694 .map(TestInformation::properties) 695 .map(properties -> properties.get(RUN_TESTS_ON_SDK_SANDBOX)) 696 .map(value -> Boolean.TRUE.toString().equals(value)) 697 .orElse(false); 698 } 699 isTestRunningOnSdkSandbox(TestInformation testInfo)700 boolean isTestRunningOnSdkSandbox(TestInformation testInfo) throws DeviceNotAvailableException { 701 return mInstrumentSdkSandbox 702 || mInstrumentSdkInSandbox 703 || testHasRunOnSdkSandboxProperty(testInfo); 704 } 705 706 /** 707 * Configures the passed {@link RemoteAndroidTestRunner runner} with the run options that are 708 * fetched from {@link InstrumentationTest#getRunOptions(TestInformation)} 709 */ createRemoteAndroidTestRunner( String packageName, String runnerName, IDevice device, TestInformation testInformation)710 IRemoteAndroidTestRunner createRemoteAndroidTestRunner( 711 String packageName, String runnerName, IDevice device, TestInformation testInformation) 712 throws DeviceNotAvailableException { 713 try (CloseableTraceScope ignored = 714 new CloseableTraceScope("createRemoteAndroidTestRunner")) { 715 RemoteAndroidTestRunner runner; 716 String runOptions = 717 String.join(mOrchestrator ? "," : " ", getRunOptions(testInformation)); 718 if (!mOrchestrator) { 719 runner = new DefaultRemoteAndroidTestRunner(packageName, runnerName, device); 720 } else { 721 runner = 722 new AndroidTestOrchestratorRemoteTestRunner( 723 packageName, runnerName, device); 724 } 725 if (!runOptions.isEmpty()) { 726 runner.setRunOptions(runOptions); 727 } 728 return runner; 729 } 730 } 731 resolveAbiName()732 private String resolveAbiName() throws DeviceNotAvailableException { 733 if (mAbi != null && mForceAbi != null) { 734 throw new IllegalArgumentException("cannot specify both abi flags"); 735 } 736 String abiName = null; 737 if (mAbi != null) { 738 abiName = mAbi.getName(); 739 } else if (mForceAbi != null && !mForceAbi.isEmpty()) { 740 abiName = AbiFormatter.getDefaultAbi(mDevice, mForceAbi); 741 if (abiName == null) { 742 throw new RuntimeException( 743 String.format("Cannot find abi for force-abi %s", mForceAbi)); 744 } 745 } 746 return abiName; 747 } 748 749 /** Set the {@link ListInstrumentationParser}. */ 750 @VisibleForTesting setListInstrumentationParser(ListInstrumentationParser listInstrumentationParser)751 void setListInstrumentationParser(ListInstrumentationParser listInstrumentationParser) { 752 mListInstrumentationParser = listInstrumentationParser; 753 } 754 755 /** 756 * Get the {@link ListInstrumentationParser} used to parse 'pm list instrumentation' queries. 757 */ getListInstrumentationParser()758 protected ListInstrumentationParser getListInstrumentationParser() { 759 if (mListInstrumentationParser == null) { 760 mListInstrumentationParser = new ListInstrumentationParser(); 761 } 762 return mListInstrumentationParser; 763 } 764 765 /** 766 * Query the device for a test runner to use. 767 * 768 * @return the first test runner name that matches the package or null if we don't find any. 769 * @throws DeviceNotAvailableException 770 */ queryRunnerName()771 protected String queryRunnerName() throws DeviceNotAvailableException { 772 try (CloseableTraceScope ignored = new CloseableTraceScope("query_runner_name")) { 773 ListInstrumentationParser parser = getListInstrumentationParser(); 774 getDevice().executeShellCommand("pm list instrumentation", parser); 775 776 Set<String> candidates = new LinkedHashSet<>(); 777 for (InstrumentationTarget target : parser.getInstrumentationTargets()) { 778 if (mPackageName.equals(target.packageName)) { 779 candidates.add(target.runnerName); 780 } 781 } 782 if (candidates.isEmpty()) { 783 CLog.w("Unable to determine runner name for package: %s", mPackageName); 784 return null; 785 } 786 // Bias toward using one of the AJUR runner when available, otherwise use the first 787 // runner 788 // available. 789 Set<String> intersection = 790 Sets.intersection(candidates, ListInstrumentationParser.SHARDABLE_RUNNERS); 791 if (intersection.isEmpty()) { 792 return candidates.iterator().next(); 793 } 794 return intersection.iterator().next(); 795 } 796 } 797 installApk(String apkResourcePath, TestInformation testInfo)798 private TestAppInstallSetup installApk(String apkResourcePath, TestInformation testInfo) 799 throws DeviceNotAvailableException, IOException, BuildError, TargetSetupError { 800 File apkOnDisk = null; 801 try (CloseableTraceScope apkInstall = 802 new CloseableTraceScope(String.format("install_%s", apkResourcePath))) { 803 apkOnDisk = FileUtil.createTempFile(apkResourcePath, ".apk"); 804 ResourceUtil.extractResourceToFile(String.format("/%s", apkResourcePath), apkOnDisk); 805 TestAppInstallSetup appInstaller = new TestAppInstallSetup(); 806 appInstaller.setForceQueryable(true); 807 appInstaller.addTestFile(apkOnDisk); 808 appInstaller.setUp(testInfo); 809 CLog.i("Successfully installed %s", apkResourcePath); 810 return appInstaller; 811 } finally { 812 FileUtil.deleteFile(apkOnDisk); 813 } 814 } 815 installOrchestratorHelperApks(TestInformation testInfo)816 private void installOrchestratorHelperApks(TestInformation testInfo) { 817 try { 818 mOrchestratorSetup = installApk("test-orchestrator-normalized.apk", testInfo); 819 mTestServicesSetup = installApk("test-services-normalized.apk", testInfo); 820 } catch (IOException | BuildError | TargetSetupError | DeviceNotAvailableException e) { 821 throw new IllegalStateException("Could not install test orchestrator", e); 822 } 823 } 824 825 /** {@inheritDoc} */ 826 @Override run(TestInformation testInfo, final ITestInvocationListener listener)827 public void run(TestInformation testInfo, final ITestInvocationListener listener) 828 throws DeviceNotAvailableException { 829 checkArgument(mDevice != null, "Device has not been set."); 830 checkArgument(mPackageName != null, "Package name has not been set."); 831 // Install the apk before checking the runner 832 if (mInstallFile != null) { 833 if (mDevice.isBypassLowTargetSdkBlockSupported()) { 834 mInstallArgs.add("--bypass-low-target-sdk-block"); 835 } 836 837 String installOutput = 838 mDevice.installPackage( 839 mInstallFile, true, mInstallArgs.toArray(new String[] {})); 840 if (installOutput != null) { 841 throw new HarnessRuntimeException( 842 String.format( 843 "Error while installing '%s': %s", 844 mInstallFile.getName(), installOutput), 845 DeviceErrorIdentifier.APK_INSTALLATION_FAILED); 846 } 847 } 848 if (mRunnerName == null) { 849 setRunnerName(queryRunnerName()); 850 if (mRunnerName == null) { 851 throw new HarnessRuntimeException( 852 "Runner name has not been set and no matching instrumentations were found.", 853 InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 854 } 855 CLog.i("No runner name specified. Using: %s.", mRunnerName); 856 } 857 if (mOrchestrator && mCollectTestsOnly) { 858 // Disable the orchestrator when running in dry-mode as the orchestrator does not 859 // support the dry-mode, which means we need to switch over to the default mode. 860 mOrchestrator = false; 861 } 862 if (mOrchestrator) { 863 installOrchestratorHelperApks(testInfo); 864 } 865 mRunner = 866 createRemoteAndroidTestRunner( 867 mPackageName, mRunnerName, mDevice.getIDevice(), testInfo); 868 setRunnerArgs(mRunner); 869 if (testInfo != null && testInfo.properties().containsKey(SKIP_TESTS_REASON_KEY)) { 870 mRunner.addInstrumentationArg( 871 SKIP_TESTS_REASON_KEY, testInfo.properties().get(SKIP_TESTS_REASON_KEY)); 872 } 873 874 doTestRun(testInfo, listener); 875 if (mInstallFile != null) { 876 mDevice.uninstallPackage(mPackageName); 877 } 878 if (mOrchestrator) { 879 mOrchestratorSetup.tearDown(testInfo, null); 880 mTestServicesSetup.tearDown(testInfo, null); 881 } 882 } 883 setRunnerArgs(IRemoteAndroidTestRunner runner)884 protected void setRunnerArgs(IRemoteAndroidTestRunner runner) { 885 if (mTestClassName != null) { 886 if (mTestMethodName != null) { 887 runner.setMethodName(mTestClassName, mTestMethodName); 888 } else { 889 runner.setClassName(mTestClassName); 890 } 891 } else if (mTestPackageName != null) { 892 runner.setTestPackageName(mTestPackageName); 893 } 894 if (mTestFilePathOnDevice != null) { 895 addInstrumentationArg(TEST_FILE_INST_ARGS_KEY, mTestFilePathOnDevice); 896 } 897 if (mTestSize != null) { 898 runner.setTestSize(TestSize.getTestSize(mTestSize)); 899 } 900 addTimeoutsToRunner(runner); 901 if (mRunName != null) { 902 runner.setRunName(mRunName); 903 } 904 for (Map.Entry<String, String> argEntry : mInstrArgMap.entrySet()) { 905 runner.addInstrumentationArg(argEntry.getKey(), argEntry.getValue()); 906 } 907 } 908 909 /** Helper method to add test-timeout & shell-timeout timeouts to given runner */ addTimeoutsToRunner(IRemoteAndroidTestRunner runner)910 private void addTimeoutsToRunner(IRemoteAndroidTestRunner runner) { 911 if (mTimeout != null) { 912 CLog.w( 913 "\"timeout\" argument is deprecated and should not be used! \"shell-timeout\"" 914 + " argument value is overwritten with %d ms", 915 mTimeout); 916 setShellTimeout(mTimeout); 917 } 918 if (mTestTimeout < 0) { 919 throw new IllegalArgumentException( 920 String.format("test-timeout %d cannot be negative", mTestTimeout)); 921 } 922 if (mShellTimeout <= mTestTimeout) { 923 // set shell timeout to 110% of test timeout 924 mShellTimeout = mTestTimeout + mTestTimeout / 10; 925 CLog.w( 926 String.format( 927 "shell-timeout should be larger than test-timeout %d; NOTE: extending" 928 + " shell-timeout to %d, please consider fixing this!", 929 mTestTimeout, mShellTimeout)); 930 } 931 runner.setMaxTimeToOutputResponse(mShellTimeout, TimeUnit.MILLISECONDS); 932 runner.setMaxTimeout(mMaxTimeout, TimeUnit.MILLISECONDS); 933 addInstrumentationArg(TEST_TIMEOUT_INST_ARGS_KEY, Long.toString(mTestTimeout)); 934 } 935 936 /** 937 * Execute test run. 938 * 939 * @param listener the test result listener 940 * @throws DeviceNotAvailableException if device stops communicating 941 */ doTestRun(TestInformation testInfo, ITestInvocationListener listener)942 private void doTestRun(TestInformation testInfo, ITestInvocationListener listener) 943 throws DeviceNotAvailableException { 944 // If this is a dry-run, just collect the tests and return 945 if (mCollectTestsOnly) { 946 checkState( 947 mTestsToRun == null, 948 "Tests to run should not be set explicitly when --collect-tests-only is set."); 949 950 // Use the actual listener to collect the tests, and print a error if this fails 951 Collection<TestDescription> collectedTests = 952 collectTestsToRun(testInfo, mRunner, listener); 953 if (collectedTests == null) { 954 CLog.e("Failed to collect tests for %s", mPackageName); 955 } else { 956 CLog.i("Collected %d tests for %s", collectedTests.size(), mPackageName); 957 } 958 return; 959 } 960 961 // If the tests to run weren't provided explicitly, collect them. 962 Collection<TestDescription> testsToRun = mTestsToRun; 963 // Don't collect tests when running in orchestrator mode as the orchestrator(proxy) will 964 // handle this step for us. 965 if (testsToRun == null && !mOrchestrator) { 966 // Don't notify the listener since it's not a real run. 967 testsToRun = collectTestsToRun(testInfo, mRunner, null); 968 } 969 970 // Only set the debug flag after collecting tests. 971 if (mDebug) { 972 mRunner.setDebug(true); 973 } 974 if (mConfiguration != null && mConfiguration.getCoverageOptions().isCoverageEnabled()) { 975 mRunner.addInstrumentationArg("coverage", "true"); 976 } 977 978 // Reruns do not create new listeners. 979 if (!mIsRerun) { 980 981 List<IMetricCollector> copyList = new ArrayList<IMetricCollector>(mCollectors); 982 if (mConfiguration != null 983 && mConfiguration.getCommandOptions().reportTestCaseCount()) { 984 CountTestCasesCollector counter = new CountTestCasesCollector(this); 985 copyList.add(counter); 986 } 987 if (testsToRun != null && testsToRun.isEmpty()) { 988 // Do not initialize collectors when collection was successful with no tests to run. 989 CLog.d( 990 "No tests were collected for %s. Skipping initializing collectors.", 991 mPackageName); 992 } else { 993 // TODO: Convert to device-side collectors when possible. 994 if (testInfo != null) { 995 for (IMetricCollector collector : copyList) { 996 if (collector.isDisabled()) { 997 CLog.d("%s has been disabled. Skipping.", collector); 998 } else { 999 try (CloseableTraceScope ignored = 1000 new CloseableTraceScope( 1001 "init_for_inst_" 1002 + collector.getClass().getSimpleName())) { 1003 CLog.d( 1004 "Initializing %s for instrumentation.", 1005 collector.getClass().getCanonicalName()); 1006 if (collector instanceof IConfigurationReceiver) { 1007 ((IConfigurationReceiver) collector) 1008 .setConfiguration(mConfiguration); 1009 } 1010 if (collector instanceof DeviceTraceCollector) { 1011 ((DeviceTraceCollector) collector) 1012 .setInstrumentationPkgName(mPackageName); 1013 } 1014 listener = collector.init(testInfo.getContext(), listener); 1015 } 1016 } 1017 } 1018 } 1019 } 1020 } 1021 1022 // Add the extra listeners only to the actual run and not the --collect-test-only one 1023 if (!mExtraDeviceListener.isEmpty()) { 1024 mRunner.addInstrumentationArg("listener", ArrayUtil.join(",", mExtraDeviceListener)); 1025 } 1026 1027 InstrumentationListener instrumentationListener = 1028 new InstrumentationListener(getDevice(), testsToRun, listener); 1029 instrumentationListener.setPackageName(mPackageName); 1030 instrumentationListener.setDisableDuplicateCheck(mDisableDuplicateCheck); 1031 if (mEnableSoftRestartCheck) { 1032 instrumentationListener.setOriginalSystemServer( 1033 getDevice().getProcessByName("system_server")); 1034 } 1035 instrumentationListener.setReportUnexecutedTests(mReportUnexecuted); 1036 1037 try (CloseableTraceScope instru = new CloseableTraceScope("run_instrumentation")) { 1038 if (testsToRun == null) { 1039 // Failed to collect the tests or collection is off. Just try to run them all. 1040 runInstrumentationTests(testInfo, mRunner, instrumentationListener); 1041 } else if (!testsToRun.isEmpty()) { 1042 runWithRerun(testInfo, listener, instrumentationListener, testsToRun); 1043 } else { 1044 CLog.i("No tests expected for %s, skipping", mPackageName); 1045 listener.testRunStarted(mPackageName, 0); 1046 listener.testRunEnded(0, new HashMap<String, Metric>()); 1047 } 1048 } 1049 } 1050 runInstrumentationTests( TestInformation testInfo, IRemoteAndroidTestRunner runner, ITestInvocationListener... receivers)1051 private boolean runInstrumentationTests( 1052 TestInformation testInfo, 1053 IRemoteAndroidTestRunner runner, 1054 ITestInvocationListener... receivers) 1055 throws DeviceNotAvailableException { 1056 if (testInfo != null && testInfo.properties().containsKey(RUN_TESTS_AS_USER_KEY)) { 1057 return mDevice.runInstrumentationTestsAsUser( 1058 runner, 1059 Integer.parseInt(testInfo.properties().get(RUN_TESTS_AS_USER_KEY)), 1060 receivers); 1061 } 1062 return mDevice.runInstrumentationTests(runner, receivers); 1063 } 1064 1065 /** 1066 * Execute the test run, but re-run incomplete tests individually if run fails to complete. 1067 * 1068 * @param listener the {@link ITestInvocationListener} 1069 * @param expectedTests the full set of expected tests in this run. 1070 */ runWithRerun( TestInformation testInfo, final ITestInvocationListener listener, final InstrumentationListener instrumentationListener, Collection<TestDescription> expectedTests)1071 private void runWithRerun( 1072 TestInformation testInfo, 1073 final ITestInvocationListener listener, 1074 final InstrumentationListener instrumentationListener, 1075 Collection<TestDescription> expectedTests) 1076 throws DeviceNotAvailableException { 1077 CollectingTestListener testTracker = new CollectingTestListener(); 1078 instrumentationListener.addListener(testTracker); 1079 1080 runInstrumentationTests(testInfo, mRunner, instrumentationListener); 1081 TestRunResult testRun = testTracker.getCurrentRunResults(); 1082 if (testRun.isRunFailure() || !testRun.getCompletedTests().containsAll(expectedTests)) { 1083 // Don't re-run any completed tests, unless this is a coverage run. 1084 if (mConfiguration != null 1085 && !mConfiguration.getCoverageOptions().isCoverageEnabled()) { 1086 expectedTests.removeAll(excludeNonExecuted(testTracker.getCurrentRunResults())); 1087 IRetryDecision decision = mConfiguration.getRetryDecision(); 1088 if (!RetryStrategy.NO_RETRY.equals(decision.getRetryStrategy()) 1089 && decision.getMaxTestRunAttempts() > 1) { 1090 // Delegate retry to the module/invocation level. 1091 // This prevents the InstrumentationTest retry from re-running by itself and 1092 // creating overhead. 1093 return; 1094 } 1095 } 1096 rerunTests(expectedTests, testInfo, listener); 1097 } 1098 } 1099 1100 /** Filter out "NOT_EXECUTED" and Skipped for the purpose of tracking what needs to be rerun. */ excludeNonExecuted(TestRunResult results)1101 protected static Set<TestDescription> excludeNonExecuted(TestRunResult results) { 1102 Set<TestDescription> completedTest = results.getCompletedTests(); 1103 for (Entry<TestDescription, TestResult> entry : results.getTestResults().entrySet()) { 1104 if (completedTest.contains(entry.getKey()) && entry.getValue().getFailure() != null) { 1105 if (FailureStatus.NOT_EXECUTED.equals( 1106 entry.getValue().getFailure().getFailureStatus())) { 1107 completedTest.remove(entry.getKey()); 1108 } 1109 } 1110 if (completedTest.contains(entry.getKey()) 1111 && TestStatus.SKIPPED.equals(entry.getValue().getResultStatus())) { 1112 completedTest.remove(entry.getKey()); 1113 } 1114 } 1115 return completedTest; 1116 } 1117 1118 /** 1119 * Rerun any <var>mRemainingTests</var> 1120 * 1121 * @param listener the {@link ITestInvocationListener} 1122 * @throws DeviceNotAvailableException 1123 */ rerunTests( Collection<TestDescription> expectedTests, TestInformation testInfo, final ITestInvocationListener listener)1124 private void rerunTests( 1125 Collection<TestDescription> expectedTests, 1126 TestInformation testInfo, 1127 final ITestInvocationListener listener) 1128 throws DeviceNotAvailableException { 1129 if (expectedTests.isEmpty()) { 1130 CLog.d("No tests to re-run, all tests executed at least once."); 1131 return; 1132 } 1133 // Ensure device is online and responsive before retrying. 1134 mDevice.waitForDeviceAvailable(); 1135 1136 IRemoteTest testReRunner = null; 1137 try { 1138 testReRunner = getTestReRunner(expectedTests); 1139 } catch (ConfigurationException e) { 1140 CLog.e("Failed to create test runner: %s", e.getMessage()); 1141 return; 1142 } 1143 if (testReRunner == null) { 1144 CLog.d("No internal rerun configured"); 1145 return; 1146 } 1147 1148 if (mNativeCoverageListener != null) { 1149 mNativeCoverageListener.setCollectOnTestEnd(false); 1150 } 1151 1152 testReRunner.run(testInfo, listener); 1153 1154 if (mNativeCoverageListener != null) { 1155 mNativeCoverageListener.setCollectOnTestEnd(true); 1156 mNativeCoverageListener.logCoverageMeasurements(mDevice, "rerun_merged"); 1157 } 1158 } 1159 1160 @VisibleForTesting getTestReRunner(Collection<TestDescription> tests)1161 IRemoteTest getTestReRunner(Collection<TestDescription> tests) throws ConfigurationException { 1162 if (mReRunUsingTestFile) { 1163 // Track the feature usage 1164 InvocationMetricLogger.addInvocationMetrics( 1165 InvocationMetricKey.INSTRUMENTATION_RERUN_FROM_FILE, 1); 1166 return new InstrumentationFileTest(this, tests, mReRunUsingTestFileAttempts); 1167 } else { 1168 // Track the feature usage which is deprecated now. 1169 InvocationMetricLogger.addInvocationMetrics( 1170 InvocationMetricKey.INSTRUMENTATION_RERUN_SERIAL, 1); 1171 return null; 1172 } 1173 } 1174 1175 /** 1176 * Collect the list of tests that should be executed by this test run. 1177 * 1178 * <p>This will be done by executing the test run in 'logOnly' mode, and recording the list of 1179 * tests. 1180 * 1181 * @param runner the {@link IRemoteAndroidTestRunner} to use to run the tests. 1182 * @return a {@link Collection} of {@link TestDescription}s that represent all tests to be 1183 * executed by this run 1184 * @throws DeviceNotAvailableException 1185 */ collectTestsToRun( final TestInformation testInfo, final IRemoteAndroidTestRunner runner, final ITestInvocationListener listener)1186 private Collection<TestDescription> collectTestsToRun( 1187 final TestInformation testInfo, 1188 final IRemoteAndroidTestRunner runner, 1189 final ITestInvocationListener listener) 1190 throws DeviceNotAvailableException { 1191 if (isRerunMode()) { 1192 CLog.d( 1193 "Collecting test info for %s on device %s", 1194 mPackageName, mDevice.getSerialNumber()); 1195 runner.setTestCollection(true); 1196 // always explicitly set debug to false when collecting tests 1197 runner.setDebug(false); 1198 // try to collect tests multiple times, in case device is temporarily not available 1199 // on first attempt 1200 try (CloseableTraceScope ignored = 1201 new CloseableTraceScope(InvocationMetricKey.instru_collect_tests.toString())) { 1202 Collection<TestDescription> tests = 1203 collectTestsAndRetry(testInfo, runner, listener); 1204 // done with "logOnly" mode, restore proper test timeout before real test execution 1205 addTimeoutsToRunner(runner); 1206 runner.setTestCollection(false); 1207 return tests; 1208 } 1209 } 1210 return null; 1211 } 1212 1213 /** 1214 * Performs the actual work of collecting tests, making multiple attempts if necessary 1215 * 1216 * @param runner the {@link IRemoteAndroidTestRunner} that will be used for the instrumentation 1217 * @param listener the {ITestInvocationListener} where to report results, can be null if we are 1218 * not reporting the results to the main invocation and simply collecting tests. 1219 * @return the collection of tests, or <code>null</code> if tests could not be collected 1220 * @throws DeviceNotAvailableException if communication with the device was lost 1221 */ 1222 @VisibleForTesting collectTestsAndRetry( final TestInformation testInfo, final IRemoteAndroidTestRunner runner, final ITestInvocationListener listener)1223 Collection<TestDescription> collectTestsAndRetry( 1224 final TestInformation testInfo, 1225 final IRemoteAndroidTestRunner runner, 1226 final ITestInvocationListener listener) 1227 throws DeviceNotAvailableException { 1228 boolean communicationFailure = false; 1229 for (int i = 0; i < COLLECT_TESTS_ATTEMPTS; i++) { 1230 CollectingTestListener collector = new CollectingTestListener(); 1231 boolean instrResult = false; 1232 // We allow to override the ddmlib default timeout for collection of tests. 1233 runner.setMaxTimeToOutputResponse(mCollectTestTimeout, TimeUnit.MILLISECONDS); 1234 if (listener == null) { 1235 instrResult = runInstrumentationTests(testInfo, runner, collector); 1236 } else { 1237 instrResult = runInstrumentationTests(testInfo, runner, collector, listener); 1238 } 1239 TestRunResult runResults = collector.getCurrentRunResults(); 1240 if (!instrResult || !runResults.isRunComplete()) { 1241 // communication failure with device, retry 1242 CLog.w( 1243 "No results when collecting tests to run for %s on device %s. Retrying", 1244 mPackageName, mDevice.getSerialNumber()); 1245 communicationFailure = true; 1246 } else if (runResults.isRunFailure()) { 1247 // not a communication failure, but run still failed. 1248 // TODO: should retry be attempted 1249 CLog.w( 1250 "Run failure %s when collecting tests to run for %s on device %s.", 1251 runResults.getRunFailureMessage(), mPackageName, mDevice.getSerialNumber()); 1252 return null; 1253 } else { 1254 // success! 1255 return runResults.getCompletedTests(); 1256 } 1257 } 1258 if (communicationFailure) { 1259 // TODO: find a better way to handle this 1260 // throwing DeviceUnresponsiveException is not always ideal because a misbehaving 1261 // instrumentation can hang, even though device is responsive. Would be nice to have 1262 // a louder signal for this situation though than just logging an error 1263 // throw new DeviceUnresponsiveException(String.format( 1264 // "Communication failure when attempting to collect tests %s on 1265 // device %s", 1266 // mPackageName, mDevice.getSerialNumber())); 1267 CLog.w( 1268 "Ignoring repeated communication failure when collecting tests %s for device" 1269 + " %s", 1270 mPackageName, mDevice.getSerialNumber()); 1271 } 1272 CLog.e( 1273 "Failed to collect tests to run for %s on device %s.", 1274 mPackageName, mDevice.getSerialNumber()); 1275 return null; 1276 } 1277 1278 /** {@inheritDoc} */ 1279 @Override setCollectTestsOnly(boolean shouldCollectTest)1280 public void setCollectTestsOnly(boolean shouldCollectTest) { 1281 mCollectTestsOnly = shouldCollectTest; 1282 } 1283 1284 @Override setAbi(IAbi abi)1285 public void setAbi(IAbi abi) { 1286 mAbi = abi; 1287 } 1288 1289 @Override getAbi()1290 public IAbi getAbi() { 1291 return mAbi; 1292 } 1293 1294 @Override setMetricCollectors(List<IMetricCollector> collectors)1295 public void setMetricCollectors(List<IMetricCollector> collectors) { 1296 mCollectors = collectors; 1297 } 1298 1299 /** Set True if we enforce the AJUR output format of instrumentation. */ setEnforceFormat(boolean enforce)1300 public void setEnforceFormat(boolean enforce) { 1301 mShouldEnforceFormat = enforce; 1302 } 1303 1304 /** 1305 * Set the instrumentation debug setting. 1306 * 1307 * @param debug boolean value to set the instrumentation debug setting to. 1308 */ setDebug(boolean debug)1309 public void setDebug(boolean debug) { 1310 mDebug = debug; 1311 } 1312 1313 /** 1314 * Get the instrumentation debug setting. 1315 * 1316 * @return The boolean debug setting. 1317 */ getDebug()1318 public boolean getDebug() { 1319 return mDebug; 1320 } 1321 1322 /** Set wether or not to use the isolated storage. */ setIsolatedStorage(boolean isolatedStorage)1323 public void setIsolatedStorage(boolean isolatedStorage) { 1324 mIsolatedStorage = isolatedStorage; 1325 } 1326 } 1327