• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package com.android.tradefed.testtype.suite;
17 
18 import com.android.annotations.VisibleForTesting;
19 import com.android.ddmlib.Log.LogLevel;
20 import com.android.tradefed.build.IBuildInfo;
21 import com.android.tradefed.config.ConfigurationException;
22 import com.android.tradefed.config.IConfiguration;
23 import com.android.tradefed.config.IConfigurationReceiver;
24 import com.android.tradefed.config.IDeviceConfiguration;
25 import com.android.tradefed.config.Option;
26 import com.android.tradefed.config.Option.Importance;
27 import com.android.tradefed.config.OptionCopier;
28 import com.android.tradefed.device.DeviceNotAvailableException;
29 import com.android.tradefed.device.ITestDevice;
30 import com.android.tradefed.device.NullDevice;
31 import com.android.tradefed.device.StubDevice;
32 import com.android.tradefed.device.cloud.NestedRemoteDevice;
33 import com.android.tradefed.device.metric.CollectorHelper;
34 import com.android.tradefed.device.metric.IMetricCollector;
35 import com.android.tradefed.device.metric.IMetricCollectorReceiver;
36 import com.android.tradefed.invoker.IInvocationContext;
37 import com.android.tradefed.invoker.shard.token.ITokenRequest;
38 import com.android.tradefed.invoker.shard.token.TokenProperty;
39 import com.android.tradefed.log.ITestLogger;
40 import com.android.tradefed.log.LogUtil.CLog;
41 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
42 import com.android.tradefed.result.ITestInvocationListener;
43 import com.android.tradefed.result.ITestLoggerReceiver;
44 import com.android.tradefed.result.ResultForwarder;
45 import com.android.tradefed.suite.checker.ISystemStatusChecker;
46 import com.android.tradefed.suite.checker.ISystemStatusCheckerReceiver;
47 import com.android.tradefed.suite.checker.StatusCheckerResult;
48 import com.android.tradefed.suite.checker.StatusCheckerResult.CheckStatus;
49 import com.android.tradefed.targetprep.ITargetPreparer;
50 import com.android.tradefed.testtype.Abi;
51 import com.android.tradefed.testtype.IAbi;
52 import com.android.tradefed.testtype.IBuildReceiver;
53 import com.android.tradefed.testtype.IDeviceTest;
54 import com.android.tradefed.testtype.IInvocationContextReceiver;
55 import com.android.tradefed.testtype.IMultiDeviceTest;
56 import com.android.tradefed.testtype.IRemoteTest;
57 import com.android.tradefed.testtype.IReportNotExecuted;
58 import com.android.tradefed.testtype.IRuntimeHintProvider;
59 import com.android.tradefed.testtype.IShardableTest;
60 import com.android.tradefed.testtype.ITestCollector;
61 import com.android.tradefed.util.AbiFormatter;
62 import com.android.tradefed.util.AbiUtils;
63 import com.android.tradefed.util.MultiMap;
64 import com.android.tradefed.util.TimeUtil;
65 
66 import com.google.inject.Inject;
67 import com.google.inject.Injector;
68 
69 import java.util.ArrayList;
70 import java.util.Arrays;
71 import java.util.Collection;
72 import java.util.Collections;
73 import java.util.HashMap;
74 import java.util.HashSet;
75 import java.util.Iterator;
76 import java.util.LinkedHashMap;
77 import java.util.LinkedHashSet;
78 import java.util.List;
79 import java.util.Map;
80 import java.util.Map.Entry;
81 import java.util.Random;
82 import java.util.Set;
83 
84 /**
85  * Abstract class used to run Test Suite. This class provide the base of how the Suite will be run.
86  * Each implementation can define the list of tests via the {@link #loadTests()} method.
87  */
88 public abstract class ITestSuite
89         implements IRemoteTest,
90                 IDeviceTest,
91                 IMultiDeviceTest,
92                 IBuildReceiver,
93                 ISystemStatusCheckerReceiver,
94                 IShardableTest,
95                 ITestCollector,
96                 IInvocationContextReceiver,
97                 IRuntimeHintProvider,
98                 IMetricCollectorReceiver,
99                 IConfigurationReceiver,
100                 IReportNotExecuted,
101                 ITokenRequest {
102 
103     /** The Retry Strategy to be used when re-running some tests. */
104     public enum RetryStrategy {
105         /** Rerun all the tests for the number of attempts specified. */
106         ITERATIONS,
107         /**
108          * Rerun all the tests until the max count is reached or a failure occurs whichever come
109          * first.
110          */
111         RERUN_UNTIL_FAILURE,
112         /**
113          * Rerun all the test case failures until passed or the max number of attempts specified.
114          */
115         RETRY_TEST_CASE_FAILURE,
116         /** Rerun all the test run failures until passed or the max number of attempts specified. */
117         RETRY_TEST_RUN_FAILURE,
118         /**
119          * Rerun all the test run and test cases failures until passed or the max number of attempts
120          * specified. Test run failures are rerun in priority (a.k.a. if a run failure and a test
121          * case failure occur, the run failure is rerun).
122          */
123         RETRY_ANY_FAILURE,
124     }
125 
126     public static final String SKIP_SYSTEM_STATUS_CHECKER = "skip-system-status-check";
127     public static final String RUNNER_WHITELIST = "runner-whitelist";
128     public static final String PREPARER_WHITELIST = "preparer-whitelist";
129     public static final String MODULE_CHECKER_PRE = "PreModuleChecker";
130     public static final String MODULE_CHECKER_POST = "PostModuleChecker";
131     public static final String ABI_OPTION = "abi";
132     public static final String SKIP_HOST_ARCH_CHECK = "skip-host-arch-check";
133     public static final String PRIMARY_ABI_RUN = "primary-abi-only";
134     public static final String PARAMETER_KEY = "parameter";
135     public static final String TOKEN_KEY = "token";
136     public static final String MODULE_METADATA_INCLUDE_FILTER = "module-metadata-include-filter";
137     public static final String MODULE_METADATA_EXCLUDE_FILTER = "module-metadata-exclude-filter";
138 
139     private static final String PRODUCT_CPU_ABI_KEY = "ro.product.cpu.abi";
140 
141     // Options for test failure case
142     @Option(
143         name = "bugreport-on-failure",
144         description =
145                 "Take a bugreport on every test failure. Warning: This may require a lot"
146                         + "of storage space of the machine running the tests."
147     )
148     private boolean mBugReportOnFailure = false;
149 
150     @Deprecated
151     @Option(
152         name = "logcat-on-failure",
153         description = "Take a logcat snapshot on every test failure."
154     )
155     private boolean mLogcatOnFailure = false;
156 
157     @Deprecated
158     @Option(
159         name = "logcat-on-failure-size",
160         description =
161                 "The max number of logcat data in bytes to capture when "
162                         + "--logcat-on-failure is on. Should be an amount that can comfortably fit in memory."
163     )
164     private int mMaxLogcatBytes = 500 * 1024; // 500K
165 
166     @Deprecated
167     @Option(
168         name = "screenshot-on-failure",
169         description = "Take a screenshot on every test failure."
170     )
171     private boolean mScreenshotOnFailure = false;
172 
173     @Option(name = "reboot-on-failure",
174             description = "Reboot the device after every test failure.")
175     private boolean mRebootOnFailure = false;
176 
177     // Options for suite runner behavior
178     @Option(name = "reboot-per-module", description = "Reboot the device before every module run.")
179     private boolean mRebootPerModule = false;
180 
181     @Option(name = "reboot-at-last-retry",
182         description = "Reboot the device at the last intra-module retry")
183     private boolean mRebootAtLastRetry = false;
184 
185     @Option(name = "skip-all-system-status-check",
186             description = "Whether all system status check between modules should be skipped")
187     private boolean mSkipAllSystemStatusCheck = false;
188 
189     @Option(
190         name = SKIP_SYSTEM_STATUS_CHECKER,
191         description =
192                 "Disable specific system status checkers."
193                         + "Specify zero or more SystemStatusChecker as canonical class names. e.g. "
194                         + "\"com.android.tradefed.suite.checker.KeyguardStatusChecker\" If not "
195                         + "specified, all configured or whitelisted system status checkers will "
196                         + "run."
197     )
198     private Set<String> mSystemStatusCheckBlacklist = new HashSet<>();
199 
200     @Option(
201         name = "report-system-checkers",
202         description = "Whether reporting system checkers as test or not."
203     )
204     private boolean mReportSystemChecker = false;
205 
206     @Deprecated
207     @Option(
208         name = "random-order",
209         description = "Whether randomizing the order of the modules to be ran or not."
210     )
211     private boolean mRandomOrder = false;
212 
213     @Deprecated
214     @Option(
215         name = "random-seed",
216         description = "Seed to randomize the order of the modules."
217     )
218     private long mRandomSeed = -1;
219 
220     @Option(
221         name = "collect-tests-only",
222         description =
223                 "Only invoke the suite to collect list of applicable test cases. All "
224                         + "test run callbacks will be triggered, but test execution will not be "
225                         + "actually carried out."
226     )
227     private boolean mCollectTestsOnly = false;
228 
229     // Abi related options
230     @Option(
231         name = ABI_OPTION,
232         shortName = 'a',
233         description = "the abi to test. For example: 'arm64-v8a'.",
234         importance = Importance.IF_UNSET
235     )
236     private String mAbiName = null;
237 
238     @Option(
239         name = SKIP_HOST_ARCH_CHECK,
240         description = "Whether host architecture check should be skipped."
241     )
242     private boolean mSkipHostArchCheck = false;
243 
244     @Option(
245         name = PRIMARY_ABI_RUN,
246         description =
247                 "Whether to run tests with only the device primary abi. "
248                         + "This is overriden by the --abi option."
249     )
250     private boolean mPrimaryAbiRun = false;
251 
252     @Option(
253         name = MODULE_METADATA_INCLUDE_FILTER,
254         description =
255                 "Include modules for execution based on matching of metadata fields: for any of "
256                         + "the specified filter name and value, if a module has a metadata field "
257                         + "with the same name and value, it will be included. When both module "
258                         + "inclusion and exclusion rules are applied, inclusion rules will be "
259                         + "evaluated first. Using this together with test filter inclusion rules "
260                         + "may result in no tests to execute if the rules don't overlap."
261     )
262     private MultiMap<String, String> mModuleMetadataIncludeFilter = new MultiMap<>();
263 
264     @Option(
265         name = MODULE_METADATA_EXCLUDE_FILTER,
266         description =
267                 "Exclude modules for execution based on matching of metadata fields: for any of "
268                         + "the specified filter name and value, if a module has a metadata field "
269                         + "with the same name and value, it will be excluded. When both module "
270                         + "inclusion and exclusion rules are applied, inclusion rules will be "
271                         + "evaluated first."
272     )
273     private MultiMap<String, String> mModuleMetadataExcludeFilter = new MultiMap<>();
274 
275     @Option(name = RUNNER_WHITELIST, description = "Runner class(es) that are allowed to run.")
276     private Set<String> mAllowedRunners = new HashSet<>();
277 
278     @Option(
279         name = PREPARER_WHITELIST,
280         description =
281                 "Preparer class(es) that are allowed to run. This mostly usefeul for dry-runs."
282     )
283     private Set<String> mAllowedPreparers = new HashSet<>();
284 
285     @Option(
286         name = "intra-module-sharding",
287         description = "Whether or not to allow intra-module sharding."
288     )
289     private boolean mIntraModuleSharding = true;
290 
291     @Option(
292         name = "isolated-module",
293         description = "Whether or not to attempt the module isolation between modules"
294     )
295     private boolean mIsolatedModule = false;
296 
297     @Option(
298         name = "reboot-before-test",
299         description = "Reboot the device before the test suite starts."
300     )
301     private boolean mRebootBeforeTest = false;
302 
303     // [Options relate to module retry and intra-module retry][
304     @Option(
305         name = "max-testcase-run-count",
306         description =
307                 "If the IRemoteTest can have its testcases run multiple times, "
308                         + "the max number of runs for each testcase."
309     )
310     private int mMaxRunLimit = 1;
311 
312     @Option(
313         name = "retry-strategy",
314         description =
315                 "The retry strategy to be used when re-running some tests with "
316                         + "--max-testcase-run-count"
317     )
318     private RetryStrategy mRetryStrategy = RetryStrategy.RETRY_TEST_CASE_FAILURE;
319 
320     @Option(
321         name = "merge-attempts",
322         description = "Whether or not to use the merge the results of the different attempts."
323     )
324     private boolean mMergeAttempts = true;
325     // end [Options relate to module retry and intra-module retry]
326 
327     private ITestDevice mDevice;
328     private IBuildInfo mBuildInfo;
329     private Map<ITestDevice, IBuildInfo> mDeviceInfos;
330     private List<ISystemStatusChecker> mSystemStatusCheckers;
331     private IInvocationContext mContext;
332     private List<IMetricCollector> mMetricCollectors;
333     private IConfiguration mMainConfiguration;
334 
335     // Sharding attributes
336     private boolean mIsSharded = false;
337     private ModuleDefinition mDirectModule = null;
338     private boolean mShouldMakeDynamicModule = true;
339 
340     // Guice object
341     private Injector mInjector;
342 
343     // Current modules to run, null if not started to run yet.
344     private List<ModuleDefinition> mRunModules = null;
345 
346     /**
347      * Get the current Guice {@link Injector} from the invocation. It should allow us to continue
348      * the object injection of modules.
349      */
350     @Inject
setInvocationInjector(Injector injector)351     public void setInvocationInjector(Injector injector) {
352         mInjector = injector;
353     }
354 
355     /** Forward our invocation scope guice objects to whoever needs them in modules. */
applyGuiceInjection(LinkedHashMap<String, IConfiguration> runConfig)356     private void applyGuiceInjection(LinkedHashMap<String, IConfiguration> runConfig) {
357         if (mInjector == null) {
358             // TODO: Convert to a strong failure
359             CLog.d("No injector received by the suite.");
360             return;
361         }
362         for (IConfiguration config : runConfig.values()) {
363             for (IRemoteTest test : config.getTests()) {
364                 mInjector.injectMembers(test);
365             }
366         }
367     }
368 
369     /**
370      * Abstract method to load the tests configuration that will be run. Each tests is defined by a
371      * {@link IConfiguration} and a unique name under which it will report results.
372      */
loadTests()373     public abstract LinkedHashMap<String, IConfiguration> loadTests();
374 
375     /**
376      * Return an instance of the class implementing {@link ITestSuite}.
377      */
createInstance()378     private ITestSuite createInstance() {
379         try {
380             return this.getClass().newInstance();
381         } catch (InstantiationException | IllegalAccessException e) {
382             throw new RuntimeException(e);
383         }
384     }
385 
loadAndFilter()386     private LinkedHashMap<String, IConfiguration> loadAndFilter() {
387         LinkedHashMap<String, IConfiguration> runConfig = loadTests();
388         if (runConfig.isEmpty()) {
389             CLog.i("No config were loaded. Nothing to run.");
390             return runConfig;
391         }
392         // Apply our guice scope to all modules objects
393         applyGuiceInjection(runConfig);
394 
395         LinkedHashMap<String, IConfiguration> filteredConfig = new LinkedHashMap<>();
396         for (Entry<String, IConfiguration> config : runConfig.entrySet()) {
397             if (!mModuleMetadataIncludeFilter.isEmpty()
398                     || !mModuleMetadataExcludeFilter.isEmpty()) {
399                 if (!filterByConfigMetadata(
400                         config.getValue(),
401                         mModuleMetadataIncludeFilter,
402                         mModuleMetadataExcludeFilter)) {
403                     // if the module config did not pass the metadata filters, it's excluded
404                     // from execution.
405                     continue;
406                 }
407             }
408             if (!filterByRunnerType(config.getValue(), mAllowedRunners)) {
409                 // if the module config did not pass the runner type filter, it's excluded from
410                 // execution.
411                 continue;
412             }
413             filterPreparers(config.getValue(), mAllowedPreparers);
414             filteredConfig.put(config.getKey(), config.getValue());
415         }
416         runConfig.clear();
417         return filteredConfig;
418     }
419 
420     /** Helper that creates and returns the list of {@link ModuleDefinition} to be executed. */
createExecutionList()421     private List<ModuleDefinition> createExecutionList() {
422         List<ModuleDefinition> runModules = new ArrayList<>();
423         if (mDirectModule != null) {
424             // If we are sharded and already know what to run then we just do it.
425             runModules.add(mDirectModule);
426             mDirectModule.setDevice(mDevice);
427             mDirectModule.setDeviceInfos(mDeviceInfos);
428             mDirectModule.setBuild(mBuildInfo);
429             return runModules;
430         }
431 
432         LinkedHashMap<String, IConfiguration> runConfig = loadAndFilter();
433         if (runConfig.isEmpty()) {
434             CLog.i("No config were loaded. Nothing to run.");
435             return runModules;
436         }
437 
438         for (Entry<String, IConfiguration> config : runConfig.entrySet()) {
439             // Validate the configuration, it will throw if not valid.
440             ValidateSuiteConfigHelper.validateConfig(config.getValue());
441             Map<String, List<ITargetPreparer>> preparersPerDevice =
442                     getPreparerPerDevice(config.getValue());
443             ModuleDefinition module =
444                     new ModuleDefinition(
445                             config.getKey(),
446                             config.getValue().getTests(),
447                             preparersPerDevice,
448                             config.getValue().getMultiTargetPreparers(),
449                             config.getValue());
450             module.setDevice(mDevice);
451             module.setDeviceInfos(mDeviceInfos);
452             module.setBuild(mBuildInfo);
453             runModules.add(module);
454         }
455 
456         /** Randomize all the modules to be ran if random-order is set and no sharding.*/
457         if (mRandomOrder) {
458             randomizeTestModules(runModules, mRandomSeed);
459         }
460 
461         CLog.logAndDisplay(LogLevel.DEBUG, "[Total Unique Modules = %s]", runModules.size());
462         // Free the map once we are done with it.
463         runConfig = null;
464         return runModules;
465     }
466 
467     /**
468      * Helper method that handle randomizing the order of the modules.
469      *
470      * @param runModules The {@code List<ModuleDefinition>} of the test modules to be ran.
471      * @param randomSeed The {@code long} seed used to randomize the order of test modules, use the
472      *     current time as seed if no specified seed provided.
473      */
474     @VisibleForTesting
randomizeTestModules(List<ModuleDefinition> runModules, long randomSeed)475     void randomizeTestModules(List<ModuleDefinition> runModules, long randomSeed) {
476         // Use current time as seed if no specified seed provided.
477         if (randomSeed == -1) {
478             randomSeed = System.currentTimeMillis();
479         }
480         CLog.i("Randomizing all the modules with seed: %s", randomSeed);
481         Collections.shuffle(runModules, new Random(randomSeed));
482     }
483 
checkClassLoad(Set<String> classes, String type)484     private void checkClassLoad(Set<String> classes, String type) {
485         for (String c : classes) {
486             try {
487                 Class.forName(c);
488             } catch (ClassNotFoundException e) {
489                 ConfigurationException ex =
490                         new ConfigurationException(
491                                 String.format(
492                                         "--%s must contains valid class, %s was not found",
493                                         type, c),
494                                 e);
495                 throw new RuntimeException(ex);
496             }
497         }
498     }
499 
500     /** Create the mapping of device to its target_preparer. */
getPreparerPerDevice(IConfiguration config)501     private Map<String, List<ITargetPreparer>> getPreparerPerDevice(IConfiguration config) {
502         Map<String, List<ITargetPreparer>> res = new LinkedHashMap<>();
503         for (IDeviceConfiguration holder : config.getDeviceConfig()) {
504             List<ITargetPreparer> preparers = new ArrayList<>();
505             res.put(holder.getDeviceName(), preparers);
506             preparers.addAll(holder.getTargetPreparers());
507         }
508         return res;
509     }
510 
511     /**
512      * Opportunity to clean up all the things that were needed during the suites setup but are not
513      * required to run the tests.
514      */
cleanUpSuiteSetup()515     void cleanUpSuiteSetup() {
516         // Empty by default.
517     }
518 
519     /** Generic run method for all test loaded from {@link #loadTests()}. */
520     @Override
run(ITestInvocationListener listener)521     public final void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
522         // Load and check the module checkers, runners and preparers in black and whitelist
523         checkClassLoad(mSystemStatusCheckBlacklist, SKIP_SYSTEM_STATUS_CHECKER);
524         checkClassLoad(mAllowedRunners, RUNNER_WHITELIST);
525         checkClassLoad(mAllowedPreparers, PREPARER_WHITELIST);
526 
527         mRunModules = createExecutionList();
528         // Check if we have something to run.
529         if (mRunModules.isEmpty()) {
530             CLog.i("No tests to be run.");
531             return;
532         }
533 
534         // Allow checkers to log files for easier debugging.
535         for (ISystemStatusChecker checker : mSystemStatusCheckers) {
536             if (checker instanceof ITestLoggerReceiver) {
537                 ((ITestLoggerReceiver) checker).setTestLogger(listener);
538             }
539         }
540 
541         // If requested reboot each device before the testing starts.
542         if (mRebootBeforeTest) {
543             for (ITestDevice device : mContext.getDevices()) {
544                 if (!(device.getIDevice() instanceof StubDevice)) {
545                     CLog.d(
546                             "Rebooting device '%s' before test starts as requested.",
547                             device.getSerialNumber());
548                     mDevice.reboot();
549                 }
550             }
551         }
552 
553         /** Setup a special listener to take actions on test failures. */
554         TestFailureListener failureListener =
555                 new TestFailureListener(
556                         mContext.getDevices(), mBugReportOnFailure, mRebootOnFailure);
557         /** Create the list of listeners applicable at the module level. */
558         List<ITestInvocationListener> moduleListeners = createModuleListeners();
559 
560         // Only print the running log if we are going to run something.
561         if (mRunModules.get(0).hasTests()) {
562             CLog.logAndDisplay(
563                     LogLevel.INFO,
564                     "%s running %s modules: %s",
565                     mDevice.getSerialNumber(),
566                     mRunModules.size(),
567                     mRunModules);
568         }
569 
570         /** Run all the module, make sure to reduce the list to release resources as we go. */
571         try {
572             while (!mRunModules.isEmpty()) {
573                 ModuleDefinition module = mRunModules.remove(0);
574                 // Before running the module we ensure it has tests at this point or skip completely
575                 // to avoid running SystemCheckers and preparation for nothing.
576                 if (module.hasTests()) {
577                     continue;
578                 }
579 
580                 // Populate the module context with devices and builds
581                 for (String deviceName : mContext.getDeviceConfigNames()) {
582                     module.getModuleInvocationContext()
583                             .addAllocatedDevice(deviceName, mContext.getDevice(deviceName));
584                     module.getModuleInvocationContext()
585                             .addDeviceBuildInfo(deviceName, mContext.getBuildInfo(deviceName));
586                 }
587                 listener.testModuleStarted(module.getModuleInvocationContext());
588                 // Trigger module start on module level listener too
589                 new ResultForwarder(moduleListeners)
590                         .testModuleStarted(module.getModuleInvocationContext());
591                 try {
592                     runSingleModule(module, listener, moduleListeners, failureListener);
593                 } finally {
594                     // Trigger module end on module level listener too
595                     new ResultForwarder(moduleListeners).testModuleEnded();
596                     // clear out module invocation context since we are now done with module
597                     // execution
598                     listener.testModuleEnded();
599                 }
600                 // Module isolation routine
601                 moduleIsolation(mContext, listener);
602             }
603         } catch (DeviceNotAvailableException e) {
604             CLog.e(
605                     "A DeviceNotAvailableException occurred, following modules did not run: %s",
606                     mRunModules);
607             reportNotExecuted(listener, "Module did not run due to device not available.");
608             throw e;
609         }
610     }
611 
612     /**
613      * Returns the list of {@link ITestInvocationListener} applicable to the {@link ModuleListener}
614      * level. These listeners will be re-used for each module, they will not be re-instantiated so
615      * they should not assume an internal state.
616      */
createModuleListeners()617     protected List<ITestInvocationListener> createModuleListeners() {
618         return new ArrayList<>();
619     }
620 
621     /**
622      * Routine that attempt to reset a device between modules in order to provide isolation.
623      *
624      * @param context The invocation context.
625      * @param logger A logger where extra logs can be saved.
626      * @throws DeviceNotAvailableException
627      */
moduleIsolation(IInvocationContext context, ITestLogger logger)628     private void moduleIsolation(IInvocationContext context, ITestLogger logger)
629             throws DeviceNotAvailableException {
630         // TODO: we can probably make it smarter: Did any test ran for example?
631         ITestDevice device = context.getDevices().get(0);
632         if (mIsolatedModule && (device instanceof NestedRemoteDevice)) {
633             boolean res =
634                     ((NestedRemoteDevice) device)
635                             .resetVirtualDevice(
636                                     logger,
637                                     context.getBuildInfos().get(0),
638                                     /* Do not collect the logs */ false);
639             if (!res) {
640                 String serial = device.getSerialNumber();
641                 throw new DeviceNotAvailableException(
642                         String.format(
643                                 "Failed to reset the AVD '%s' during module isolation.", serial),
644                         serial);
645             }
646         }
647     }
648 
649     /**
650      * Helper method that handle running a single module logic.
651      *
652      * @param module The {@link ModuleDefinition} to be ran.
653      * @param listener The {@link ITestInvocationListener} where to report results
654      * @param moduleListeners The {@link ITestInvocationListener}s that runs at the module level.
655      * @param failureListener special listener that we add to collect information on failures.
656      * @throws DeviceNotAvailableException
657      */
runSingleModule( ModuleDefinition module, ITestInvocationListener listener, List<ITestInvocationListener> moduleListeners, TestFailureListener failureListener)658     private void runSingleModule(
659             ModuleDefinition module,
660             ITestInvocationListener listener,
661             List<ITestInvocationListener> moduleListeners,
662             TestFailureListener failureListener)
663             throws DeviceNotAvailableException {
664         if (mRebootPerModule) {
665             if ("user".equals(mDevice.getProperty("ro.build.type"))) {
666                 CLog.e(
667                         "reboot-per-module should only be used during development, "
668                                 + "this is a\" user\" build device");
669             } else {
670                 CLog.d("Rebooting device before starting next module");
671                 mDevice.reboot();
672             }
673         }
674 
675         if (!mSkipAllSystemStatusCheck) {
676             runPreModuleCheck(module.getId(), mSystemStatusCheckers, mDevice, listener);
677         }
678         if (mCollectTestsOnly) {
679             module.setCollectTestsOnly(mCollectTestsOnly);
680         }
681         // Pass the run defined collectors to be used.
682         module.setMetricCollectors(CollectorHelper.cloneCollectors(mMetricCollectors));
683         // Pass the main invocation logSaver
684         module.setLogSaver(mMainConfiguration.getLogSaver());
685         // Pass the retry strategy to the module
686         module.setRetryStrategy(mRetryStrategy, mMergeAttempts);
687         // Pass the reboot strategy at the last intra-module retry to the module
688         module.setRebootAtLastRetry(mRebootAtLastRetry);
689 
690         // Actually run the module
691         module.run(listener, moduleListeners, failureListener, mMaxRunLimit);
692 
693         if (!mSkipAllSystemStatusCheck) {
694             runPostModuleCheck(module.getId(), mSystemStatusCheckers, mDevice, listener);
695         }
696     }
697 
698     /**
699      * Helper to run the System Status checkers preExecutionChecks defined for the test and log
700      * their failures.
701      */
runPreModuleCheck( String moduleName, List<ISystemStatusChecker> checkers, ITestDevice device, ITestInvocationListener listener)702     private void runPreModuleCheck(
703             String moduleName,
704             List<ISystemStatusChecker> checkers,
705             ITestDevice device,
706             ITestInvocationListener listener)
707             throws DeviceNotAvailableException {
708         long startTime = System.currentTimeMillis();
709         CLog.i("Running system status checker before module execution: %s", moduleName);
710         Map<String, String> failures = new LinkedHashMap<>();
711         boolean bugreportNeeded = false;
712         for (ISystemStatusChecker checker : checkers) {
713             // Check if the status checker should be skipped.
714             if (mSystemStatusCheckBlacklist.contains(checker.getClass().getName())) {
715                 CLog.d(
716                         "%s was skipped via %s",
717                         checker.getClass().getName(), SKIP_SYSTEM_STATUS_CHECKER);
718                 continue;
719             }
720 
721             StatusCheckerResult result = new StatusCheckerResult(CheckStatus.FAILED);
722             try {
723                 result = checker.preExecutionCheck(device);
724             } catch (RuntimeException e) {
725                 // Catch RuntimeException to avoid leaking throws that go to the invocation.
726                 result.setErrorMessage(e.getMessage());
727                 result.setBugreportNeeded(true);
728             }
729             if (!CheckStatus.SUCCESS.equals(result.getStatus())) {
730                 String errorMessage =
731                         (result.getErrorMessage() == null) ? "" : result.getErrorMessage();
732                 failures.put(checker.getClass().getCanonicalName(), errorMessage);
733                 bugreportNeeded = bugreportNeeded | result.isBugreportNeeded();
734                 CLog.w("System status checker [%s] failed.", checker.getClass().getCanonicalName());
735             }
736         }
737         if (!failures.isEmpty()) {
738             CLog.w("There are failed system status checkers: %s", failures.toString());
739             if (bugreportNeeded && !(device.getIDevice() instanceof StubDevice)) {
740                 device.logBugreport(
741                         String.format("bugreport-checker-pre-module-%s", moduleName), listener);
742             }
743         }
744 
745         // We report System checkers like tests.
746         reportModuleCheckerResult(MODULE_CHECKER_PRE, moduleName, failures, startTime, listener);
747     }
748 
749     /**
750      * Helper to run the System Status checkers postExecutionCheck defined for the test and log
751      * their failures.
752      */
runPostModuleCheck( String moduleName, List<ISystemStatusChecker> checkers, ITestDevice device, ITestInvocationListener listener)753     private void runPostModuleCheck(
754             String moduleName,
755             List<ISystemStatusChecker> checkers,
756             ITestDevice device,
757             ITestInvocationListener listener)
758             throws DeviceNotAvailableException {
759         long startTime = System.currentTimeMillis();
760         CLog.i("Running system status checker after module execution: %s", moduleName);
761         Map<String, String> failures = new LinkedHashMap<>();
762         boolean bugreportNeeded = false;
763         for (ISystemStatusChecker checker : checkers) {
764             // Check if the status checker should be skipped.
765             if (mSystemStatusCheckBlacklist.contains(checker.getClass().getName())) {
766                 continue;
767             }
768 
769             StatusCheckerResult result = new StatusCheckerResult(CheckStatus.FAILED);
770             try {
771                 result = checker.postExecutionCheck(device);
772             } catch (RuntimeException e) {
773                 // Catch RuntimeException to avoid leaking throws that go to the invocation.
774                 result.setErrorMessage(e.getMessage());
775                 result.setBugreportNeeded(true);
776             }
777             if (!CheckStatus.SUCCESS.equals(result.getStatus())) {
778                 String errorMessage =
779                         (result.getErrorMessage() == null) ? "" : result.getErrorMessage();
780                 failures.put(checker.getClass().getCanonicalName(), errorMessage);
781                 bugreportNeeded = bugreportNeeded | result.isBugreportNeeded();
782                 CLog.w("System status checker [%s] failed", checker.getClass().getCanonicalName());
783             }
784         }
785         if (!failures.isEmpty()) {
786             CLog.w("There are failed system status checkers: %s", failures.toString());
787             if (bugreportNeeded && !(device.getIDevice() instanceof StubDevice)) {
788                 device.logBugreport(
789                         String.format("bugreport-checker-post-module-%s", moduleName), listener);
790             }
791         }
792 
793         // We report System checkers like tests.
794         reportModuleCheckerResult(MODULE_CHECKER_POST, moduleName, failures, startTime, listener);
795     }
796 
797     /** Helper to report status checker results as test results. */
reportModuleCheckerResult( String identifier, String moduleName, Map<String, String> failures, long startTime, ITestInvocationListener listener)798     private void reportModuleCheckerResult(
799             String identifier,
800             String moduleName,
801             Map<String, String> failures,
802             long startTime,
803             ITestInvocationListener listener) {
804         if (!mReportSystemChecker) {
805             // do not log here, otherwise it could be very verbose.
806             return;
807         }
808         // Avoid messing with the final test count by making them empty runs.
809         listener.testRunStarted(identifier + "_" + moduleName, 0, 0, System.currentTimeMillis());
810         if (!failures.isEmpty()) {
811             listener.testRunFailed(String.format("%s failed '%s' checkers", moduleName, failures));
812         }
813         listener.testRunEnded(
814                 System.currentTimeMillis() - startTime, new HashMap<String, Metric>());
815     }
816 
817     /** {@inheritDoc} */
818     @Override
split(int shardCountHint)819     public Collection<IRemoteTest> split(int shardCountHint) {
820         if (shardCountHint <= 1 || mIsSharded) {
821             // cannot shard or already sharded
822             return null;
823         }
824 
825         LinkedHashMap<String, IConfiguration> runConfig = loadAndFilter();
826         if (runConfig.isEmpty()) {
827             CLog.i("No config were loaded. Nothing to run.");
828             return null;
829         }
830         injectInfo(runConfig);
831 
832         // We split individual tests on double the shardCountHint to provide better average.
833         // The test pool mechanism prevent this from creating too much overhead.
834         List<ModuleDefinition> splitModules =
835                 ModuleSplitter.splitConfiguration(
836                         runConfig, shardCountHint, mShouldMakeDynamicModule, mIntraModuleSharding);
837         runConfig.clear();
838         runConfig = null;
839 
840         // Clean up the parent that will get sharded: It is fine to clean up before copying the
841         // options, because the sharded module is already created/populated so there is no need
842         // to carry these extra data.
843         cleanUpSuiteSetup();
844 
845         // create an association of one ITestSuite <=> one ModuleDefinition as the smallest
846         // execution unit supported.
847         List<IRemoteTest> splitTests = new ArrayList<>();
848         for (ModuleDefinition m : splitModules) {
849             ITestSuite suite = createInstance();
850             OptionCopier.copyOptionsNoThrow(this, suite);
851             suite.mIsSharded = true;
852             suite.mDirectModule = m;
853             splitTests.add(suite);
854         }
855         // return the list of ITestSuite with their ModuleDefinition assigned
856         return splitTests;
857     }
858 
859     /**
860      * Inject {@link ITestDevice} and {@link IBuildInfo} to the {@link IRemoteTest}s in the config
861      * before sharding since they may be needed.
862      */
injectInfo(LinkedHashMap<String, IConfiguration> runConfig)863     private void injectInfo(LinkedHashMap<String, IConfiguration> runConfig) {
864         for (IConfiguration config : runConfig.values()) {
865             for (IRemoteTest test : config.getTests()) {
866                 if (test instanceof IBuildReceiver) {
867                     ((IBuildReceiver) test).setBuild(mBuildInfo);
868                 }
869                 if (test instanceof IDeviceTest) {
870                     ((IDeviceTest) test).setDevice(mDevice);
871                 }
872                 if (test instanceof IMultiDeviceTest) {
873                     ((IMultiDeviceTest) test).setDeviceInfos(mDeviceInfos);
874                 }
875                 if (test instanceof IInvocationContextReceiver) {
876                     ((IInvocationContextReceiver) test).setInvocationContext(mContext);
877                 }
878                 if (test instanceof ITestCollector) {
879                     ((ITestCollector) test).setCollectTestsOnly(mCollectTestsOnly);
880                 }
881             }
882         }
883     }
884 
885     /** {@inheritDoc} */
886     @Override
setDevice(ITestDevice device)887     public void setDevice(ITestDevice device) {
888         mDevice = device;
889     }
890 
891     /**
892      * {@inheritDoc}
893      */
894     @Override
getDevice()895     public ITestDevice getDevice() {
896         return mDevice;
897     }
898 
899     /** Set the value of mAbiName */
setAbiName(String abiName)900     public void setAbiName(String abiName) {
901         mAbiName = abiName;
902     }
903 
904     /**
905      * {@inheritDoc}
906      */
907     @Override
setBuild(IBuildInfo buildInfo)908     public void setBuild(IBuildInfo buildInfo) {
909         mBuildInfo = buildInfo;
910     }
911 
912     /**
913      * Implementation of {@link ITestSuite} may require the build info to load the tests.
914      */
getBuildInfo()915     public IBuildInfo getBuildInfo() {
916         return mBuildInfo;
917     }
918 
919     /** {@inheritDoc} */
920     @Override
setDeviceInfos(Map<ITestDevice, IBuildInfo> deviceInfos)921     public void setDeviceInfos(Map<ITestDevice, IBuildInfo> deviceInfos) {
922         mDeviceInfos = deviceInfos;
923     }
924 
925     /** Set the value of mPrimaryAbiRun */
setPrimaryAbiRun(boolean primaryAbiRun)926     public void setPrimaryAbiRun(boolean primaryAbiRun) {
927         mPrimaryAbiRun = primaryAbiRun;
928     }
929 
930     /**
931      * {@inheritDoc}
932      */
933     @Override
setSystemStatusChecker(List<ISystemStatusChecker> systemCheckers)934     public void setSystemStatusChecker(List<ISystemStatusChecker> systemCheckers) {
935         mSystemStatusCheckers = systemCheckers;
936     }
937 
938     /**
939      * Run the test suite in collector only mode, this requires all the sub-tests to implements this
940      * interface too.
941      */
942     @Override
setCollectTestsOnly(boolean shouldCollectTest)943     public void setCollectTestsOnly(boolean shouldCollectTest) {
944         mCollectTestsOnly = shouldCollectTest;
945     }
946 
947     /** {@inheritDoc} */
948     @Override
setMetricCollectors(List<IMetricCollector> collectors)949     public void setMetricCollectors(List<IMetricCollector> collectors) {
950         mMetricCollectors = collectors;
951     }
952 
953     /**
954      * When doing distributed sharding, we cannot have ModuleDefinition that shares tests in a pool
955      * otherwise intra-module sharding will not work, so we allow to disable it.
956      */
setShouldMakeDynamicModule(boolean dynamicModule)957     public void setShouldMakeDynamicModule(boolean dynamicModule) {
958         mShouldMakeDynamicModule = dynamicModule;
959     }
960 
961     /** {@inheritDoc} */
962     @Override
setInvocationContext(IInvocationContext invocationContext)963     public void setInvocationContext(IInvocationContext invocationContext) {
964         mContext = invocationContext;
965     }
966 
967     /** Set the max number of run attempt for each module. */
setMaxRunLimit(int maxRunLimit)968     public final void setMaxRunLimit(int maxRunLimit) {
969         mMaxRunLimit = maxRunLimit;
970     }
971 
972     /** {@inheritDoc} */
973     @Override
getRuntimeHint()974     public long getRuntimeHint() {
975         if (mDirectModule != null) {
976             CLog.d(
977                     "    %s: %s",
978                     mDirectModule.getId(),
979                     TimeUtil.formatElapsedTime(mDirectModule.getRuntimeHint()));
980             return mDirectModule.getRuntimeHint();
981         }
982         return 0l;
983     }
984 
985     /** {@inheritDoc} */
986     @Override
setConfiguration(IConfiguration configuration)987     public void setConfiguration(IConfiguration configuration) {
988         mMainConfiguration = configuration;
989     }
990 
991     /** {@inheritDoc} */
992     @Override
reportNotExecuted(ITestInvocationListener listener)993     public void reportNotExecuted(ITestInvocationListener listener) {
994         reportNotExecuted(listener, IReportNotExecuted.NOT_EXECUTED_FAILURE);
995     }
996 
997     /** {@inheritDoc} */
998     @Override
reportNotExecuted(ITestInvocationListener listener, String message)999     public void reportNotExecuted(ITestInvocationListener listener, String message) {
1000         // If the runner is already in progress, report the remaining tests as not executed.
1001         List<ModuleDefinition> runModules = null;
1002         if (mRunModules != null) {
1003             runModules = new ArrayList<>(mRunModules);
1004         }
1005         if (runModules == null) {
1006             runModules = createExecutionList();
1007         }
1008 
1009         while (!runModules.isEmpty()) {
1010             ModuleDefinition module = runModules.remove(0);
1011             module.reportNotExecuted(listener, message);
1012         }
1013     }
1014 
addModuleMetadataIncludeFilters(MultiMap<String, String> filters)1015     public void addModuleMetadataIncludeFilters(MultiMap<String, String> filters) {
1016         mModuleMetadataIncludeFilter.putAll(filters);
1017     }
1018 
addModuleMetadataExcludeFilters(MultiMap<String, String> filters)1019     public void addModuleMetadataExcludeFilters(MultiMap<String, String> filters) {
1020         mModuleMetadataExcludeFilter.putAll(filters);
1021     }
1022 
1023     /**
1024      * Returns the {@link ModuleDefinition} to be executed directly, or null if none yet (when the
1025      * ITestSuite has not been sharded yet).
1026      */
getDirectModule()1027     public ModuleDefinition getDirectModule() {
1028         return mDirectModule;
1029     }
1030 
1031     @Override
getRequiredTokens()1032     public Set<TokenProperty> getRequiredTokens() {
1033         if (mDirectModule == null) {
1034             return null;
1035         }
1036         return mDirectModule.getRequiredTokens();
1037     }
1038 
1039     /**
1040      * Gets the set of ABIs supported by both Compatibility testing {@link
1041      * AbiUtils#getAbisSupportedByCompatibility()} and the device under test.
1042      *
1043      * @return The set of ABIs to run the tests on
1044      * @throws DeviceNotAvailableException
1045      */
getAbis(ITestDevice device)1046     public Set<IAbi> getAbis(ITestDevice device) throws DeviceNotAvailableException {
1047         Set<IAbi> abis = new LinkedHashSet<>();
1048         Set<String> archAbis = getAbisForBuildTargetArch();
1049         // Handle null-device: use abi in common with host and suite build
1050         if (mPrimaryAbiRun) {
1051             if (mAbiName == null) {
1052                 // Get the primary from the device and make it the --abi to run.
1053                 mAbiName = getPrimaryAbi(device);
1054             } else {
1055                 CLog.d(
1056                         "Option --%s supersedes the option --%s, using abi: %s",
1057                         ABI_OPTION, PRIMARY_ABI_RUN, mAbiName);
1058             }
1059         }
1060         if (mAbiName != null) {
1061             // A particular abi was requested, it still needs to be supported by the build.
1062             if ((!mSkipHostArchCheck && !archAbis.contains(mAbiName))
1063                     || !AbiUtils.isAbiSupportedByCompatibility(mAbiName)) {
1064                 throw new IllegalArgumentException(
1065                         String.format(
1066                                 "Your tests suite hasn't been built with "
1067                                         + "abi '%s' support, this suite currently supports '%s'.",
1068                                 mAbiName, archAbis));
1069             } else {
1070                 abis.add(new Abi(mAbiName, AbiUtils.getBitness(mAbiName)));
1071                 return abis;
1072             }
1073         } else {
1074             // Run on all abi in common between the device and suite builds.
1075             List<String> deviceAbis = getDeviceAbis(device);
1076             if (deviceAbis.isEmpty()) {
1077                 throw new IllegalArgumentException(
1078                         String.format(
1079                                 "Couldn't determinate the abi of the device '%s'.",
1080                                 device.getSerialNumber()));
1081             }
1082             for (String abi : deviceAbis) {
1083                 if ((mSkipHostArchCheck || archAbis.contains(abi))
1084                         && AbiUtils.isAbiSupportedByCompatibility(abi)) {
1085                     abis.add(new Abi(abi, AbiUtils.getBitness(abi)));
1086                 } else {
1087                     CLog.d(
1088                             "abi '%s' is supported by device but not by this suite build (%s), "
1089                                     + "tests will not run against it.",
1090                             abi, archAbis);
1091                 }
1092             }
1093             if (abis.isEmpty()) {
1094                 throw new IllegalArgumentException(
1095                         String.format(
1096                                 "None of the abi supported by this tests suite build ('%s') are "
1097                                         + "supported by the device ('%s').",
1098                                 archAbis, deviceAbis));
1099             }
1100             return abis;
1101         }
1102     }
1103 
1104     /** Returns the primary abi of the device or host if it's a null device. */
getPrimaryAbi(ITestDevice device)1105     private String getPrimaryAbi(ITestDevice device) throws DeviceNotAvailableException {
1106         if (device.getIDevice() instanceof NullDevice) {
1107             Set<String> hostAbis = getHostAbis();
1108             return hostAbis.iterator().next();
1109         }
1110         String property = device.getProperty(PRODUCT_CPU_ABI_KEY);
1111         if (property == null) {
1112             String serial = device.getSerialNumber();
1113             throw new DeviceNotAvailableException(
1114                     String.format(
1115                             "Device '%s' was not online to query %s", serial, PRODUCT_CPU_ABI_KEY),
1116                     serial);
1117         }
1118         return property.trim();
1119     }
1120 
1121     /** Returns the list of abis supported by the device or host if it's a null device. */
getDeviceAbis(ITestDevice device)1122     private List<String> getDeviceAbis(ITestDevice device) throws DeviceNotAvailableException {
1123         if (device.getIDevice() instanceof NullDevice) {
1124             return new ArrayList<>(getHostAbis());
1125         }
1126         // Make it an arrayList to be able to modify the content.
1127         return new ArrayList<>(Arrays.asList(AbiFormatter.getSupportedAbis(device, "")));
1128     }
1129 
1130     /** Return the abis supported by the Host build target architecture. Exposed for testing. */
1131     @VisibleForTesting
getAbisForBuildTargetArch()1132     protected Set<String> getAbisForBuildTargetArch() {
1133         // If TestSuiteInfo does not exists, the stub arch will be replaced by all possible abis.
1134         Set<String> abis = new LinkedHashSet<>();
1135         for (String arch : TestSuiteInfo.getInstance().getTargetArchs()) {
1136             abis.addAll(AbiUtils.getAbisForArch(arch));
1137         }
1138         return abis;
1139     }
1140 
1141     /** Returns the host machine abis. */
1142     @VisibleForTesting
getHostAbis()1143     protected Set<String> getHostAbis() {
1144         return AbiUtils.getHostAbi();
1145     }
1146 
1147     /** Returns the abi requested with the option -a or --abi. */
getRequestedAbi()1148     public final String getRequestedAbi() {
1149         return mAbiName;
1150     }
1151 
1152     /** Getter used to validate the proper Guice injection. */
1153     @VisibleForTesting
getInjector()1154     final Injector getInjector() {
1155         return mInjector;
1156     }
1157 
1158     /**
1159      * Apply the metadata filter to the config and see if the config should run.
1160      *
1161      * @param config The {@link IConfiguration} being evaluated.
1162      * @param include the metadata include filter
1163      * @param exclude the metadata exclude filter
1164      * @return True if the module should run, false otherwise.
1165      */
1166     @VisibleForTesting
filterByConfigMetadata( IConfiguration config, MultiMap<String, String> include, MultiMap<String, String> exclude)1167     protected boolean filterByConfigMetadata(
1168             IConfiguration config,
1169             MultiMap<String, String> include,
1170             MultiMap<String, String> exclude) {
1171         MultiMap<String, String> metadata = config.getConfigurationDescription().getAllMetaData();
1172         boolean shouldInclude = false;
1173         for (String key : include.keySet()) {
1174             Set<String> filters = new HashSet<>(include.get(key));
1175             if (metadata.containsKey(key)) {
1176                 filters.retainAll(metadata.get(key));
1177                 if (!filters.isEmpty()) {
1178                     // inclusion filter is not empty and there's at least one matching inclusion
1179                     // rule so there's no need to match other inclusion rules
1180                     shouldInclude = true;
1181                     break;
1182                 }
1183             }
1184         }
1185         if (!include.isEmpty() && !shouldInclude) {
1186             // if inclusion filter is not empty and we didn't find a match, the module will not be
1187             // included
1188             return false;
1189         }
1190         // Now evaluate exclusion rules, this ordering also means that exclusion rules may override
1191         // inclusion rules: a config already matched for inclusion may still be excluded if matching
1192         // rules exist
1193         for (String key : exclude.keySet()) {
1194             Set<String> filters = new HashSet<>(exclude.get(key));
1195             if (metadata.containsKey(key)) {
1196                 filters.retainAll(metadata.get(key));
1197                 if (!filters.isEmpty()) {
1198                     // we found at least one matching exclusion rules, so we are excluding this
1199                     // this module
1200                     return false;
1201                 }
1202             }
1203         }
1204         // we've matched at least one inclusion rule (if there's any) AND we didn't match any of the
1205         // exclusion rules (if there's any)
1206         return true;
1207     }
1208 
1209     /**
1210      * Filter out the preparers that were not whitelisted. This is useful for collect-tests-only
1211      * where some preparers are not needed to dry run through the invocation.
1212      *
1213      * @param config the {@link IConfiguration} considered for filtering.
1214      * @param preparerWhiteList the current preparer whitelist.
1215      */
1216     @VisibleForTesting
filterPreparers(IConfiguration config, Set<String> preparerWhiteList)1217     void filterPreparers(IConfiguration config, Set<String> preparerWhiteList) {
1218         // If no filters was provided, skip the filtering.
1219         if (preparerWhiteList.isEmpty()) {
1220             return;
1221         }
1222         for (IDeviceConfiguration deviceConfig : config.getDeviceConfig()) {
1223             List<ITargetPreparer> preparers = new ArrayList<>(deviceConfig.getTargetPreparers());
1224             for (ITargetPreparer prep : preparers) {
1225                 if (!preparerWhiteList.contains(prep.getClass().getName())) {
1226                     deviceConfig.getTargetPreparers().remove(prep);
1227                 }
1228             }
1229         }
1230     }
1231 
1232     /**
1233      * Apply the Runner whitelist filtering, removing any runner that was not whitelisted. If a
1234      * configuration has several runners, some might be removed and the config will still run.
1235      *
1236      * @param config The {@link IConfiguration} being evaluated.
1237      * @param allowedRunners The current runner whitelist.
1238      * @return True if the configuration module is allowed to run, false otherwise.
1239      */
1240     @VisibleForTesting
filterByRunnerType(IConfiguration config, Set<String> allowedRunners)1241     protected boolean filterByRunnerType(IConfiguration config, Set<String> allowedRunners) {
1242         // If no filters are provided, simply run everything.
1243         if (allowedRunners.isEmpty()) {
1244             return true;
1245         }
1246         Iterator<IRemoteTest> iterator = config.getTests().iterator();
1247         while (iterator.hasNext()) {
1248             IRemoteTest test = iterator.next();
1249             if (!allowedRunners.contains(test.getClass().getName())) {
1250                 CLog.d(
1251                         "Runner '%s' in module '%s' was skipped by the runner whitelist: '%s'.",
1252                         test.getClass().getName(), config.getName(), allowedRunners);
1253                 iterator.remove();
1254             }
1255         }
1256 
1257         if (config.getTests().isEmpty()) {
1258             CLog.d("Module %s does not have any more tests, skipping it.", config.getName());
1259             return false;
1260         }
1261         return true;
1262     }
1263 }
1264