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.BuildRetrievalError;
21 import com.android.tradefed.build.IBuildInfo;
22 import com.android.tradefed.build.IDeviceBuildInfo;
23 import com.android.tradefed.config.Configuration;
24 import com.android.tradefed.config.ConfigurationException;
25 import com.android.tradefed.config.DynamicRemoteFileResolver;
26 import com.android.tradefed.config.IConfiguration;
27 import com.android.tradefed.config.IConfigurationReceiver;
28 import com.android.tradefed.config.IDeviceConfiguration;
29 import com.android.tradefed.config.Option;
30 import com.android.tradefed.config.Option.Importance;
31 import com.android.tradefed.config.OptionCopier;
32 import com.android.tradefed.device.DeviceNotAvailableException;
33 import com.android.tradefed.device.DeviceProperties;
34 import com.android.tradefed.device.ITestDevice;
35 import com.android.tradefed.device.NullDevice;
36 import com.android.tradefed.device.StubDevice;
37 import com.android.tradefed.device.cloud.NestedRemoteDevice;
38 import com.android.tradefed.device.connection.AbstractConnection;
39 import com.android.tradefed.device.connection.AdbTcpConnection;
40 import com.android.tradefed.device.metric.CollectorHelper;
41 import com.android.tradefed.device.metric.IMetricCollector;
42 import com.android.tradefed.device.metric.IMetricCollectorReceiver;
43 import com.android.tradefed.error.HarnessRuntimeException;
44 import com.android.tradefed.error.IHarnessException;
45 import com.android.tradefed.invoker.IInvocationContext;
46 import com.android.tradefed.invoker.TestInformation;
47 import com.android.tradefed.invoker.logger.CurrentInvocation;
48 import com.android.tradefed.invoker.logger.CurrentInvocation.IsolationGrade;
49 import com.android.tradefed.invoker.logger.InvocationMetricLogger;
50 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey;
51 import com.android.tradefed.invoker.logger.TfObjectTracker;
52 import com.android.tradefed.invoker.shard.token.ITokenRequest;
53 import com.android.tradefed.invoker.shard.token.TokenProperty;
54 import com.android.tradefed.invoker.tracing.CloseableTraceScope;
55 import com.android.tradefed.log.ITestLogger;
56 import com.android.tradefed.log.LogUtil.CLog;
57 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
58 import com.android.tradefed.postprocessor.IPostProcessor;
59 import com.android.tradefed.result.ByteArrayInputStreamSource;
60 import com.android.tradefed.result.FailureDescription;
61 import com.android.tradefed.result.ITestInvocationListener;
62 import com.android.tradefed.result.ITestLoggerReceiver;
63 import com.android.tradefed.result.InputStreamSource;
64 import com.android.tradefed.result.LogDataType;
65 import com.android.tradefed.result.ResultForwarder;
66 import com.android.tradefed.result.error.DeviceErrorIdentifier;
67 import com.android.tradefed.result.error.InfraErrorIdentifier;
68 import com.android.tradefed.result.error.TestErrorIdentifier;
69 import com.android.tradefed.retry.IRetryDecision;
70 import com.android.tradefed.retry.RetryStrategy;
71 import com.android.tradefed.service.TradefedFeatureClient;
72 import com.android.tradefed.suite.checker.ISystemStatusChecker;
73 import com.android.tradefed.suite.checker.ISystemStatusCheckerReceiver;
74 import com.android.tradefed.suite.checker.StatusCheckerResult;
75 import com.android.tradefed.suite.checker.StatusCheckerResult.CheckStatus;
76 import com.android.tradefed.targetprep.ITargetPreparer;
77 import com.android.tradefed.testtype.Abi;
78 import com.android.tradefed.testtype.IAbi;
79 import com.android.tradefed.testtype.IBuildReceiver;
80 import com.android.tradefed.testtype.IDeviceTest;
81 import com.android.tradefed.testtype.IInvocationContextReceiver;
82 import com.android.tradefed.testtype.IRemoteTest;
83 import com.android.tradefed.testtype.IReportNotExecuted;
84 import com.android.tradefed.testtype.IRuntimeHintProvider;
85 import com.android.tradefed.testtype.IShardableTest;
86 import com.android.tradefed.testtype.ITestCollector;
87 import com.android.tradefed.util.AbiFormatter;
88 import com.android.tradefed.util.AbiUtils;
89 import com.android.tradefed.util.MultiMap;
90 import com.android.tradefed.util.StreamUtil;
91 import com.android.tradefed.util.TimeUtil;
92 
93 import com.google.common.collect.ImmutableSet;
94 import com.proto.tradefed.feature.FeatureResponse;
95 
96 import java.io.File;
97 import java.io.FileNotFoundException;
98 import java.io.IOException;
99 import java.io.InputStream;
100 import java.io.PrintWriter;
101 import java.io.StringWriter;
102 import java.lang.reflect.InvocationTargetException;
103 import java.util.ArrayList;
104 import java.util.Arrays;
105 import java.util.Collection;
106 import java.util.Collections;
107 import java.util.HashMap;
108 import java.util.HashSet;
109 import java.util.Iterator;
110 import java.util.LinkedHashMap;
111 import java.util.LinkedHashSet;
112 import java.util.List;
113 import java.util.Map;
114 import java.util.Map.Entry;
115 import java.util.Random;
116 import java.util.Set;
117 import java.util.stream.Collectors;
118 
119 /**
120  * Abstract class used to run Test Suite. This class provide the base of how the Suite will be run.
121  * Each implementation can define the list of tests via the {@link #loadTests()} method.
122  */
123 public abstract class ITestSuite
124         implements IRemoteTest,
125                 IDeviceTest,
126                 IBuildReceiver,
127                 ISystemStatusCheckerReceiver,
128                 IShardableTest,
129                 ITestCollector,
130                 IInvocationContextReceiver,
131                 IRuntimeHintProvider,
132                 IMetricCollectorReceiver,
133                 IConfigurationReceiver,
134                 IReportNotExecuted,
135                 ITokenRequest,
136                 ITestLoggerReceiver {
137 
138     public static final String MODULE_START_TIME = "MODULE_START_TIME";
139     public static final String MODULE_END_TIME = "MODULE_END_TIME";
140 
141     public static final String SKIP_SYSTEM_STATUS_CHECKER = "skip-system-status-check";
142     public static final String RUNNER_WHITELIST = "runner-whitelist";
143     public static final String PREPARER_WHITELIST = "preparer-whitelist";
144     public static final String MODULE_CHECKER_PRE = "PreModuleChecker";
145     public static final String MODULE_CHECKER_POST = "PostModuleChecker";
146     public static final String ABI_OPTION = "abi";
147     public static final String SKIP_HOST_ARCH_CHECK = "skip-host-arch-check";
148     public static final String PRIMARY_ABI_RUN = "primary-abi-only";
149     public static final String PARAMETER_KEY = "parameter";
150     public static final String MAINLINE_PARAMETER_KEY = "mainline-param";
151     public static final String ACTIVE_MAINLINE_PARAMETER_KEY = "active-mainline-parameter";
152     public static final String TOKEN_KEY = "token";
153     public static final String MODULE_METADATA_INCLUDE_FILTER = "module-metadata-include-filter";
154     public static final String MODULE_METADATA_EXCLUDE_FILTER = "module-metadata-exclude-filter";
155     public static final String RANDOM_SEED = "random-seed";
156     public static final String SKIP_STAGING_ARTIFACTS = "skip-staging-artifacts";
157     public static final String STAGE_MODULE_ARTIFACTS = "stage-module-artifacts";
158 
159     private static final String PRODUCT_CPU_ABI_KEY = "ro.product.cpu.abi";
160 
161     public static final String TEST_TYPE_KEY = "test-type";
162     public static final String TEST_TYPE_VALUE_PERFORMANCE = "performance";
163 
164     private static final Set<String> ALLOWED_PREPARERS_CONFIGS =
165             ImmutableSet.of("/suite/allowed-preparers.txt", "/suite/google-allowed-preparers.txt");
166 
167     @Deprecated
168     @Option(
169             name = "bugreport-on-failure",
170             description =
171                     "Take a bugreport on every test failure. Warning: This may require a lot"
172                             + "of storage space of the machine running the tests.")
173     private boolean mBugReportOnFailure = false;
174 
175     @Deprecated
176     @Option(
177         name = "logcat-on-failure",
178         description = "Take a logcat snapshot on every test failure."
179     )
180     private boolean mLogcatOnFailure = false;
181 
182     @Deprecated
183     @Option(
184             name = "logcat-on-failure-size",
185             description =
186                     "The max number of logcat data in bytes to capture when --logcat-on-failure is"
187                             + " on. Should be an amount that can comfortably fit in memory.")
188     private int mMaxLogcatBytes = 500 * 1024; // 500K
189 
190     @Deprecated
191     @Option(
192         name = "screenshot-on-failure",
193         description = "Take a screenshot on every test failure."
194     )
195     private boolean mScreenshotOnFailure = false;
196 
197     @Deprecated
198     @Option(name = "reboot-on-failure", description = "Reboot the device after every test failure.")
199     private boolean mRebootOnFailure = false;
200 
201     // Options for suite runner behavior
202     @Option(name = "reboot-per-module", description = "Reboot the device before every module run.")
203     private boolean mRebootPerModule = false;
204 
205     @Option(name = "skip-all-system-status-check",
206             description = "Whether all system status check between modules should be skipped")
207     private boolean mSkipAllSystemStatusCheck = false;
208 
209     @Option(
210         name = SKIP_SYSTEM_STATUS_CHECKER,
211         description =
212                 "Disable specific system status checkers."
213                         + "Specify zero or more SystemStatusChecker as canonical class names. e.g. "
214                         + "\"com.android.tradefed.suite.checker.KeyguardStatusChecker\" If not "
215                         + "specified, all configured or whitelisted system status checkers will "
216                         + "run."
217     )
218     private Set<String> mSystemStatusCheckBlacklist = new HashSet<>();
219 
220     @Option(
221         name = "report-system-checkers",
222         description = "Whether reporting system checkers as test or not."
223     )
224     private boolean mReportSystemChecker = false;
225 
226     @Option(
227         name = "random-order",
228         description = "Whether randomizing the order of the modules to be ran or not."
229     )
230     private boolean mRandomOrder = false;
231 
232     @Option(
233         name = RANDOM_SEED,
234         description = "Seed to randomize the order of the modules."
235     )
236     private long mRandomSeed = -1;
237 
238     @Option(
239         name = "collect-tests-only",
240         description =
241                 "Only invoke the suite to collect list of applicable test cases. All "
242                         + "test run callbacks will be triggered, but test execution will not be "
243                         + "actually carried out."
244     )
245     private boolean mCollectTestsOnly = false;
246 
247     // Abi related options
248     @Option(
249         name = ABI_OPTION,
250         shortName = 'a',
251         description = "the abi to test. For example: 'arm64-v8a'.",
252         importance = Importance.IF_UNSET
253     )
254     private String mAbiName = null;
255 
256     @Option(
257         name = SKIP_HOST_ARCH_CHECK,
258         description = "Whether host architecture check should be skipped."
259     )
260     private boolean mSkipHostArchCheck = false;
261 
262     @Option(
263         name = PRIMARY_ABI_RUN,
264         description =
265                 "Whether to run tests with only the device primary abi. "
266                         + "This is overriden by the --abi option."
267     )
268     private boolean mPrimaryAbiRun = false;
269 
270     @Option(
271         name = MODULE_METADATA_INCLUDE_FILTER,
272         description =
273                 "Include modules for execution based on matching of metadata fields: for any of "
274                         + "the specified filter name and value, if a module has a metadata field "
275                         + "with the same name and value, it will be included. When both module "
276                         + "inclusion and exclusion rules are applied, inclusion rules will be "
277                         + "evaluated first. Using this together with test filter inclusion rules "
278                         + "may result in no tests to execute if the rules don't overlap."
279     )
280     private MultiMap<String, String> mModuleMetadataIncludeFilter = new MultiMap<>();
281 
282     @Option(
283         name = MODULE_METADATA_EXCLUDE_FILTER,
284         description =
285                 "Exclude modules for execution based on matching of metadata fields: for any of "
286                         + "the specified filter name and value, if a module has a metadata field "
287                         + "with the same name and value, it will be excluded. When both module "
288                         + "inclusion and exclusion rules are applied, inclusion rules will be "
289                         + "evaluated first."
290     )
291     private MultiMap<String, String> mModuleMetadataExcludeFilter = new MultiMap<>();
292 
293     @Option(name = RUNNER_WHITELIST, description = "Runner class(es) that are allowed to run.")
294     private Set<String> mAllowedRunners = new HashSet<>();
295 
296     @Option(
297         name = PREPARER_WHITELIST,
298         description =
299                 "Preparer class(es) that are allowed to run. This mostly usefeul for dry-runs."
300     )
301     private Set<String> mAllowedPreparers = new HashSet<>();
302 
303     @Option(
304         name = "enable-module-dynamic-download",
305         description =
306                 "Whether or not to allow the downloading of dynamic @option files at module level."
307     )
308     private boolean mEnableDynamicDownload = false;
309 
310     @Option(
311         name = "intra-module-sharding",
312         description = "Whether or not to allow intra-module sharding."
313     )
314     private boolean mIntraModuleSharding = true;
315 
316     @Option(
317         name = "isolated-module",
318         description = "Whether or not to attempt the module isolation between modules"
319     )
320     private boolean mIsolatedModule = false;
321 
322     @Option(
323             name = "isolated-module-grade",
324             description =
325                     "When isolated-module is enabled, this defines what action we try to attempt.")
326     private IsolatedModuleGrade mIsolatedModuleGrade = IsolatedModuleGrade.FULLY_ISOLATED;
327 
328     @Option(
329             name = "recover-device-by-cvd",
330             description =
331                     "Try to recover the device by cvd tool when the device is gone during test"
332                             + " running.")
333     protected boolean mRecoverDeviceByCvd = false;
334 
335     /** @deprecated to be deleted when next version is deployed */
336     @Deprecated
337     @Option(
338         name = "retry-strategy",
339         description =
340                 "The retry strategy to be used when re-running some tests with "
341                         + "--max-testcase-run-count"
342     )
343     private RetryStrategy mRetryStrategy = RetryStrategy.NO_RETRY;
344 
345     // [Options relate to module retry and intra-module retry][
346     @Option(
347         name = "merge-attempts",
348         description = "Whether or not to use the merge the results of the different attempts."
349     )
350     private boolean mMergeAttempts = true;
351     // end [Options relate to module retry and intra-module retry]
352 
353     @Option(
354             name = "partial-download-via-feature",
355             description = "Feature flag to test partial download via feature service.")
356     private boolean mStageArtifactsViaFeature = true;
357 
358     @Option(
359             name = SKIP_STAGING_ARTIFACTS,
360             description = "Skip staging artifacts with remote-files if already staged.")
361     private boolean mSkipStagingArtifacts = false;
362 
363     @Option(
364             name = "multi-devices-modules",
365             description = "Running strategy for modules that require multiple devices.")
366     private MultiDeviceModuleStrategy mMultiDevicesStrategy = MultiDeviceModuleStrategy.EXCLUDE_ALL;
367 
368     @Option(
369             name = "use-snapshot-for-reset",
370             description = "Feature flag to use snapshot/restore instead of powerwash.")
371     private boolean mUseSnapshotForReset = false;
372 
373     @Option(
374             name = "use-snapshot-before-first-module",
375             description =
376                     "Immediately restore the device after taking a snapshot. Ensures tests are"
377                             + " consistently run within a restored VM.")
378     private boolean mUseSnapshotBeforeFirstModule = false;
379 
380     @Option(name = "stage-remote-file", description = "Whether to allow staging of remote files.")
381     private boolean mStageRemoteFile = true;
382 
383     public enum IsolatedModuleGrade {
384         REBOOT_ISOLATED, // Reboot was done before the test.
385         FULLY_ISOLATED; // Test received a fresh device.
386     }
387 
388     public enum MultiDeviceModuleStrategy {
389         EXCLUDE_ALL,
390         RUN,
391         ONLY_MULTI_DEVICES
392     }
393 
394     private ITestDevice mDevice;
395     private IBuildInfo mBuildInfo;
396     private List<ISystemStatusChecker> mSystemStatusCheckers;
397     private IInvocationContext mContext;
398     private List<IMetricCollector> mMetricCollectors;
399     private IConfiguration mMainConfiguration;
400     private Set<IAbi> mAbis = new LinkedHashSet<>();
401 
402     // Sharding attributes
403     private boolean mIsSharded = false;
404     private ModuleDefinition mDirectModule = null;
405     private boolean mShouldMakeDynamicModule = true;
406 
407     // Current modules to run, null if not started to run yet.
408     private List<ModuleDefinition> mRunModules = null;
409     private ModuleDefinition mModuleInProgress = null;
410     // Logger to be used to files.
411     private ITestLogger mCurrentLogger = null;
412     // Whether or not we are currently in split
413     private boolean mIsSplitting = false;
414 
415     private boolean mDisableAutoRetryTimeReporting = false;
416 
417     private DynamicRemoteFileResolver mDynamicResolver = new DynamicRemoteFileResolver();
418 
419     @VisibleForTesting
setDynamicResolver(DynamicRemoteFileResolver resolver)420     void setDynamicResolver(DynamicRemoteFileResolver resolver) {
421         mDynamicResolver = resolver;
422     }
423 
424     @VisibleForTesting
setDirectModule(ModuleDefinition module)425     public void setDirectModule(ModuleDefinition module) {
426         mDirectModule = module;
427         mIsSharded = true;
428     }
429 
430     /**
431      * Abstract method to load the tests configuration that will be run. Each tests is defined by a
432      * {@link IConfiguration} and a unique name under which it will report results.
433      */
loadTests()434     public abstract LinkedHashMap<String, IConfiguration> loadTests();
435 
436     /**
437      * Return an instance of the class implementing {@link ITestSuite}.
438      */
createInstance()439     private ITestSuite createInstance() {
440         try {
441             return this.getClass().getDeclaredConstructor().newInstance();
442         } catch (InstantiationException
443                 | IllegalAccessException
444                 | InvocationTargetException
445                 | NoSuchMethodException e) {
446             throw new RuntimeException(e);
447         }
448     }
449 
getTestsDir()450     public File getTestsDir() throws FileNotFoundException {
451         IBuildInfo build = getBuildInfo();
452         File testsDir = null;
453         if (build instanceof IDeviceBuildInfo) {
454             testsDir = ((IDeviceBuildInfo) build).getTestsDir();
455         }
456         if (testsDir != null && testsDir.exists()) {
457             return testsDir;
458         }
459         // TODO: handle multi build?
460         throw new FileNotFoundException("Could not found a tests dir folder.");
461     }
462 
loadAndFilter()463     private LinkedHashMap<String, IConfiguration> loadAndFilter() {
464         LinkedHashMap<String, IConfiguration> runConfig = loadTests();
465         if (runConfig.isEmpty()) {
466             CLog.i("No config were loaded. Nothing to run.");
467             return runConfig;
468         }
469 
470         Set<String> moduleNames = new HashSet<>();
471         LinkedHashMap<String, IConfiguration> filteredConfig = new LinkedHashMap<>();
472         for (Entry<String, IConfiguration> config : runConfig.entrySet()) {
473             if (!mModuleMetadataIncludeFilter.isEmpty()
474                     || !mModuleMetadataExcludeFilter.isEmpty()) {
475                 if (!filterByConfigMetadata(
476                         config.getValue(),
477                         mModuleMetadataIncludeFilter,
478                         mModuleMetadataExcludeFilter)) {
479                     // if the module config did not pass the metadata filters, it's excluded
480                     // from execution.
481                     continue;
482                 }
483             }
484             if (!filterByRunnerType(config.getValue(), mAllowedRunners)) {
485                 // if the module config did not pass the runner type filter, it's excluded from
486                 // execution.
487                 continue;
488             }
489             switch (mMultiDevicesStrategy) {
490                 case EXCLUDE_ALL:
491                     if (config.getValue().getDeviceConfig().size() > 1) {
492                         // Exclude multi-devices configs
493                         continue;
494                     }
495                     break;
496                 case ONLY_MULTI_DEVICES:
497                     if (config.getValue().getDeviceConfig().size() == 1) {
498                         // Exclude single devices configs
499                         continue;
500                     }
501                     break;
502                 default:
503                     break;
504             }
505             filterPreparers(config.getValue(), mAllowedPreparers);
506 
507             // Copy the CoverageOptions from the main configuration to the module configuration.
508             if (mMainConfiguration != null) {
509                 config.getValue().setCoverageOptions(mMainConfiguration.getCoverageOptions());
510             }
511 
512             filteredConfig.put(config.getKey(), config.getValue());
513             moduleNames.add(config.getValue().getConfigurationDescription().getModuleName());
514         }
515 
516         if (stageAtInvocationLevel()) {
517             stageTestArtifacts(mDevice, moduleNames);
518         } else {
519             CLog.d(SKIP_STAGING_ARTIFACTS + " is set. Skipping #stageTestArtifacts");
520         }
521 
522         runConfig.clear();
523         return filteredConfig;
524     }
525 
stageAtInvocationLevel()526     private boolean stageAtInvocationLevel() {
527         if (mBuildInfo != null) {
528             if (mSkipStagingArtifacts
529                     || mBuildInfo.getBuildAttributes().get(SKIP_STAGING_ARTIFACTS) != null) {
530                 return false;
531             } else {
532                 return true;
533             }
534         }
535         return false;
536     }
537 
stageModuleLevel()538     private boolean stageModuleLevel() {
539         if (mBuildInfo != null) {
540             return mBuildInfo.getBuildAttributes().get(STAGE_MODULE_ARTIFACTS) != null;
541         }
542         return false;
543     }
544 
545     /** Helper to download all artifacts for the given modules. */
stageTestArtifacts(ITestDevice device, Set<String> modules)546     private void stageTestArtifacts(ITestDevice device, Set<String> modules) {
547         if (mBuildInfo.getRemoteFiles() == null || mBuildInfo.getRemoteFiles().isEmpty()) {
548             CLog.d("No remote build info, skipping stageTestArtifacts");
549             return;
550         }
551         CLog.i(String.format("Start to stage test artifacts for %d modules.", modules.size()));
552         long startTime = System.currentTimeMillis();
553         try (CloseableTraceScope ignored =
554                 new CloseableTraceScope(
555                         InvocationMetricKey.stage_suite_test_artifacts.toString())) {
556             // Include the file if its path contains a folder name matching any of the module.
557             String moduleRegex =
558                     modules.stream()
559                             .map(m -> String.format("/%s/", m))
560                             .collect(Collectors.joining("|"));
561             List<String> includeFilters = Arrays.asList(moduleRegex);
562             // Ignore config file as it's part of config and jar zip artifact that's staged already.
563             List<String> excludeFilters = Arrays.asList("[.]config$", "[.]jar$");
564             if (mStageArtifactsViaFeature) {
565                 try (TradefedFeatureClient client = new TradefedFeatureClient()) {
566                     Map<String, String> args = new HashMap<>();
567                     String destination = getTestsDir().getAbsolutePath();
568                     // In *TS cases, download from root dir reference instead of tests dir
569                     if (mBuildInfo.getBuildAttributes().containsKey("ROOT_DIR")) {
570                         destination = mBuildInfo.getBuildAttributes().get("ROOT_DIR");
571                     }
572                     CLog.d(
573                             "downloading to destination: %s the following include_filters: %s",
574                             destination, includeFilters);
575                     args.put(ResolvePartialDownload.DESTINATION_DIR, destination);
576                     args.put(
577                             ResolvePartialDownload.INCLUDE_FILTERS,
578                             String.join(";", includeFilters));
579                     args.put(
580                             ResolvePartialDownload.EXCLUDE_FILTERS,
581                             String.join(";", excludeFilters));
582                     // Pass the remote paths
583                     String remotePaths =
584                             mBuildInfo.getRemoteFiles().stream()
585                                     .map(p -> p.toString())
586                                     .collect(Collectors.joining(";"));
587                     args.put(ResolvePartialDownload.REMOTE_PATHS, remotePaths);
588                     FeatureResponse rep =
589                             client.triggerFeature(
590                                     ResolvePartialDownload.RESOLVE_PARTIAL_DOWNLOAD_FEATURE_NAME,
591                                     args);
592                     if (rep.hasErrorInfo()) {
593                         throw new HarnessRuntimeException(
594                                 rep.getErrorInfo().getErrorTrace(),
595                                 InfraErrorIdentifier.ARTIFACT_DOWNLOAD_ERROR);
596                     }
597                 } catch (FileNotFoundException e) {
598                     throw new HarnessRuntimeException(
599                             e.getMessage(), e, InfraErrorIdentifier.ARTIFACT_DOWNLOAD_ERROR);
600                 }
601             } else {
602                 mDynamicResolver.setDevice(device);
603                 mDynamicResolver.addExtraArgs(
604                         mMainConfiguration.getCommandOptions().getDynamicDownloadArgs());
605                 for (File remoteFile : mBuildInfo.getRemoteFiles()) {
606                     try {
607                         mDynamicResolver.resolvePartialDownloadZip(
608                                 getTestsDir(),
609                                 remoteFile.toString(),
610                                 includeFilters,
611                                 excludeFilters);
612                     } catch (BuildRetrievalError | FileNotFoundException e) {
613                         String message =
614                                 String.format(
615                                         "Failed to download partial zip from %s for modules: %s",
616                                         remoteFile, String.join(", ", modules));
617                         CLog.e(message);
618                         CLog.e(e);
619                         if (e instanceof IHarnessException) {
620                             throw new HarnessRuntimeException(message, (IHarnessException) e);
621                         }
622                         throw new HarnessRuntimeException(
623                                 message, e, InfraErrorIdentifier.ARTIFACT_DOWNLOAD_ERROR);
624                     }
625                 }
626             }
627         }
628         long elapsedTime = System.currentTimeMillis() - startTime;
629         InvocationMetricLogger.addInvocationMetrics(
630                 InvocationMetricKey.STAGE_TESTS_TIME, elapsedTime);
631         CLog.i(
632                 String.format(
633                         "Staging test artifacts for %d modules finished in %s.",
634                         modules.size(), TimeUtil.formatElapsedTime(elapsedTime)));
635     }
636 
637     /** Helper that creates and returns the list of {@link ModuleDefinition} to be executed. */
createExecutionList()638     private List<ModuleDefinition> createExecutionList() {
639         List<ModuleDefinition> runModules = new ArrayList<>();
640         if (mDirectModule != null) {
641             // If we are sharded and already know what to run then we just do it.
642             runModules.add(mDirectModule);
643             mDirectModule.setDevice(mDevice);
644             mDirectModule.setBuild(mBuildInfo);
645             return runModules;
646         }
647         try (CloseableTraceScope ignore = new CloseableTraceScope("suite:createExecutionList")) {
648             long start = System.currentTimeMillis();
649             LinkedHashMap<String, IConfiguration> runConfig = loadAndFilter();
650             InvocationMetricLogger.addInvocationPairMetrics(
651                     InvocationMetricKey.TEST_SETUP_PAIR, start, System.currentTimeMillis());
652             if (runConfig.isEmpty()) {
653                 CLog.i("No config were loaded. Nothing to run.");
654                 return runModules;
655             }
656 
657             Map<String, List<ITargetPreparer>> suitePreparersPerDevice =
658                     getAllowedPreparerPerDevice(mMainConfiguration);
659 
660             for (Entry<String, IConfiguration> config : runConfig.entrySet()) {
661                 // Validate the configuration, it will throw if not valid.
662                 ValidateSuiteConfigHelper.validateConfig(config.getValue());
663                 Map<String, List<ITargetPreparer>> preparersPerDevice =
664                         getPreparerPerDevice(config.getValue());
665                 ModuleDefinition module =
666                         new ModuleDefinition(
667                                 config.getKey(),
668                                 config.getValue().getTests(),
669                                 preparersPerDevice,
670                                 suitePreparersPerDevice,
671                                 config.getValue().getMultiTargetPreparers(),
672                                 config.getValue());
673                 if (mDisableAutoRetryTimeReporting) {
674                     module.disableAutoRetryReportingTime();
675                 }
676                 module.setDevice(mDevice);
677                 module.setBuild(mBuildInfo);
678                 runModules.add(module);
679             }
680 
681             /** Randomize all the modules to be ran if random-order is set and no sharding. */
682             if (mRandomOrder) {
683                 randomizeTestModules(runModules, mRandomSeed);
684             }
685 
686             CLog.logAndDisplay(LogLevel.DEBUG, "[Total Unique Modules = %s]", runModules.size());
687             // Free the map once we are done with it.
688             runConfig = null;
689             return runModules;
690         }
691     }
692 
693     /**
694      * Helper method that handle randomizing the order of the modules.
695      *
696      * @param runModules The {@code List<ModuleDefinition>} of the test modules to be ran.
697      * @param randomSeed The {@code long} seed used to randomize the order of test modules, use the
698      *     current time as seed if no specified seed provided.
699      */
700     @VisibleForTesting
randomizeTestModules(List<ModuleDefinition> runModules, long randomSeed)701     void randomizeTestModules(List<ModuleDefinition> runModules, long randomSeed) {
702         // Use current time as seed if no specified seed provided.
703         if (randomSeed == -1) {
704             randomSeed = System.currentTimeMillis();
705         }
706         CLog.i("Randomizing all the modules with seed: %s", randomSeed);
707         Collections.shuffle(runModules, new Random(randomSeed));
708         mBuildInfo.addBuildAttribute(RANDOM_SEED, String.valueOf(randomSeed));
709     }
710 
checkClassLoad(Set<String> classes, String type)711     private void checkClassLoad(Set<String> classes, String type) {
712         for (String c : classes) {
713             try {
714                 Class.forName(c);
715             } catch (ClassNotFoundException e) {
716                 ConfigurationException ex =
717                         new ConfigurationException(
718                                 String.format(
719                                         "--%s must contains valid class, %s was not found",
720                                         type, c),
721                                 e);
722                 throw new RuntimeException(ex);
723             }
724         }
725     }
726 
727     /** Create the mapping of device to its target_preparer. */
getPreparerPerDevice(IConfiguration config)728     private Map<String, List<ITargetPreparer>> getPreparerPerDevice(IConfiguration config) {
729         Map<String, List<ITargetPreparer>> res = new LinkedHashMap<>();
730         for (IDeviceConfiguration holder : config.getDeviceConfig()) {
731             List<ITargetPreparer> preparers = new ArrayList<>();
732             res.put(holder.getDeviceName(), preparers);
733             preparers.addAll(holder.getTargetPreparers());
734         }
735         return res;
736     }
737 
738     /** Create the mapping of device to its target_preparer that's allowed to rerun. */
getAllowedPreparerPerDevice(IConfiguration config)739     private Map<String, List<ITargetPreparer>> getAllowedPreparerPerDevice(IConfiguration config) {
740         // For unittests, mMainConfiguration might not have been set.
741         if (config == null) {
742             return new LinkedHashMap<String, List<ITargetPreparer>>();
743         }
744         // Read the list of allowed suite level target preparers from resource files.
745         Set<String> allowedSuitePreparers = new HashSet<>();
746         for (String resource : ALLOWED_PREPARERS_CONFIGS) {
747             try (InputStream resStream = ITestSuite.class.getResourceAsStream(resource)) {
748                 if (resStream == null) {
749                     CLog.d("Resource not found for allowed preparers: %s", resource);
750                     continue;
751                 }
752                 List<String> preparers =
753                         Arrays.asList(StreamUtil.getStringFromStream(resStream).split("\n"));
754                 allowedSuitePreparers.addAll(preparers);
755             } catch (IOException e) {
756                 CLog.e(e);
757             }
758         }
759 
760         Map<String, List<ITargetPreparer>> res = new LinkedHashMap<>();
761         for (IDeviceConfiguration holder : config.getDeviceConfig()) {
762             List<ITargetPreparer> preparers = new ArrayList<>();
763             for (ITargetPreparer preparer : holder.getTargetPreparers()) {
764                 if (allowedSuitePreparers.contains(preparer.getClass().getCanonicalName())) {
765                     preparers.add(preparer);
766                 }
767             }
768             res.put(holder.getDeviceName(), preparers);
769         }
770         return res;
771     }
772 
773     /**
774      * Opportunity to clean up all the things that were needed during the suites setup but are not
775      * required to run the tests.
776      */
cleanUpSuiteSetup()777     public void cleanUpSuiteSetup() {
778         // Empty by default.
779     }
780 
781     /** Generic run method for all test loaded from {@link #loadTests()}. */
782     @Override
run(TestInformation testInfo, ITestInvocationListener listener)783     public final void run(TestInformation testInfo, ITestInvocationListener listener)
784             throws DeviceNotAvailableException {
785         mCurrentLogger = listener;
786         // Load and check the module checkers, runners and preparers in black and whitelist
787         checkClassLoad(mSystemStatusCheckBlacklist, SKIP_SYSTEM_STATUS_CHECKER);
788         checkClassLoad(mAllowedRunners, RUNNER_WHITELIST);
789         checkClassLoad(mAllowedPreparers, PREPARER_WHITELIST);
790 
791         mRunModules = createExecutionList();
792         // Check if we have something to run.
793         if (mRunModules.isEmpty()) {
794             CLog.i("No tests to be run.");
795             return;
796         }
797 
798         // Allow checkers to log files for easier debugging.
799         for (ISystemStatusChecker checker : mSystemStatusCheckers) {
800             if (checker instanceof ITestLoggerReceiver) {
801                 ((ITestLoggerReceiver) checker).setTestLogger(listener);
802             }
803         }
804 
805         /** Create the list of listeners applicable at the module level. */
806         List<ITestInvocationListener> moduleListeners = createModuleListeners();
807 
808         if (mUseSnapshotForReset) {
809             AbstractConnection connection = mDevice.getConnection();
810             if (connection instanceof AdbTcpConnection) {
811                 // Capture a snapshot once at the beginning of the suite
812                 if (((AdbTcpConnection) connection).getSuiteSnapshots().containsKey(mDevice)) {
813                     CLog.d("Suite snapshot already taken for '%s'", mDevice.getSerialNumber());
814                 } else {
815                     ((AdbTcpConnection) connection)
816                             .snapshotDevice(mDevice, mContext.getInvocationId());
817                     ((AdbTcpConnection) connection)
818                             .getSuiteSnapshots()
819                             .put(mDevice, mContext.getInvocationId());
820                 }
821                 if (mUseSnapshotBeforeFirstModule) {
822                     String snapshot =
823                             ((AdbTcpConnection) connection).getSuiteSnapshots().get(mDevice);
824                     ((AdbTcpConnection) connection).recoverVirtualDevice(mDevice, snapshot, null);
825                 }
826             }
827         }
828 
829         // Only print the running log if we are going to run something.
830         if (mRunModules.get(0).hasTests()) {
831             CLog.logAndDisplay(
832                     LogLevel.INFO,
833                     "%s running %s modules: %s",
834                     mDevice.getSerialNumber(),
835                     mRunModules.size(),
836                     mRunModules);
837         }
838 
839         /** Run all the module, make sure to reduce the list to release resources as we go. */
840         try {
841             while (!mRunModules.isEmpty()) {
842                 ModuleDefinition module = mRunModules.remove(0);
843 
844                 if (!shouldModuleRun(module)) {
845                     continue;
846                 }
847                 // Before running the module we ensure it has tests at this point or skip completely
848                 // to avoid running SystemCheckers and preparation for nothing.
849                 if (!module.hasTests()) {
850                     continue;
851                 }
852 
853                 try (CloseableTraceScope ignore = new CloseableTraceScope(module.getId())) {
854                     if (!stageAtInvocationLevel() && stageModuleLevel()) {
855                         stageTestArtifacts(
856                                 mDevice,
857                                 ImmutableSet.of(
858                                         module.getModuleConfiguration()
859                                                 .getConfigurationDescription()
860                                                 .getModuleName()));
861                     }
862                     // Populate the module context with devices and builds
863                     for (String deviceName : mContext.getDeviceConfigNames()) {
864                         module.getModuleInvocationContext()
865                                 .addAllocatedDevice(deviceName, mContext.getDevice(deviceName));
866                         module.getModuleInvocationContext()
867                                 .addDeviceBuildInfo(deviceName, mContext.getBuildInfo(deviceName));
868                     }
869                     // Add isolation status before module start for reporting
870                     if (!IsolationGrade.NOT_ISOLATED.equals(
871                             CurrentInvocation.moduleCurrentIsolation())) {
872                         module.getModuleInvocationContext()
873                                 .addInvocationAttribute(
874                                         ModuleDefinition.MODULE_ISOLATED,
875                                         CurrentInvocation.moduleCurrentIsolation().toString());
876                     }
877                     // Add module specific post processors.
878                     listener = listenerWithPostProcessorsForPerfModule(module, listener);
879                     // Only the module callback will be called here.
880                     ITestInvocationListener listenerWithCollectors = listener;
881                     if (mMetricCollectors != null) {
882                         for (IMetricCollector collector :
883                                 CollectorHelper.cloneCollectors(mMetricCollectors)) {
884                             if (collector.isDisabled()) {
885                                 CLog.d("%s has been disabled. Skipping.", collector);
886                             } else if (!collector.captureModuleLevel()) {
887                                 CLog.d("%s isn't applicable at module level. Skipping.", collector);
888                             } else {
889                                 if (collector instanceof IConfigurationReceiver) {
890                                     ((IConfigurationReceiver) collector)
891                                             .setConfiguration(module.getModuleConfiguration());
892                                 }
893                                 try (CloseableTraceScope ignored =
894                                         new CloseableTraceScope(
895                                                 "init_for_module_"
896                                                         + collector.getClass().getSimpleName())) {
897                                     listenerWithCollectors =
898                                             collector.init(
899                                                     module.getModuleInvocationContext(),
900                                                     listenerWithCollectors);
901                                     TfObjectTracker.countWithParents(collector.getClass());
902                                 }
903                             }
904                         }
905                     }
906                     module.getModuleInvocationContext()
907                             .addInvocationAttribute(
908                                     MODULE_START_TIME, Long.toString(System.currentTimeMillis()));
909                     listenerWithCollectors.testModuleStarted(module.getModuleInvocationContext());
910                     mModuleInProgress = module;
911                     // Trigger module start on module level listener too
912                     new ResultForwarder(moduleListeners)
913                             .testModuleStarted(module.getModuleInvocationContext());
914                     TestInformation moduleInfo =
915                             TestInformation.createModuleTestInfo(
916                                     testInfo, module.getModuleInvocationContext());
917                     logModuleConfig(listener, module);
918                     try {
919                         runSingleModule(module, moduleInfo, listener, moduleListeners);
920                     } finally {
921                         module.getModuleInvocationContext()
922                                 .addInvocationAttribute(
923                                         MODULE_END_TIME, Long.toString(System.currentTimeMillis()));
924                         // Trigger module end on module level listener too
925                         new ResultForwarder(moduleListeners).testModuleEnded();
926                         // clear out module invocation context since we are now done with module
927                         // execution
928                         listenerWithCollectors.testModuleEnded();
929                         mModuleInProgress = null;
930                         // Following modules will not be isolated if no action is taken
931                         CurrentInvocation.setModuleIsolation(IsolationGrade.NOT_ISOLATED);
932                     }
933                     // Module isolation routine
934                     moduleIsolation(mContext, listener);
935                 }
936             }
937         } catch (DeviceNotAvailableException e) {
938             CLog.e(
939                     "A DeviceNotAvailableException occurred, following modules did not run: %s",
940                     mRunModules);
941             reportNotExecuted(listener, "Module did not run due to device not available.");
942             throw e;
943         }
944     }
945 
946     /**
947      * Returns a listener with module-level post processors (for perf modules) instered to the
948      * listener chain.
949      */
listenerWithPostProcessorsForPerfModule( ModuleDefinition module, ITestInvocationListener listener)950     private ITestInvocationListener listenerWithPostProcessorsForPerfModule(
951             ModuleDefinition module, ITestInvocationListener listener) {
952         IConfiguration config = module.getModuleConfiguration();
953         List<String> testTypes = config.getConfigurationDescription().getMetaData(TEST_TYPE_KEY);
954         if (testTypes == null || !testTypes.contains(TEST_TYPE_VALUE_PERFORMANCE)) {
955             return listener; // not a perf module
956         }
957         List<IPostProcessor> topLevelPostProcessors = mMainConfiguration.getPostProcessors();
958         List<IPostProcessor> modulePostProcessors = config.getPostProcessors();
959         if (modulePostProcessors.size() > 0 && topLevelPostProcessors.size() > 0) {
960             CLog.w("Post processors specified at both top level and module level (%s)", module);
961         }
962         for (IPostProcessor postProcessor : modulePostProcessors) {
963             try {
964                 listener = postProcessor.init(listener);
965             } catch (Exception e) {
966                 CLog.e(
967                         "Post processor %s is ignored as it fails to init() with exception: %s",
968                         postProcessor.getClass().getSimpleName(), e);
969             }
970         }
971         return listener;
972     }
973 
974     /** Log the module configuration. */
logModuleConfig(ITestLogger logger, ModuleDefinition module)975     private void logModuleConfig(ITestLogger logger, ModuleDefinition module) {
976         try (StringWriter configXmlWriter = new StringWriter();
977                 PrintWriter wrapperWriter = new PrintWriter(configXmlWriter)) {
978             module.getModuleConfiguration()
979                     .dumpXml(
980                             wrapperWriter,
981                             new ArrayList<String>(Configuration.NON_MODULE_OBJECTS),
982                             true,
983                             false);
984             wrapperWriter.flush();
985             // Specified UTF-8 encoding for an abundance of caution, but its possible we could want
986             // something else in the future
987             byte[] configXmlByteArray = configXmlWriter.toString().getBytes("UTF-8");
988             try (InputStreamSource source = new ByteArrayInputStreamSource(configXmlByteArray)) {
989                 logger.testLog("module-configuration", LogDataType.HARNESS_CONFIG, source);
990             }
991         } catch (RuntimeException | IOException e) {
992             CLog.e(e);
993         }
994     }
995 
996     /**
997      * Returns the list of {@link ITestInvocationListener} applicable to the {@link ModuleListener}
998      * level. These listeners will be re-used for each module, they will not be re-instantiated so
999      * they should not assume an internal state.
1000      */
createModuleListeners()1001     protected List<ITestInvocationListener> createModuleListeners() {
1002         return new ArrayList<>();
1003     }
1004 
1005     /**
1006      * Routine that attempt to reset a device between modules in order to provide isolation.
1007      *
1008      * @param context The invocation context.
1009      * @param logger A logger where extra logs can be saved.
1010      * @throws DeviceNotAvailableException
1011      */
moduleIsolation(IInvocationContext context, ITestLogger logger)1012     private void moduleIsolation(IInvocationContext context, ITestLogger logger)
1013             throws DeviceNotAvailableException {
1014         if (!mIsolatedModule) {
1015             return;
1016         }
1017         if (IsolatedModuleGrade.REBOOT_ISOLATED.equals(mIsolatedModuleGrade)) {
1018             CLog.d("isolated-module is enabled with grade REBOOT_ISOLATED.");
1019             try (CloseableTraceScope ignored = new CloseableTraceScope("isolated_module_reboot")) {
1020                 for (ITestDevice device : context.getDevices()) {
1021                     device.reboot();
1022                 }
1023                 CurrentInvocation.setModuleIsolation(IsolationGrade.REBOOT_ISOLATED);
1024                 CurrentInvocation.setRunIsolation(IsolationGrade.REBOOT_ISOLATED);
1025             }
1026         } else if (IsolatedModuleGrade.FULLY_ISOLATED.equals(mIsolatedModuleGrade)) {
1027             // TODO: we can probably make it smarter: Did any test ran for example?
1028             ITestDevice device = context.getDevices().get(0);
1029             if (device instanceof NestedRemoteDevice) {
1030                 boolean res = ((NestedRemoteDevice) device).resetVirtualDevice();
1031                 if (!res) {
1032                     String serial = device.getSerialNumber();
1033                     throw new DeviceNotAvailableException(
1034                             String.format(
1035                                     "Failed to reset the AVD '%s' during module isolation.",
1036                                     serial),
1037                             serial);
1038                 }
1039             } else if (mUseSnapshotForReset) {
1040                 AbstractConnection connection = device.getConnection();
1041                 if (connection instanceof AdbTcpConnection) {
1042                     String snapshot =
1043                             ((AdbTcpConnection) connection).getSuiteSnapshots().get(device);
1044                     // snapshot should not be null, otherwise the device would have crashed.
1045                     ((AdbTcpConnection) connection).recoverVirtualDevice(device, snapshot, null);
1046                 }
1047             }
1048         }
1049     }
1050 
1051     /**
1052      * Helper method that handle running a single module logic.
1053      *
1054      * @param module The {@link ModuleDefinition} to be ran.
1055      * @param moduleInfo The {@link TestInformation} for the module.
1056      * @param listener The {@link ITestInvocationListener} where to report results
1057      * @param moduleListeners The {@link ITestInvocationListener}s that runs at the module level.
1058      * @param failureListener special listener that we add to collect information on failures.
1059      * @throws DeviceNotAvailableException
1060      */
runSingleModule( ModuleDefinition module, TestInformation moduleInfo, ITestInvocationListener listener, List<ITestInvocationListener> moduleListeners)1061     private void runSingleModule(
1062             ModuleDefinition module,
1063             TestInformation moduleInfo,
1064             ITestInvocationListener listener,
1065             List<ITestInvocationListener> moduleListeners)
1066             throws DeviceNotAvailableException {
1067         Map<String, String> properties = new LinkedHashMap<>();
1068         try (CloseableTraceScope ignored = new CloseableTraceScope("module_pre_check")) {
1069             if (mRebootPerModule) {
1070                 if ("user".equals(mDevice.getProperty(DeviceProperties.BUILD_TYPE))) {
1071                     CLog.e(
1072                             "reboot-per-module should only be used during development, "
1073                                     + "this is a\" user\" build device");
1074                 } else {
1075                     CLog.d("Rebooting device before starting next module");
1076                     mDevice.reboot();
1077                 }
1078             }
1079 
1080             if (!mSkipAllSystemStatusCheck && !mSystemStatusCheckers.isEmpty()) {
1081                 properties.putAll(
1082                         runPreModuleCheck(
1083                                 module.getId(), mSystemStatusCheckers, mDevice, listener));
1084             }
1085             if (mCollectTestsOnly) {
1086                 module.setCollectTestsOnly(mCollectTestsOnly);
1087             }
1088             if (mRecoverDeviceByCvd) {
1089                 module.setRecoverVirtualDevice(mRecoverDeviceByCvd);
1090             }
1091             // Pass the run defined collectors to be used.
1092             module.setMetricCollectors(CollectorHelper.cloneCollectors(mMetricCollectors));
1093             // Pass the main invocation logSaver
1094             module.setLogSaver(mMainConfiguration.getLogSaver());
1095 
1096             IRetryDecision decision = mMainConfiguration.getRetryDecision();
1097             // Pass whether we should merge the attempts of not
1098             if (mMergeAttempts
1099                     && decision.getMaxRetryCount() > 1
1100                     && !RetryStrategy.NO_RETRY.equals(decision.getRetryStrategy())) {
1101                 CLog.d("Overriding '--merge-attempts' to false for auto-retry.");
1102                 mMergeAttempts = false;
1103             }
1104             module.setMergeAttemps(mMergeAttempts);
1105             // Pass the retry decision to be used.
1106             module.setRetryDecision(decision);
1107             // Restore the config, as the setter might have override it with module config.
1108             if (decision instanceof IConfigurationReceiver) {
1109                 ((IConfigurationReceiver) decision).setConfiguration(mMainConfiguration);
1110             }
1111 
1112             module.setEnableDynamicDownload(mEnableDynamicDownload);
1113             module.transferSuiteLevelOptions(mMainConfiguration);
1114         }
1115         // Actually run the module
1116         module.run(
1117                 moduleInfo,
1118                 listener,
1119                 moduleListeners,
1120                 getConfiguration().getRetryDecision().getMaxRetryCount());
1121 
1122         if (!mSkipAllSystemStatusCheck && !mSystemStatusCheckers.isEmpty()) {
1123             try (CloseableTraceScope ignored = new CloseableTraceScope("module_post_check")) {
1124                 properties.putAll(
1125                         runPostModuleCheck(
1126                                 module.getId(), mSystemStatusCheckers, mDevice, listener));
1127             }
1128         }
1129         for (Map.Entry<String, String> entry : properties.entrySet()) {
1130             module.getModuleInvocationContext()
1131                     .addInvocationAttribute(entry.getKey(), entry.getValue());
1132         }
1133     }
1134 
1135     /**
1136      * Helper to run the System Status checkers preExecutionChecks defined for the test and log
1137      * their failures.
1138      */
runPreModuleCheck( String moduleName, List<ISystemStatusChecker> checkers, ITestDevice device, ITestInvocationListener listener)1139     private Map<String, String> runPreModuleCheck(
1140             String moduleName,
1141             List<ISystemStatusChecker> checkers,
1142             ITestDevice device,
1143             ITestInvocationListener listener)
1144             throws DeviceNotAvailableException {
1145         long startTime = System.currentTimeMillis();
1146         CLog.i("Running system status checker before module execution: %s", moduleName);
1147         Map<String, String> failures = new LinkedHashMap<>();
1148         Map<String, String> properties = new LinkedHashMap<>();
1149         boolean bugreportNeeded = false;
1150         for (ISystemStatusChecker checker : checkers) {
1151             // Track usage of the checker
1152             TfObjectTracker.countWithParents(checker.getClass());
1153             // Check if the status checker should be skipped.
1154             if (mSystemStatusCheckBlacklist.contains(checker.getClass().getName())) {
1155                 CLog.d(
1156                         "%s was skipped via %s",
1157                         checker.getClass().getName(), SKIP_SYSTEM_STATUS_CHECKER);
1158                 continue;
1159             }
1160 
1161             StatusCheckerResult result = new StatusCheckerResult(CheckStatus.FAILED);
1162             try {
1163                 result = checker.preExecutionCheck(device);
1164             } catch (RuntimeException e) {
1165                 CLog.e(e);
1166                 // Catch RuntimeException to avoid leaking throws that go to the invocation.
1167                 result.setErrorMessage(e.getMessage());
1168                 result.setBugreportNeeded(true);
1169             }
1170             properties.putAll(result.getModuleProperties());
1171             if (!CheckStatus.SUCCESS.equals(result.getStatus())) {
1172                 String errorMessage =
1173                         (result.getErrorMessage() == null) ? "" : result.getErrorMessage();
1174                 failures.put(checker.getClass().getCanonicalName(), errorMessage);
1175                 bugreportNeeded = bugreportNeeded | result.isBugreportNeeded();
1176                 CLog.w("System status checker [%s] failed.", checker.getClass().getCanonicalName());
1177             }
1178         }
1179         if (!failures.isEmpty()) {
1180             CLog.w("There are failed system status checkers: %s", failures.toString());
1181             if (bugreportNeeded && !(device.getIDevice() instanceof StubDevice)) {
1182                 device.logBugreport(
1183                         String.format("bugreport-checker-pre-module-%s", moduleName), listener);
1184             }
1185         }
1186 
1187         // We report System checkers like tests.
1188         reportModuleCheckerResult(MODULE_CHECKER_PRE, moduleName, failures, startTime, listener);
1189         InvocationMetricLogger.addInvocationPairMetrics(
1190                 InvocationMetricKey.STATUS_CHECKER_PAIR, startTime, System.currentTimeMillis());
1191         return properties;
1192     }
1193 
1194     /**
1195      * Helper to run the System Status checkers postExecutionCheck defined for the test and log
1196      * their failures.
1197      */
runPostModuleCheck( String moduleName, List<ISystemStatusChecker> checkers, ITestDevice device, ITestInvocationListener listener)1198     private Map<String, String> runPostModuleCheck(
1199             String moduleName,
1200             List<ISystemStatusChecker> checkers,
1201             ITestDevice device,
1202             ITestInvocationListener listener)
1203             throws DeviceNotAvailableException {
1204         long startTime = System.currentTimeMillis();
1205         CLog.i("Running system status checker after module execution: %s", moduleName);
1206         Map<String, String> failures = new LinkedHashMap<>();
1207         Map<String, String> properties = new LinkedHashMap<>();
1208         boolean bugreportNeeded = false;
1209         for (ISystemStatusChecker checker : checkers) {
1210             // Check if the status checker should be skipped.
1211             if (mSystemStatusCheckBlacklist.contains(checker.getClass().getName())) {
1212                 continue;
1213             }
1214 
1215             StatusCheckerResult result = new StatusCheckerResult(CheckStatus.FAILED);
1216             try {
1217                 result = checker.postExecutionCheck(device);
1218             } catch (RuntimeException e) {
1219                 CLog.e(e);
1220                 // Catch RuntimeException to avoid leaking throws that go to the invocation.
1221                 result.setErrorMessage(e.getMessage());
1222                 result.setBugreportNeeded(true);
1223             } catch (DeviceNotAvailableException dnae) {
1224                 // Wrap the DNAE to provide a better error message
1225                 String message =
1226                         String.format(
1227                                 "Device became unavailable after %s due to: %s",
1228                                 moduleName, dnae.getMessage());
1229                 DeviceNotAvailableException wrapper =
1230                         new DeviceNotAvailableException(message, dnae, dnae.getSerial());
1231                 throw wrapper;
1232             }
1233             properties.putAll(result.getModuleProperties());
1234             if (!CheckStatus.SUCCESS.equals(result.getStatus())) {
1235                 String errorMessage =
1236                         (result.getErrorMessage() == null) ? "" : result.getErrorMessage();
1237                 failures.put(checker.getClass().getCanonicalName(), errorMessage);
1238                 bugreportNeeded = bugreportNeeded | result.isBugreportNeeded();
1239                 CLog.w("System status checker [%s] failed", checker.getClass().getCanonicalName());
1240             }
1241         }
1242         if (!failures.isEmpty()) {
1243             CLog.w("There are failed system status checkers: %s", failures.toString());
1244             if (bugreportNeeded && !(device.getIDevice() instanceof StubDevice)) {
1245                 device.logBugreport(
1246                         String.format("bugreport-checker-post-module-%s", moduleName), listener);
1247             }
1248         }
1249 
1250         // We report System checkers like tests.
1251         reportModuleCheckerResult(MODULE_CHECKER_POST, moduleName, failures, startTime, listener);
1252         InvocationMetricLogger.addInvocationPairMetrics(
1253                 InvocationMetricKey.STATUS_CHECKER_PAIR, startTime, System.currentTimeMillis());
1254         return properties;
1255     }
1256 
1257     /** Helper to report status checker results as test results. */
reportModuleCheckerResult( String identifier, String moduleName, Map<String, String> failures, long startTime, ITestInvocationListener listener)1258     private void reportModuleCheckerResult(
1259             String identifier,
1260             String moduleName,
1261             Map<String, String> failures,
1262             long startTime,
1263             ITestInvocationListener listener) {
1264         if (!mReportSystemChecker) {
1265             // do not log here, otherwise it could be very verbose.
1266             return;
1267         }
1268         // Avoid messing with the final test count by making them empty runs.
1269         listener.testRunStarted(identifier + "_" + moduleName, 0, 0, System.currentTimeMillis());
1270         if (!failures.isEmpty()) {
1271             FailureDescription description =
1272                     FailureDescription.create(
1273                                     String.format("%s failed '%s' checkers", moduleName, failures))
1274                             .setErrorIdentifier(TestErrorIdentifier.MODULE_CHANGED_SYSTEM_STATUS);
1275             listener.testRunFailed(description);
1276         }
1277         listener.testRunEnded(
1278                 System.currentTimeMillis() - startTime, new HashMap<String, Metric>());
1279     }
1280 
1281     /** Returns true if we are currently in {@link #split(int)}. */
isSplitting()1282     public boolean isSplitting() {
1283         return mIsSplitting;
1284     }
1285 
1286     /** {@inheritDoc} */
1287     @Override
split(Integer shardCountHint, TestInformation testInfo)1288     public Collection<IRemoteTest> split(Integer shardCountHint, TestInformation testInfo) {
1289         if (shardCountHint == null || shardCountHint <= 1 || mIsSharded) {
1290             // cannot shard or already sharded
1291             return null;
1292         }
1293         // TODO: Replace by relying on testInfo directly
1294         setBuild(testInfo.getBuildInfo());
1295         setDevice(testInfo.getDevice());
1296         setInvocationContext(testInfo.getContext());
1297 
1298         mIsSplitting = true;
1299         try {
1300             long start = System.currentTimeMillis();
1301             LinkedHashMap<String, IConfiguration> runConfig = loadAndFilter();
1302             InvocationMetricLogger.addInvocationPairMetrics(
1303                     InvocationMetricKey.TEST_SETUP_PAIR, start, System.currentTimeMillis());
1304             if (runConfig.isEmpty()) {
1305                 CLog.i("No config were loaded. Nothing to run.");
1306                 return null;
1307             }
1308             injectInfo(runConfig, testInfo);
1309 
1310             // We split individual tests on double the shardCountHint to provide better average.
1311             // The test pool mechanism prevent this from creating too much overhead.
1312             List<ModuleDefinition> splitModules =
1313                     ModuleSplitter.splitConfiguration(
1314                             testInfo,
1315                             runConfig,
1316                             getAllowedPreparerPerDevice(mMainConfiguration),
1317                             shardCountHint,
1318                             mShouldMakeDynamicModule,
1319                             mIntraModuleSharding);
1320             runConfig.clear();
1321             runConfig = null;
1322 
1323             // Clean up the parent that will get sharded: It is fine to clean up before copying the
1324             // options, because the sharded module is already created/populated so there is no need
1325             // to carry these extra data.
1326             cleanUpSuiteSetup();
1327 
1328             // create an association of one ITestSuite <=> one ModuleDefinition as the smallest
1329             // execution unit supported.
1330             List<IRemoteTest> splitTests = new ArrayList<>();
1331             for (ModuleDefinition m : splitModules) {
1332                 ITestSuite suite = createInstance();
1333                 OptionCopier.copyOptionsNoThrow(this, suite);
1334                 suite.mIsSharded = true;
1335                 suite.mDirectModule = m;
1336                 splitTests.add(suite);
1337             }
1338             // return the list of ITestSuite with their ModuleDefinition assigned
1339             return splitTests;
1340         } finally {
1341             // Done splitting at that point
1342             mIsSplitting = false;
1343         }
1344     }
1345 
1346     /**
1347      * Inject {@link ITestDevice} and {@link IBuildInfo} to the {@link IRemoteTest}s in the config
1348      * before sharding since they may be needed.
1349      */
injectInfo( LinkedHashMap<String, IConfiguration> runConfig, TestInformation testInfo)1350     private void injectInfo(
1351             LinkedHashMap<String, IConfiguration> runConfig, TestInformation testInfo) {
1352         for (IConfiguration config : runConfig.values()) {
1353             for (IRemoteTest test : config.getTests()) {
1354                 if (test instanceof IBuildReceiver) {
1355                     ((IBuildReceiver) test).setBuild(testInfo.getBuildInfo());
1356                 }
1357                 if (test instanceof IDeviceTest) {
1358                     ((IDeviceTest) test).setDevice(testInfo.getDevice());
1359                 }
1360                 if (test instanceof IInvocationContextReceiver) {
1361                     ((IInvocationContextReceiver) test).setInvocationContext(testInfo.getContext());
1362                 }
1363                 if (test instanceof ITestCollector) {
1364                     ((ITestCollector) test).setCollectTestsOnly(mCollectTestsOnly);
1365                 }
1366             }
1367         }
1368     }
1369 
1370     /** {@inheritDoc} */
1371     @Override
setDevice(ITestDevice device)1372     public void setDevice(ITestDevice device) {
1373         mDevice = device;
1374     }
1375 
1376     /**
1377      * {@inheritDoc}
1378      */
1379     @Override
getDevice()1380     public ITestDevice getDevice() {
1381         return mDevice;
1382     }
1383 
1384     /** Set the value of mAbiName */
setAbiName(String abiName)1385     public void setAbiName(String abiName) {
1386         mAbiName = abiName;
1387     }
1388 
1389     /**
1390      * {@inheritDoc}
1391      */
1392     @Override
setBuild(IBuildInfo buildInfo)1393     public void setBuild(IBuildInfo buildInfo) {
1394         mBuildInfo = buildInfo;
1395         mBuildInfo.allowStagingRemoteFile(mStageRemoteFile);
1396     }
1397 
1398     /**
1399      * Implementation of {@link ITestSuite} may require the build info to load the tests.
1400      */
getBuildInfo()1401     public IBuildInfo getBuildInfo() {
1402         return mBuildInfo;
1403     }
1404 
1405     /** Set the value of mPrimaryAbiRun */
setPrimaryAbiRun(boolean primaryAbiRun)1406     public void setPrimaryAbiRun(boolean primaryAbiRun) {
1407         mPrimaryAbiRun = primaryAbiRun;
1408     }
1409 
1410     /**
1411      * {@inheritDoc}
1412      */
1413     @Override
setSystemStatusChecker(List<ISystemStatusChecker> systemCheckers)1414     public void setSystemStatusChecker(List<ISystemStatusChecker> systemCheckers) {
1415         mSystemStatusCheckers = systemCheckers;
1416     }
1417 
1418     /**
1419      * Run the test suite in collector only mode, this requires all the sub-tests to implements this
1420      * interface too.
1421      */
1422     @Override
setCollectTestsOnly(boolean shouldCollectTest)1423     public void setCollectTestsOnly(boolean shouldCollectTest) {
1424         mCollectTestsOnly = shouldCollectTest;
1425     }
1426 
1427     /** {@inheritDoc} */
1428     @Override
setMetricCollectors(List<IMetricCollector> collectors)1429     public void setMetricCollectors(List<IMetricCollector> collectors) {
1430         mMetricCollectors = collectors;
1431     }
1432 
1433     /**
1434      * When doing distributed sharding, we cannot have ModuleDefinition that shares tests in a pool
1435      * otherwise intra-module sharding will not work, so we allow to disable it.
1436      */
setShouldMakeDynamicModule(boolean dynamicModule)1437     public void setShouldMakeDynamicModule(boolean dynamicModule) {
1438         mShouldMakeDynamicModule = dynamicModule;
1439     }
1440 
1441     /** {@inheritDoc} */
1442     @Override
setInvocationContext(IInvocationContext invocationContext)1443     public void setInvocationContext(IInvocationContext invocationContext) {
1444         mContext = invocationContext;
1445     }
1446 
1447     /**
1448      * Returns the invocation context.
1449      */
getInvocationContext()1450     public IInvocationContext getInvocationContext() {
1451         return mContext;
1452     }
1453 
1454     /** {@inheritDoc} */
1455     @Override
setTestLogger(ITestLogger testLogger)1456     public void setTestLogger(ITestLogger testLogger) {
1457         mCurrentLogger = testLogger;
1458     }
1459 
getCurrentTestLogger()1460     public ITestLogger getCurrentTestLogger() {
1461         return mCurrentLogger;
1462     }
1463 
1464     /** {@inheritDoc} */
1465     @Override
getRuntimeHint()1466     public long getRuntimeHint() {
1467         if (mDirectModule != null) {
1468             CLog.d(
1469                     "    %s: %s",
1470                     mDirectModule.getId(),
1471                     TimeUtil.formatElapsedTime(mDirectModule.getRuntimeHint()));
1472             return mDirectModule.getRuntimeHint();
1473         }
1474         return 0L;
1475     }
1476 
1477     /** {@inheritDoc} */
1478     @Override
setConfiguration(IConfiguration configuration)1479     public void setConfiguration(IConfiguration configuration) {
1480         mMainConfiguration = configuration;
1481     }
1482 
1483     /** Returns the invocation {@link IConfiguration}. */
getConfiguration()1484     public final IConfiguration getConfiguration() {
1485         return mMainConfiguration;
1486     }
1487 
1488     /** {@inheritDoc} */
1489     @Override
reportNotExecuted(ITestInvocationListener listener)1490     public void reportNotExecuted(ITestInvocationListener listener) {
1491         reportNotExecuted(listener, IReportNotExecuted.NOT_EXECUTED_FAILURE);
1492     }
1493 
1494     /** {@inheritDoc} */
1495     @Override
reportNotExecuted(ITestInvocationListener listener, String message)1496     public void reportNotExecuted(ITestInvocationListener listener, String message) {
1497         // If the runner is already in progress, report the remaining tests as not executed.
1498         List<ModuleDefinition> runModules = null;
1499         if (mRunModules != null) {
1500             runModules = new ArrayList<>(mRunModules);
1501         }
1502         if (runModules == null) {
1503             runModules = createExecutionList();
1504         }
1505 
1506         if (mModuleInProgress != null) {
1507             // TODO: Ensure in-progress data make sense
1508             String inProgressMessage =
1509                     String.format(
1510                             "Module %s was interrupted after starting. Results might not be "
1511                                     + "accurate or complete.",
1512                             mModuleInProgress.getId());
1513             mModuleInProgress.reportNotExecuted(listener, inProgressMessage);
1514         }
1515 
1516         while (!runModules.isEmpty()) {
1517             ModuleDefinition module = runModules.remove(0);
1518             module.reportNotExecuted(listener, message);
1519         }
1520     }
1521 
getModuleMetadataIncludeFilters()1522     public MultiMap<String, String> getModuleMetadataIncludeFilters() {
1523         return mModuleMetadataIncludeFilter;
1524     }
1525 
addModuleMetadataIncludeFilters(MultiMap<String, String> filters)1526     public void addModuleMetadataIncludeFilters(MultiMap<String, String> filters) {
1527         mModuleMetadataIncludeFilter.putAll(filters);
1528     }
1529 
addModuleMetadataExcludeFilters(MultiMap<String, String> filters)1530     public void addModuleMetadataExcludeFilters(MultiMap<String, String> filters) {
1531         mModuleMetadataExcludeFilter.putAll(filters);
1532     }
1533 
1534     /**
1535      * Returns the {@link ModuleDefinition} to be executed directly, or null if none yet (when the
1536      * ITestSuite has not been sharded yet).
1537      */
getDirectModule()1538     public ModuleDefinition getDirectModule() {
1539         return mDirectModule;
1540     }
1541 
1542     @Override
getRequiredTokens(TestInformation testInfo)1543     public Set<TokenProperty> getRequiredTokens(TestInformation testInfo) {
1544         if (mDirectModule == null) {
1545             return null;
1546         }
1547         return mDirectModule.getRequiredTokens(testInfo);
1548     }
1549 
1550     /**
1551      * Gets the set of ABIs supported by both Compatibility testing {@link
1552      * AbiUtils#getAbisSupportedByCompatibility()} and the device under test.
1553      *
1554      * @return The set of ABIs to run the tests on
1555      * @throws DeviceNotAvailableException
1556      */
getAbis(ITestDevice device)1557     public Set<IAbi> getAbis(ITestDevice device) throws DeviceNotAvailableException {
1558         if (!mAbis.isEmpty()) {
1559             return mAbis;
1560         }
1561         Set<IAbi> abis = new LinkedHashSet<>();
1562         Set<String> archAbis = getAbisForBuildTargetArch();
1563         // Handle null-device: use abi in common with host and suite build
1564         if (mPrimaryAbiRun) {
1565             if (mAbiName == null) {
1566                 // Get the primary from the device and make it the --abi to run.
1567                 mAbiName = getPrimaryAbi(device);
1568             } else {
1569                 CLog.d(
1570                         "Option --%s supersedes the option --%s, using abi: %s",
1571                         ABI_OPTION, PRIMARY_ABI_RUN, mAbiName);
1572             }
1573         }
1574         if (mAbiName != null) {
1575             // A particular abi was requested, it still needs to be supported by the build.
1576             if ((!mSkipHostArchCheck && !archAbis.contains(mAbiName))
1577                     || !AbiUtils.isAbiSupportedByCompatibility(mAbiName)) {
1578                 throw new IllegalArgumentException(
1579                         String.format(
1580                                 "Your tests suite hasn't been built with "
1581                                         + "abi '%s' support, this suite currently supports '%s'.",
1582                                 mAbiName, archAbis));
1583             } else {
1584                 abis.add(new Abi(mAbiName, AbiUtils.getBitness(mAbiName)));
1585                 return abis;
1586             }
1587         } else {
1588             // Run on all abi in common between the device and suite builds.
1589             List<String> deviceAbis = getDeviceAbis(device);
1590             if (deviceAbis.isEmpty()) {
1591                 throw new HarnessRuntimeException(
1592                         String.format(
1593                                 "Couldn't determinate the abi of the device '%s'.",
1594                                 device.getSerialNumber()),
1595                         DeviceErrorIdentifier.DEVICE_UNEXPECTED_RESPONSE);
1596             }
1597             for (String abi : deviceAbis) {
1598                 if ((mSkipHostArchCheck || archAbis.contains(abi))
1599                         && AbiUtils.isAbiSupportedByCompatibility(abi)) {
1600                     abis.add(new Abi(abi, AbiUtils.getBitness(abi)));
1601                 } else {
1602                     CLog.d(
1603                             "abi '%s' is supported by device but not by this suite build (%s), "
1604                                     + "tests will not run against it.",
1605                             abi, archAbis);
1606                 }
1607             }
1608             if (abis.isEmpty()) {
1609                 throw new IllegalArgumentException(
1610                         String.format(
1611                                 "None of the abi supported by this tests suite build ('%s') are "
1612                                         + "supported by the device ('%s').",
1613                                 archAbis, deviceAbis));
1614             }
1615             return abis;
1616         }
1617     }
1618 
1619     /** Returns the primary abi of the device or host if it's a null device. */
getPrimaryAbi(ITestDevice device)1620     private String getPrimaryAbi(ITestDevice device) throws DeviceNotAvailableException {
1621         if (device.getIDevice() instanceof NullDevice) {
1622             Set<String> hostAbis = getHostAbis();
1623             return hostAbis.iterator().next();
1624         }
1625         String property = device.getProperty(PRODUCT_CPU_ABI_KEY);
1626         if (property == null) {
1627             String serial = device.getSerialNumber();
1628             throw new DeviceNotAvailableException(
1629                     String.format(
1630                             "Device '%s' was not online to query %s", serial, PRODUCT_CPU_ABI_KEY),
1631                     serial,
1632                     DeviceErrorIdentifier.DEVICE_UNAVAILABLE);
1633         }
1634         return property.trim();
1635     }
1636 
1637     /** Returns the list of abis supported by the device or host if it's a null device. */
getDeviceAbis(ITestDevice device)1638     private List<String> getDeviceAbis(ITestDevice device) throws DeviceNotAvailableException {
1639         if (device.getIDevice() instanceof NullDevice) {
1640             return new ArrayList<>(getHostAbis());
1641         }
1642         // Make it an arrayList to be able to modify the content.
1643         return new ArrayList<>(Arrays.asList(AbiFormatter.getSupportedAbis(device, "")));
1644     }
1645 
1646     /** Return the abis supported by the Host build target architecture. Exposed for testing. */
1647     @VisibleForTesting
getAbisForBuildTargetArch()1648     protected Set<String> getAbisForBuildTargetArch() {
1649         return getAbisForBuildTargetArchFromSuite();
1650     }
1651 
1652     /** Returns the possible abis from the TestSuiteInfo. */
getAbisForBuildTargetArchFromSuite()1653     public static Set<String> getAbisForBuildTargetArchFromSuite() {
1654         // If TestSuiteInfo does not exists, the stub arch will be replaced by all possible abis.
1655         Set<String> abis = new LinkedHashSet<>();
1656         for (String arch : TestSuiteInfo.getInstance().getTargetArchs()) {
1657             abis.addAll(AbiUtils.getAbisForArch(arch));
1658         }
1659         return abis;
1660     }
1661 
1662     /** Returns the host machine abis. */
1663     @VisibleForTesting
getHostAbis()1664     protected Set<String> getHostAbis() {
1665         return AbiUtils.getHostAbi();
1666     }
1667 
1668     /** Returns the abi requested with the option -a or --abi. */
getRequestedAbi()1669     public final String getRequestedAbi() {
1670         return mAbiName;
1671     }
1672 
1673     /**
1674      * Apply the metadata filter to the config and see if the config should run.
1675      *
1676      * @param config The {@link IConfiguration} being evaluated.
1677      * @param include the metadata include filter
1678      * @param exclude the metadata exclude filter
1679      * @return True if the module should run, false otherwise.
1680      */
1681     @VisibleForTesting
filterByConfigMetadata( IConfiguration config, MultiMap<String, String> include, MultiMap<String, String> exclude)1682     public boolean filterByConfigMetadata(
1683             IConfiguration config,
1684             MultiMap<String, String> include,
1685             MultiMap<String, String> exclude) {
1686         MultiMap<String, String> metadata = config.getConfigurationDescription().getAllMetaData();
1687         boolean shouldInclude = false;
1688         for (String key : include.keySet()) {
1689             Set<String> filters = new HashSet<>(include.get(key));
1690             if (metadata.containsKey(key)) {
1691                 filters.retainAll(metadata.get(key));
1692                 if (!filters.isEmpty()) {
1693                     // inclusion filter is not empty and there's at least one matching inclusion
1694                     // rule so there's no need to match other inclusion rules
1695                     shouldInclude = true;
1696                     break;
1697                 }
1698             }
1699         }
1700         if (!include.isEmpty() && !shouldInclude) {
1701             // if inclusion filter is not empty and we didn't find a match, the module will not be
1702             // included
1703             return false;
1704         }
1705         // Now evaluate exclusion rules, this ordering also means that exclusion rules may override
1706         // inclusion rules: a config already matched for inclusion may still be excluded if matching
1707         // rules exist
1708         for (String key : exclude.keySet()) {
1709             Set<String> filters = new HashSet<>(exclude.get(key));
1710             if (metadata.containsKey(key)) {
1711                 filters.retainAll(metadata.get(key));
1712                 if (!filters.isEmpty()) {
1713                     // we found at least one matching exclusion rules, so we are excluding this
1714                     // this module
1715                     return false;
1716                 }
1717             }
1718         }
1719         // we've matched at least one inclusion rule (if there's any) AND we didn't match any of the
1720         // exclusion rules (if there's any)
1721         return true;
1722     }
1723 
1724     /**
1725      * Filter out the preparers that were not whitelisted. This is useful for collect-tests-only
1726      * where some preparers are not needed to dry run through the invocation.
1727      *
1728      * @param config the {@link IConfiguration} considered for filtering.
1729      * @param preparerWhiteList the current preparer whitelist.
1730      */
1731     @VisibleForTesting
filterPreparers(IConfiguration config, Set<String> preparerWhiteList)1732     void filterPreparers(IConfiguration config, Set<String> preparerWhiteList) {
1733         // If no filters was provided, skip the filtering.
1734         if (preparerWhiteList.isEmpty()) {
1735             return;
1736         }
1737         for (IDeviceConfiguration deviceConfig : config.getDeviceConfig()) {
1738             List<ITargetPreparer> preparers = new ArrayList<>(deviceConfig.getTargetPreparers());
1739             for (ITargetPreparer prep : preparers) {
1740                 if (!preparerWhiteList.contains(prep.getClass().getName())) {
1741                     deviceConfig.getTargetPreparers().remove(prep);
1742                 }
1743             }
1744         }
1745     }
1746 
1747     /**
1748      * Apply the Runner whitelist filtering, removing any runner that was not whitelisted. If a
1749      * configuration has several runners, some might be removed and the config will still run.
1750      *
1751      * @param config The {@link IConfiguration} being evaluated.
1752      * @param allowedRunners The current runner whitelist.
1753      * @return True if the configuration module is allowed to run, false otherwise.
1754      */
1755     @VisibleForTesting
filterByRunnerType(IConfiguration config, Set<String> allowedRunners)1756     protected boolean filterByRunnerType(IConfiguration config, Set<String> allowedRunners) {
1757         // If no filters are provided, simply run everything.
1758         if (allowedRunners.isEmpty()) {
1759             return true;
1760         }
1761         Iterator<IRemoteTest> iterator = config.getTests().iterator();
1762         while (iterator.hasNext()) {
1763             IRemoteTest test = iterator.next();
1764             if (!allowedRunners.contains(test.getClass().getName())) {
1765                 CLog.d(
1766                         "Runner '%s' in module '%s' was skipped by the runner whitelist: '%s'.",
1767                         test.getClass().getName(), config.getName(), allowedRunners);
1768                 iterator.remove();
1769             }
1770         }
1771 
1772         if (config.getTests().isEmpty()) {
1773             CLog.d("Module %s does not have any more tests, skipping it.", config.getName());
1774             return false;
1775         }
1776         return true;
1777     }
1778 
disableAutoRetryTimeReporting()1779     void disableAutoRetryTimeReporting() {
1780         mDisableAutoRetryTimeReporting = true;
1781     }
1782 
1783     @VisibleForTesting
setModuleInProgress(ModuleDefinition moduleInProgress)1784     void setModuleInProgress(ModuleDefinition moduleInProgress) {
1785         mModuleInProgress = moduleInProgress;
1786     }
1787 
setAbis(Set<IAbi> abis)1788     public final void setAbis(Set<IAbi> abis) {
1789         mAbis.addAll(abis);
1790     }
1791 
shouldModuleRun(ModuleDefinition module)1792     protected boolean shouldModuleRun(ModuleDefinition module) {
1793         return true;
1794     }
1795 
setMultiDeviceStrategy(MultiDeviceModuleStrategy strategy)1796     public void setMultiDeviceStrategy(MultiDeviceModuleStrategy strategy) {
1797         mMultiDevicesStrategy = strategy;
1798     }
1799 
getMultiDeviceStrategy()1800     public MultiDeviceModuleStrategy getMultiDeviceStrategy() {
1801         return mMultiDevicesStrategy;
1802     }
1803 
setIntraModuleSharding(boolean intraModuleSharding)1804     public void setIntraModuleSharding(boolean intraModuleSharding) {
1805         mIntraModuleSharding = intraModuleSharding;
1806     }
1807 
getIntraModuleSharding()1808     public boolean getIntraModuleSharding() {
1809         return mIntraModuleSharding;
1810     }
1811 }
1812