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