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