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