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