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.StubDevice;
31 import com.android.tradefed.device.metric.IMetricCollector;
32 import com.android.tradefed.device.metric.IMetricCollectorReceiver;
33 import com.android.tradefed.invoker.IInvocationContext;
34 import com.android.tradefed.log.LogUtil.CLog;
35 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
36 import com.android.tradefed.result.ITestInvocationListener;
37 import com.android.tradefed.result.ITestLoggerReceiver;
38 import com.android.tradefed.result.InputStreamSource;
39 import com.android.tradefed.result.LogDataType;
40 import com.android.tradefed.result.ResultForwarder;
41 import com.android.tradefed.suite.checker.ISystemStatusChecker;
42 import com.android.tradefed.suite.checker.ISystemStatusCheckerReceiver;
43 import com.android.tradefed.suite.checker.StatusCheckerResult;
44 import com.android.tradefed.suite.checker.StatusCheckerResult.CheckStatus;
45 import com.android.tradefed.targetprep.ITargetPreparer;
46 import com.android.tradefed.testtype.Abi;
47 import com.android.tradefed.testtype.IAbi;
48 import com.android.tradefed.testtype.IBuildReceiver;
49 import com.android.tradefed.testtype.IDeviceTest;
50 import com.android.tradefed.testtype.IInvocationContextReceiver;
51 import com.android.tradefed.testtype.IMultiDeviceTest;
52 import com.android.tradefed.testtype.IRemoteTest;
53 import com.android.tradefed.testtype.IRuntimeHintProvider;
54 import com.android.tradefed.testtype.IShardableTest;
55 import com.android.tradefed.testtype.ITestCollector;
56 import com.android.tradefed.util.AbiFormatter;
57 import com.android.tradefed.util.AbiUtils;
58 import com.android.tradefed.util.MultiMap;
59 import com.android.tradefed.util.TimeUtil;
60 
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.Collection;
64 import java.util.HashMap;
65 import java.util.HashSet;
66 import java.util.Iterator;
67 import java.util.LinkedHashMap;
68 import java.util.LinkedHashSet;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.Map.Entry;
72 import java.util.Set;
73 
74 /**
75  * Abstract class used to run Test Suite. This class provide the base of how the Suite will be run.
76  * Each implementation can define the list of tests via the {@link #loadTests()} method.
77  */
78 public abstract class ITestSuite
79         implements IRemoteTest,
80                 IDeviceTest,
81                 IMultiDeviceTest,
82                 IBuildReceiver,
83                 ISystemStatusCheckerReceiver,
84                 IShardableTest,
85                 ITestCollector,
86                 IInvocationContextReceiver,
87                 IRuntimeHintProvider,
88                 IMetricCollectorReceiver,
89                 IConfigurationReceiver {
90 
91     public static final String SKIP_SYSTEM_STATUS_CHECKER = "skip-system-status-check";
92     public static final String RUNNER_WHITELIST = "runner-whitelist";
93     public static final String PREPARER_WHITELIST = "preparer-whitelist";
94     public static final String MODULE_CHECKER_PRE = "PreModuleChecker";
95     public static final String MODULE_CHECKER_POST = "PostModuleChecker";
96     public static final String ABI_OPTION = "abi";
97     public static final String SKIP_HOST_ARCH_CHECK = "skip-host-arch-check";
98     public static final String PRIMARY_ABI_RUN = "primary-abi-only";
99     private static final String PRODUCT_CPU_ABI_KEY = "ro.product.cpu.abi";
100 
101     // Options for test failure case
102     @Option(
103         name = "bugreport-on-failure",
104         description =
105                 "Take a bugreport on every test failure. Warning: This may require a lot"
106                         + "of storage space of the machine running the tests."
107     )
108     private boolean mBugReportOnFailure = false;
109 
110     @Option(name = "logcat-on-failure",
111             description = "Take a logcat snapshot on every test failure.")
112     private boolean mLogcatOnFailure = false;
113 
114     @Option(name = "logcat-on-failure-size",
115             description = "The max number of logcat data in bytes to capture when "
116             + "--logcat-on-failure is on. Should be an amount that can comfortably fit in memory.")
117     private int mMaxLogcatBytes = 500 * 1024; // 500K
118 
119     @Option(name = "screenshot-on-failure",
120             description = "Take a screenshot on every test failure.")
121     private boolean mScreenshotOnFailure = false;
122 
123     @Option(name = "reboot-on-failure",
124             description = "Reboot the device after every test failure.")
125     private boolean mRebootOnFailure = false;
126 
127     // Options for suite runner behavior
128     @Option(name = "reboot-per-module", description = "Reboot the device before every module run.")
129     private boolean mRebootPerModule = false;
130 
131     @Option(name = "skip-all-system-status-check",
132             description = "Whether all system status check between modules should be skipped")
133     private boolean mSkipAllSystemStatusCheck = false;
134 
135     @Option(
136         name = SKIP_SYSTEM_STATUS_CHECKER,
137         description =
138                 "Disable specific system status checkers."
139                         + "Specify zero or more SystemStatusChecker as canonical class names. e.g. "
140                         + "\"com.android.tradefed.suite.checker.KeyguardStatusChecker\" If not "
141                         + "specified, all configured or whitelisted system status checkers will "
142                         + "run."
143     )
144     private Set<String> mSystemStatusCheckBlacklist = new HashSet<>();
145 
146     @Option(
147         name = "report-system-checkers",
148         description = "Whether reporting system checkers as test or not."
149     )
150     private boolean mReportSystemChecker = false;
151 
152     @Option(
153         name = "collect-tests-only",
154         description =
155                 "Only invoke the suite to collect list of applicable test cases. All "
156                         + "test run callbacks will be triggered, but test execution will not be "
157                         + "actually carried out."
158     )
159     private boolean mCollectTestsOnly = false;
160 
161     // Abi related options
162     @Option(
163         name = ABI_OPTION,
164         shortName = 'a',
165         description = "the abi to test. For example: 'arm64-v8a'.",
166         importance = Importance.IF_UNSET
167     )
168     private String mAbiName = null;
169 
170     @Option(
171         name = SKIP_HOST_ARCH_CHECK,
172         description = "Whether host architecture check should be skipped."
173     )
174     private boolean mSkipHostArchCheck = false;
175 
176     @Option(
177         name = PRIMARY_ABI_RUN,
178         description =
179                 "Whether to run tests with only the device primary abi. "
180                         + "This is overriden by the --abi option."
181     )
182     private boolean mPrimaryAbiRun = false;
183 
184     @Option(
185         name = "module-metadata-include-filter",
186         description =
187                 "Include modules for execution based on matching of metadata fields: for any of "
188                         + "the specified filter name and value, if a module has a metadata field "
189                         + "with the same name and value, it will be included. When both module "
190                         + "inclusion and exclusion rules are applied, inclusion rules will be "
191                         + "evaluated first. Using this together with test filter inclusion rules "
192                         + "may result in no tests to execute if the rules don't overlap."
193     )
194     private MultiMap<String, String> mModuleMetadataIncludeFilter = new MultiMap<>();
195 
196     @Option(
197         name = "module-metadata-exclude-filter",
198         description =
199                 "Exclude modules for execution based on matching of metadata fields: for any of "
200                         + "the specified filter name and value, if a module has a metadata field "
201                         + "with the same name and value, it will be excluded. When both module "
202                         + "inclusion and exclusion rules are applied, inclusion rules will be "
203                         + "evaluated first."
204     )
205     private MultiMap<String, String> mModuleMetadataExcludeFilter = new MultiMap<>();
206 
207     @Option(name = RUNNER_WHITELIST, description = "Runner class(es) that are allowed to run.")
208     private Set<String> mAllowedRunners = new HashSet<>();
209 
210     @Option(
211         name = PREPARER_WHITELIST,
212         description =
213                 "Preparer class(es) that are allowed to run. This mostly usefeul for dry-runs."
214     )
215     private Set<String> mAllowedPreparers = new HashSet<>();
216 
217     @Option(
218         name = "reboot-before-test",
219         description = "Reboot the device before the test suite starts."
220     )
221     private boolean mRebootBeforeTest = false;
222 
223     @Option(
224         name = "max-testcase-run-count",
225         description =
226                 "If the IRemoteTest can have its testcases run multiple times, "
227                         + "the max number of runs for each testcase."
228     )
229     private int mMaxRunLimit = 1;
230 
231     private ITestDevice mDevice;
232     private IBuildInfo mBuildInfo;
233     private Map<ITestDevice, IBuildInfo> mDeviceInfos;
234     private List<ISystemStatusChecker> mSystemStatusCheckers;
235     private IInvocationContext mContext;
236     private List<IMetricCollector> mMetricCollectors;
237     private IConfiguration mMainConfiguration;
238 
239     // Sharding attributes
240     private boolean mIsSharded = false;
241     private ModuleDefinition mDirectModule = null;
242     private boolean mShouldMakeDynamicModule = true;
243 
244     /**
245      * Abstract method to load the tests configuration that will be run. Each tests is defined by a
246      * {@link IConfiguration} and a unique name under which it will report results.
247      */
loadTests()248     public abstract LinkedHashMap<String, IConfiguration> loadTests();
249 
250     /**
251      * Return an instance of the class implementing {@link ITestSuite}.
252      */
createInstance()253     private ITestSuite createInstance() {
254         try {
255             return this.getClass().newInstance();
256         } catch (InstantiationException | IllegalAccessException e) {
257             throw new RuntimeException(e);
258         }
259     }
260 
loadAndFilter()261     private LinkedHashMap<String, IConfiguration> loadAndFilter() {
262         LinkedHashMap<String, IConfiguration> runConfig = loadTests();
263         if (runConfig.isEmpty()) {
264             CLog.i("No config were loaded. Nothing to run.");
265             return runConfig;
266         }
267         if (mModuleMetadataIncludeFilter.isEmpty() && mModuleMetadataExcludeFilter.isEmpty()) {
268             return runConfig;
269         }
270         LinkedHashMap<String, IConfiguration> filteredConfig = new LinkedHashMap<>();
271         for (Entry<String, IConfiguration> config : runConfig.entrySet()) {
272             if (!filterByConfigMetadata(
273                     config.getValue(),
274                     mModuleMetadataIncludeFilter,
275                     mModuleMetadataExcludeFilter)) {
276                 // if the module config did not pass the metadata filters, it's excluded
277                 // from execution.
278                 continue;
279             }
280             if (!filterByRunnerType(config.getValue(), mAllowedRunners)) {
281                 // if the module config did not pass the runner type filter, it's excluded from
282                 // execution.
283                 continue;
284             }
285             filterPreparers(config.getValue(), mAllowedPreparers);
286             filteredConfig.put(config.getKey(), config.getValue());
287         }
288         runConfig.clear();
289         return filteredConfig;
290     }
291 
292     /** Helper that creates and returns the list of {@link ModuleDefinition} to be executed. */
createExecutionList()293     private List<ModuleDefinition> createExecutionList() {
294         List<ModuleDefinition> runModules = new ArrayList<>();
295         if (mDirectModule != null) {
296             // If we are sharded and already know what to run then we just do it.
297             runModules.add(mDirectModule);
298             mDirectModule.setDevice(mDevice);
299             mDirectModule.setDeviceInfos(mDeviceInfos);
300             mDirectModule.setBuild(mBuildInfo);
301             return runModules;
302         }
303 
304         LinkedHashMap<String, IConfiguration> runConfig = loadAndFilter();
305         if (runConfig.isEmpty()) {
306             CLog.i("No config were loaded. Nothing to run.");
307             return runModules;
308         }
309 
310         for (Entry<String, IConfiguration> config : runConfig.entrySet()) {
311             // Validate the configuration, it will throw if not valid.
312             ValidateSuiteConfigHelper.validateConfig(config.getValue());
313             Map<String, List<ITargetPreparer>> preparersPerDevice =
314                     getPreparerPerDevice(config.getValue());
315             ModuleDefinition module =
316                     new ModuleDefinition(
317                             config.getKey(),
318                             config.getValue().getTests(),
319                             preparersPerDevice,
320                             config.getValue().getMultiTargetPreparers(),
321                             config.getValue());
322             module.setDevice(mDevice);
323             module.setDeviceInfos(mDeviceInfos);
324             module.setBuild(mBuildInfo);
325             runModules.add(module);
326         }
327         // Free the map once we are done with it.
328         runConfig = null;
329         return runModules;
330     }
331 
checkClassLoad(Set<String> classes, String type)332     private void checkClassLoad(Set<String> classes, String type) {
333         for (String c : classes) {
334             try {
335                 Class.forName(c);
336             } catch (ClassNotFoundException e) {
337                 ConfigurationException ex =
338                         new ConfigurationException(
339                                 String.format(
340                                         "--%s must contains valid class, %s was not found",
341                                         type, c),
342                                 e);
343                 throw new RuntimeException(ex);
344             }
345         }
346     }
347 
348     /** Create the mapping of device to its target_preparer. */
getPreparerPerDevice(IConfiguration config)349     private Map<String, List<ITargetPreparer>> getPreparerPerDevice(IConfiguration config) {
350         Map<String, List<ITargetPreparer>> res = new LinkedHashMap<>();
351         for (IDeviceConfiguration holder : config.getDeviceConfig()) {
352             List<ITargetPreparer> preparers = new ArrayList<>();
353             res.put(holder.getDeviceName(), preparers);
354             preparers.addAll(holder.getTargetPreparers());
355         }
356         return res;
357     }
358 
359     /** Generic run method for all test loaded from {@link #loadTests()}. */
360     @Override
run(ITestInvocationListener listener)361     public final void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
362         // Load and check the module checkers, runners and preparers in black and whitelist
363         checkClassLoad(mSystemStatusCheckBlacklist, SKIP_SYSTEM_STATUS_CHECKER);
364         checkClassLoad(mAllowedRunners, RUNNER_WHITELIST);
365         checkClassLoad(mAllowedPreparers, PREPARER_WHITELIST);
366 
367         List<ModuleDefinition> runModules = createExecutionList();
368         // Check if we have something to run.
369         if (runModules.isEmpty()) {
370             CLog.i("No tests to be run.");
371             return;
372         }
373 
374         // Allow checkers to log files for easier debugging.
375         for (ISystemStatusChecker checker : mSystemStatusCheckers) {
376             if (checker instanceof ITestLoggerReceiver) {
377                 ((ITestLoggerReceiver) checker).setTestLogger(listener);
378             }
379         }
380 
381         // If requested reboot each device before the testing starts.
382         if (mRebootBeforeTest) {
383             for (ITestDevice device : mContext.getDevices()) {
384                 if (!(device.getIDevice() instanceof StubDevice)) {
385                     CLog.d(
386                             "Rebooting device '%s' before test starts as requested.",
387                             device.getSerialNumber());
388                     mDevice.reboot();
389                 }
390             }
391         }
392 
393         /** Setup a special listener to take actions on test failures. */
394         TestFailureListener failureListener =
395                 new TestFailureListener(
396                         mContext.getDevices(),
397                         mBugReportOnFailure,
398                         mLogcatOnFailure,
399                         mScreenshotOnFailure,
400                         mRebootOnFailure,
401                         mMaxLogcatBytes);
402         /** Create the list of listeners applicable at the module level. */
403         List<ITestInvocationListener> moduleListeners = createModuleListeners();
404 
405         // Only print the running log if we are going to run something.
406         if (runModules.get(0).hasTests()) {
407             CLog.logAndDisplay(
408                     LogLevel.INFO,
409                     "%s running %s modules: %s",
410                     mDevice.getSerialNumber(),
411                     runModules.size(),
412                     runModules);
413         }
414 
415         /** Run all the module, make sure to reduce the list to release resources as we go. */
416         try {
417             while (!runModules.isEmpty()) {
418                 ModuleDefinition module = runModules.remove(0);
419                 // Before running the module we ensure it has tests at this point or skip completely
420                 // to avoid running SystemCheckers and preparation for nothing.
421                 if (module.hasTests()) {
422                     continue;
423                 }
424 
425                 try {
426                     // Populate the module context with devices and builds
427                     for (String deviceName : mContext.getDeviceConfigNames()) {
428                         module.getModuleInvocationContext()
429                                 .addAllocatedDevice(deviceName, mContext.getDevice(deviceName));
430                         module.getModuleInvocationContext()
431                                 .addDeviceBuildInfo(deviceName, mContext.getBuildInfo(deviceName));
432                     }
433                     listener.testModuleStarted(module.getModuleInvocationContext());
434                     // Trigger module start on module level listener too
435                     new ResultForwarder(moduleListeners)
436                             .testModuleStarted(module.getModuleInvocationContext());
437 
438                     runSingleModule(module, listener, moduleListeners, failureListener);
439                 } finally {
440                     // Trigger module end on module level listener too
441                     new ResultForwarder(moduleListeners).testModuleEnded();
442                     // clear out module invocation context since we are now done with module
443                     // execution
444                     listener.testModuleEnded();
445                 }
446             }
447         } catch (DeviceNotAvailableException e) {
448             CLog.e(
449                     "A DeviceNotAvailableException occurred, following modules did not run: %s",
450                     runModules);
451             for (ModuleDefinition module : runModules) {
452                 listener.testRunStarted(module.getId(), 0);
453                 listener.testRunFailed("Module did not run due to device not available.");
454                 listener.testRunEnded(0, new HashMap<String, Metric>());
455             }
456             throw e;
457         }
458     }
459 
460     /**
461      * Returns the list of {@link ITestInvocationListener} applicable to the {@link ModuleListener}
462      * level. These listeners will be re-used for each module, they will not be re-instantiated so
463      * they should not assume an internal state.
464      */
createModuleListeners()465     protected List<ITestInvocationListener> createModuleListeners() {
466         return new ArrayList<>();
467     }
468 
469     /**
470      * Helper method that handle running a single module logic.
471      *
472      * @param module The {@link ModuleDefinition} to be ran.
473      * @param listener The {@link ITestInvocationListener} where to report results
474      * @param moduleListeners The {@link ITestInvocationListener}s that runs at the module level.
475      * @param failureListener special listener that we add to collect information on failures.
476      * @throws DeviceNotAvailableException
477      */
runSingleModule( ModuleDefinition module, ITestInvocationListener listener, List<ITestInvocationListener> moduleListeners, TestFailureListener failureListener)478     private void runSingleModule(
479             ModuleDefinition module,
480             ITestInvocationListener listener,
481             List<ITestInvocationListener> moduleListeners,
482             TestFailureListener failureListener)
483             throws DeviceNotAvailableException {
484         if (mRebootPerModule) {
485             if ("user".equals(mDevice.getProperty("ro.build.type"))) {
486                 CLog.e(
487                         "reboot-per-module should only be used during development, "
488                                 + "this is a\" user\" build device");
489             } else {
490                 CLog.d("Rebooting device before starting next module");
491                 mDevice.reboot();
492             }
493         }
494 
495         if (!mSkipAllSystemStatusCheck) {
496             runPreModuleCheck(module.getId(), mSystemStatusCheckers, mDevice, listener);
497         }
498         if (mCollectTestsOnly) {
499             module.setCollectTestsOnly(mCollectTestsOnly);
500         }
501         // Pass the run defined collectors to be used.
502         module.setMetricCollectors(mMetricCollectors);
503         // Pass the main invocation logSaver
504         module.setLogSaver(mMainConfiguration.getLogSaver());
505 
506         // Actually run the module
507         module.run(listener, moduleListeners, failureListener, mMaxRunLimit);
508 
509         if (!mSkipAllSystemStatusCheck) {
510             runPostModuleCheck(module.getId(), mSystemStatusCheckers, mDevice, listener);
511         }
512     }
513 
514     /**
515      * Helper to run the System Status checkers preExecutionChecks defined for the test and log
516      * their failures.
517      */
runPreModuleCheck( String moduleName, List<ISystemStatusChecker> checkers, ITestDevice device, ITestInvocationListener listener)518     private void runPreModuleCheck(
519             String moduleName,
520             List<ISystemStatusChecker> checkers,
521             ITestDevice device,
522             ITestInvocationListener listener)
523             throws DeviceNotAvailableException {
524         long startTime = System.currentTimeMillis();
525         CLog.i("Running system status checker before module execution: %s", moduleName);
526         Map<String, String> failures = new LinkedHashMap<>();
527         for (ISystemStatusChecker checker : checkers) {
528             // Check if the status checker should be skipped.
529             if (mSystemStatusCheckBlacklist.contains(checker.getClass().getName())) {
530                 CLog.d(
531                         "%s was skipped via %s",
532                         checker.getClass().getName(), SKIP_SYSTEM_STATUS_CHECKER);
533                 continue;
534             }
535 
536             StatusCheckerResult result = checker.preExecutionCheck(device);
537             if (!CheckStatus.SUCCESS.equals(result.getStatus())) {
538                 String errorMessage =
539                         (result.getErrorMessage() == null) ? "" : result.getErrorMessage();
540                 failures.put(checker.getClass().getCanonicalName(), errorMessage);
541                 CLog.w("System status checker [%s] failed.", checker.getClass().getCanonicalName());
542             }
543         }
544         if (!failures.isEmpty()) {
545             CLog.w("There are failed system status checkers: %s capturing a bugreport",
546                     failures.toString());
547             try (InputStreamSource bugSource = device.getBugreport()) {
548                 listener.testLog(
549                         String.format("bugreport-checker-pre-module-%s", moduleName),
550                         LogDataType.BUGREPORT,
551                         bugSource);
552             }
553         }
554 
555         // We report System checkers like tests.
556         reportModuleCheckerResult(MODULE_CHECKER_PRE, moduleName, failures, startTime, listener);
557     }
558 
559     /**
560      * Helper to run the System Status checkers postExecutionCheck defined for the test and log
561      * their failures.
562      */
runPostModuleCheck( String moduleName, List<ISystemStatusChecker> checkers, ITestDevice device, ITestInvocationListener listener)563     private void runPostModuleCheck(
564             String moduleName,
565             List<ISystemStatusChecker> checkers,
566             ITestDevice device,
567             ITestInvocationListener listener)
568             throws DeviceNotAvailableException {
569         long startTime = System.currentTimeMillis();
570         CLog.i("Running system status checker after module execution: %s", moduleName);
571         Map<String, String> failures = new LinkedHashMap<>();
572         for (ISystemStatusChecker checker : checkers) {
573             // Check if the status checker should be skipped.
574             if (mSystemStatusCheckBlacklist.contains(checker.getClass().getName())) {
575                 continue;
576             }
577 
578             StatusCheckerResult result = checker.postExecutionCheck(device);
579             if (!CheckStatus.SUCCESS.equals(result.getStatus())) {
580                 String errorMessage =
581                         (result.getErrorMessage() == null) ? "" : result.getErrorMessage();
582                 failures.put(checker.getClass().getCanonicalName(), errorMessage);
583                 CLog.w("System status checker [%s] failed", checker.getClass().getCanonicalName());
584             }
585         }
586         if (!failures.isEmpty()) {
587             CLog.w("There are failed system status checkers: %s capturing a bugreport",
588                     failures.toString());
589             try (InputStreamSource bugSource = device.getBugreport()) {
590                 listener.testLog(
591                         String.format("bugreport-checker-post-module-%s", moduleName),
592                         LogDataType.BUGREPORT,
593                         bugSource);
594             }
595         }
596 
597         // We report System checkers like tests.
598         reportModuleCheckerResult(MODULE_CHECKER_POST, moduleName, failures, startTime, listener);
599     }
600 
601     /** Helper to report status checker results as test results. */
reportModuleCheckerResult( String identifier, String moduleName, Map<String, String> failures, long startTime, ITestInvocationListener listener)602     private void reportModuleCheckerResult(
603             String identifier,
604             String moduleName,
605             Map<String, String> failures,
606             long startTime,
607             ITestInvocationListener listener) {
608         if (!mReportSystemChecker) {
609             // do not log here, otherwise it could be very verbose.
610             return;
611         }
612         // Avoid messing with the final test count by making them empty runs.
613         listener.testRunStarted(identifier + "_" + moduleName, 0);
614         if (!failures.isEmpty()) {
615             listener.testRunFailed(String.format("%s failed '%s' checkers", moduleName, failures));
616         }
617         listener.testRunEnded(
618                 System.currentTimeMillis() - startTime, new HashMap<String, Metric>());
619     }
620 
621     /** {@inheritDoc} */
622     @Override
split(int shardCountHint)623     public Collection<IRemoteTest> split(int shardCountHint) {
624         if (shardCountHint <= 1 || mIsSharded) {
625             // cannot shard or already sharded
626             return null;
627         }
628 
629         LinkedHashMap<String, IConfiguration> runConfig = loadAndFilter();
630         if (runConfig.isEmpty()) {
631             CLog.i("No config were loaded. Nothing to run.");
632             return null;
633         }
634         injectInfo(runConfig);
635 
636         // We split individual tests on double the shardCountHint to provide better average.
637         // The test pool mechanism prevent this from creating too much overhead.
638         List<ModuleDefinition> splitModules =
639                 ModuleSplitter.splitConfiguration(
640                         runConfig, shardCountHint, mShouldMakeDynamicModule);
641         runConfig.clear();
642         runConfig = null;
643         // create an association of one ITestSuite <=> one ModuleDefinition as the smallest
644         // execution unit supported.
645         List<IRemoteTest> splitTests = new ArrayList<>();
646         for (ModuleDefinition m : splitModules) {
647             ITestSuite suite = createInstance();
648             OptionCopier.copyOptionsNoThrow(this, suite);
649             suite.mIsSharded = true;
650             suite.mDirectModule = m;
651             splitTests.add(suite);
652         }
653         // return the list of ITestSuite with their ModuleDefinition assigned
654         return splitTests;
655     }
656 
657     /**
658      * Inject {@link ITestDevice} and {@link IBuildInfo} to the {@link IRemoteTest}s in the config
659      * before sharding since they may be needed.
660      */
injectInfo(LinkedHashMap<String, IConfiguration> runConfig)661     private void injectInfo(LinkedHashMap<String, IConfiguration> runConfig) {
662         for (IConfiguration config : runConfig.values()) {
663             for (IRemoteTest test : config.getTests()) {
664                 if (test instanceof IBuildReceiver) {
665                     ((IBuildReceiver) test).setBuild(mBuildInfo);
666                 }
667                 if (test instanceof IDeviceTest) {
668                     ((IDeviceTest) test).setDevice(mDevice);
669                 }
670                 if (test instanceof IMultiDeviceTest) {
671                     ((IMultiDeviceTest) test).setDeviceInfos(mDeviceInfos);
672                 }
673                 if (test instanceof IInvocationContextReceiver) {
674                     ((IInvocationContextReceiver) test).setInvocationContext(mContext);
675                 }
676             }
677         }
678     }
679 
680     /** {@inheritDoc} */
681     @Override
setDevice(ITestDevice device)682     public void setDevice(ITestDevice device) {
683         mDevice = device;
684     }
685 
686     /**
687      * {@inheritDoc}
688      */
689     @Override
getDevice()690     public ITestDevice getDevice() {
691         return mDevice;
692     }
693 
694     /** Set the value of mAbiName */
setAbiName(String abiName)695     public void setAbiName(String abiName) {
696         mAbiName = abiName;
697     }
698 
699     /**
700      * {@inheritDoc}
701      */
702     @Override
setBuild(IBuildInfo buildInfo)703     public void setBuild(IBuildInfo buildInfo) {
704         mBuildInfo = buildInfo;
705     }
706 
707     /**
708      * Implementation of {@link ITestSuite} may require the build info to load the tests.
709      */
getBuildInfo()710     public IBuildInfo getBuildInfo() {
711         return mBuildInfo;
712     }
713 
714     /** {@inheritDoc} */
715     @Override
setDeviceInfos(Map<ITestDevice, IBuildInfo> deviceInfos)716     public void setDeviceInfos(Map<ITestDevice, IBuildInfo> deviceInfos) {
717         mDeviceInfos = deviceInfos;
718     }
719 
720     /** Set the value of mPrimaryAbiRun */
setPrimaryAbiRun(boolean primaryAbiRun)721     public void setPrimaryAbiRun(boolean primaryAbiRun) {
722         mPrimaryAbiRun = primaryAbiRun;
723     }
724 
725     /**
726      * {@inheritDoc}
727      */
728     @Override
setSystemStatusChecker(List<ISystemStatusChecker> systemCheckers)729     public void setSystemStatusChecker(List<ISystemStatusChecker> systemCheckers) {
730         mSystemStatusCheckers = systemCheckers;
731     }
732 
733     /**
734      * Run the test suite in collector only mode, this requires all the sub-tests to implements this
735      * interface too.
736      */
737     @Override
setCollectTestsOnly(boolean shouldCollectTest)738     public void setCollectTestsOnly(boolean shouldCollectTest) {
739         mCollectTestsOnly = shouldCollectTest;
740     }
741 
742     /** {@inheritDoc} */
743     @Override
setMetricCollectors(List<IMetricCollector> collectors)744     public void setMetricCollectors(List<IMetricCollector> collectors) {
745         mMetricCollectors = collectors;
746     }
747 
748     /**
749      * When doing distributed sharding, we cannot have ModuleDefinition that shares tests in a pool
750      * otherwise intra-module sharding will not work, so we allow to disable it.
751      */
setShouldMakeDynamicModule(boolean dynamicModule)752     public void setShouldMakeDynamicModule(boolean dynamicModule) {
753         mShouldMakeDynamicModule = dynamicModule;
754     }
755 
756     /** {@inheritDoc} */
757     @Override
setInvocationContext(IInvocationContext invocationContext)758     public void setInvocationContext(IInvocationContext invocationContext) {
759         mContext = invocationContext;
760     }
761 
762     /** {@inheritDoc} */
763     @Override
getRuntimeHint()764     public long getRuntimeHint() {
765         if (mDirectModule != null) {
766             CLog.d(
767                     "    %s: %s",
768                     mDirectModule.getId(),
769                     TimeUtil.formatElapsedTime(mDirectModule.getRuntimeHint()));
770             return mDirectModule.getRuntimeHint();
771         }
772         return 0l;
773     }
774 
775     /** {@inheritDoc} */
776     @Override
setConfiguration(IConfiguration configuration)777     public void setConfiguration(IConfiguration configuration) {
778         mMainConfiguration = configuration;
779     }
780 
781     /**
782      * Returns the {@link ModuleDefinition} to be executed directly, or null if none yet (when the
783      * ITestSuite has not been sharded yet).
784      */
getDirectModule()785     public ModuleDefinition getDirectModule() {
786         return mDirectModule;
787     }
788 
789     /**
790      * Gets the set of ABIs supported by both Compatibility testing {@link
791      * AbiUtils#getAbisSupportedByCompatibility()} and the device under test.
792      *
793      * @return The set of ABIs to run the tests on
794      * @throws DeviceNotAvailableException
795      */
getAbis(ITestDevice device)796     public Set<IAbi> getAbis(ITestDevice device) throws DeviceNotAvailableException {
797         Set<IAbi> abis = new LinkedHashSet<>();
798         Set<String> archAbis = getAbisForBuildTargetArch();
799         if (mPrimaryAbiRun) {
800             if (mAbiName == null) {
801                 // Get the primary from the device and make it the --abi to run.
802                 mAbiName = device.getProperty(PRODUCT_CPU_ABI_KEY).trim();
803             } else {
804                 CLog.d(
805                         "Option --%s supersedes the option --%s, using abi: %s",
806                         ABI_OPTION, PRIMARY_ABI_RUN, mAbiName);
807             }
808         }
809         if (mAbiName != null) {
810             // A particular abi was requested, it still needs to be supported by the build.
811             if ((!mSkipHostArchCheck && !archAbis.contains(mAbiName))
812                     || !AbiUtils.isAbiSupportedByCompatibility(mAbiName)) {
813                 throw new IllegalArgumentException(
814                         String.format(
815                                 "Your tests suite hasn't been built with "
816                                         + "abi '%s' support, this suite currently supports '%s'.",
817                                 mAbiName, archAbis));
818             } else {
819                 abis.add(new Abi(mAbiName, AbiUtils.getBitness(mAbiName)));
820                 return abis;
821             }
822         } else {
823             // Run on all abi in common between the device and suite builds.
824             List<String> deviceAbis = Arrays.asList(AbiFormatter.getSupportedAbis(device, ""));
825             for (String abi : deviceAbis) {
826                 if ((mSkipHostArchCheck || archAbis.contains(abi))
827                         && AbiUtils.isAbiSupportedByCompatibility(abi)) {
828                     abis.add(new Abi(abi, AbiUtils.getBitness(abi)));
829                 } else {
830                     CLog.d(
831                             "abi '%s' is supported by device but not by this suite build (%s), "
832                                     + "tests will not run against it.",
833                             abi, archAbis);
834                 }
835             }
836             if (abis.isEmpty()) {
837                 throw new IllegalArgumentException(
838                         String.format(
839                                 "None of the abi supported by this tests suite build ('%s') are "
840                                         + "supported by the device ('%s').",
841                                 archAbis, deviceAbis));
842             }
843             return abis;
844         }
845     }
846 
847     /** Return the abis supported by the Host build target architecture. Exposed for testing. */
848     @VisibleForTesting
getAbisForBuildTargetArch()849     protected Set<String> getAbisForBuildTargetArch() {
850         // If TestSuiteInfo does not exists, the stub arch will be replaced by all possible abis.
851         return AbiUtils.getAbisForArch(TestSuiteInfo.getInstance().getTargetArch());
852     }
853 
854     /** Returns the abi requested with the option -a or --abi. */
getRequestedAbi()855     public final String getRequestedAbi() {
856         return mAbiName;
857     }
858 
859     /**
860      * Apply the metadata filter to the config and see if the config should run.
861      *
862      * @param config The {@link IConfiguration} being evaluated.
863      * @param include the metadata include filter
864      * @param exclude the metadata exclude filter
865      * @return True if the module should run, false otherwise.
866      */
867     @VisibleForTesting
filterByConfigMetadata( IConfiguration config, MultiMap<String, String> include, MultiMap<String, String> exclude)868     protected boolean filterByConfigMetadata(
869             IConfiguration config,
870             MultiMap<String, String> include,
871             MultiMap<String, String> exclude) {
872         MultiMap<String, String> metadata = config.getConfigurationDescription().getAllMetaData();
873         boolean shouldInclude = false;
874         for (String key : include.keySet()) {
875             Set<String> filters = new HashSet<>(include.get(key));
876             if (metadata.containsKey(key)) {
877                 filters.retainAll(metadata.get(key));
878                 if (!filters.isEmpty()) {
879                     // inclusion filter is not empty and there's at least one matching inclusion
880                     // rule so there's no need to match other inclusion rules
881                     shouldInclude = true;
882                     break;
883                 }
884             }
885         }
886         if (!include.isEmpty() && !shouldInclude) {
887             // if inclusion filter is not empty and we didn't find a match, the module will not be
888             // included
889             return false;
890         }
891         // Now evaluate exclusion rules, this ordering also means that exclusion rules may override
892         // inclusion rules: a config already matched for inclusion may still be excluded if matching
893         // rules exist
894         for (String key : exclude.keySet()) {
895             Set<String> filters = new HashSet<>(exclude.get(key));
896             if (metadata.containsKey(key)) {
897                 filters.retainAll(metadata.get(key));
898                 if (!filters.isEmpty()) {
899                     // we found at least one matching exclusion rules, so we are excluding this
900                     // this module
901                     return false;
902                 }
903             }
904         }
905         // we've matched at least one inclusion rule (if there's any) AND we didn't match any of the
906         // exclusion rules (if there's any)
907         return true;
908     }
909 
910     /**
911      * Filter out the preparers that were not whitelisted. This is useful for collect-tests-only
912      * where some preparers are not needed to dry run through the invocation.
913      *
914      * @param config the {@link IConfiguration} considered for filtering.
915      * @param preparerWhiteList the current preparer whitelist.
916      */
917     @VisibleForTesting
filterPreparers(IConfiguration config, Set<String> preparerWhiteList)918     void filterPreparers(IConfiguration config, Set<String> preparerWhiteList) {
919         // If no filters was provided, skip the filtering.
920         if (preparerWhiteList.isEmpty()) {
921             return;
922         }
923         List<ITargetPreparer> preparers = new ArrayList<>(config.getTargetPreparers());
924         for (ITargetPreparer prep : preparers) {
925             if (!preparerWhiteList.contains(prep.getClass().getName())) {
926                 config.getTargetPreparers().remove(prep);
927             }
928         }
929     }
930 
931     /**
932      * Apply the Runner whitelist filtering, removing any runner that was not whitelisted. If a
933      * configuration has several runners, some might be removed and the config will still run.
934      *
935      * @param config The {@link IConfiguration} being evaluated.
936      * @param allowedRunners The current runner whitelist.
937      * @return True if the configuration module is allowed to run, false otherwise.
938      */
939     @VisibleForTesting
filterByRunnerType(IConfiguration config, Set<String> allowedRunners)940     protected boolean filterByRunnerType(IConfiguration config, Set<String> allowedRunners) {
941         // If no filters are provided, simply run everything.
942         if (allowedRunners.isEmpty()) {
943             return true;
944         }
945         Iterator<IRemoteTest> iterator = config.getTests().iterator();
946         while (iterator.hasNext()) {
947             IRemoteTest test = iterator.next();
948             if (!allowedRunners.contains(test.getClass().getName())) {
949                 CLog.d(
950                         "Runner '%s' in module '%s' was skipped by the runner whitelist: '%s'.",
951                         test.getClass().getName(), config.getName(), allowedRunners);
952                 iterator.remove();
953             }
954         }
955 
956         if (config.getTests().isEmpty()) {
957             CLog.d("Module %s does not have any more tests, skipping it.", config.getName());
958             return false;
959         }
960         return true;
961     }
962 }
963