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