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