• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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