1 /* 2 * Copyright (C) 2015 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 17 package com.android.compatibility.common.tradefed.testtype; 18 19 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 20 import com.android.compatibility.common.tradefed.result.InvocationFailureHandler; 21 import com.android.compatibility.common.tradefed.result.SubPlanHelper; 22 import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker; 23 import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite; 24 import com.android.compatibility.common.tradefed.util.RetryFilterHelper; 25 import com.android.compatibility.common.tradefed.util.RetryType; 26 import com.android.compatibility.common.tradefed.util.UniqueModuleCountUtil; 27 import com.android.compatibility.common.util.IInvocationResult; 28 import com.android.compatibility.common.util.ResultHandler; 29 import com.android.compatibility.common.util.TestFilter; 30 import com.android.ddmlib.Log.LogLevel; 31 import com.android.tradefed.build.IBuildInfo; 32 import com.android.tradefed.config.ConfigurationException; 33 import com.android.tradefed.config.Option; 34 import com.android.tradefed.config.Option.Importance; 35 import com.android.tradefed.config.OptionClass; 36 import com.android.tradefed.config.OptionCopier; 37 import com.android.tradefed.device.DeviceNotAvailableException; 38 import com.android.tradefed.device.DeviceUnresponsiveException; 39 import com.android.tradefed.device.ITestDevice; 40 import com.android.tradefed.invoker.IInvocationContext; 41 import com.android.tradefed.invoker.InvocationContext; 42 import com.android.tradefed.log.ITestLogger; 43 import com.android.tradefed.log.LogUtil.CLog; 44 import com.android.tradefed.result.ITestInvocationListener; 45 import com.android.tradefed.result.InputStreamSource; 46 import com.android.tradefed.result.LogDataType; 47 import com.android.tradefed.suite.checker.ISystemStatusChecker; 48 import com.android.tradefed.suite.checker.ISystemStatusCheckerReceiver; 49 import com.android.tradefed.suite.checker.StatusCheckerResult; 50 import com.android.tradefed.suite.checker.StatusCheckerResult.CheckStatus; 51 import com.android.tradefed.testtype.Abi; 52 import com.android.tradefed.testtype.IAbi; 53 import com.android.tradefed.testtype.IBuildReceiver; 54 import com.android.tradefed.testtype.IDeviceTest; 55 import com.android.tradefed.testtype.IInvocationContextReceiver; 56 import com.android.tradefed.testtype.IRemoteTest; 57 import com.android.tradefed.testtype.IShardableTest; 58 import com.android.tradefed.testtype.IStrictShardableTest; 59 import com.android.tradefed.testtype.ITestCollector; 60 import com.android.tradefed.testtype.suite.TestSuiteInfo; 61 import com.android.tradefed.util.AbiFormatter; 62 import com.android.tradefed.util.AbiUtils; 63 import com.android.tradefed.util.ArrayUtil; 64 import com.android.tradefed.util.MultiMap; 65 import com.android.tradefed.util.StreamUtil; 66 import com.android.tradefed.util.TimeUtil; 67 68 import com.google.common.annotations.VisibleForTesting; 69 70 import java.io.ByteArrayOutputStream; 71 import java.io.FileNotFoundException; 72 import java.io.PrintWriter; 73 import java.util.ArrayList; 74 import java.util.Arrays; 75 import java.util.Collection; 76 import java.util.HashSet; 77 import java.util.LinkedHashSet; 78 import java.util.LinkedList; 79 import java.util.List; 80 import java.util.Set; 81 import java.util.concurrent.CountDownLatch; 82 import java.util.concurrent.TimeUnit; 83 84 /** 85 * A Test for running Compatibility Suites. 86 * @deprecated use {@link CompatibilityTestSuite} instead. 87 */ 88 @Deprecated 89 @OptionClass(alias = "compatibility") 90 public class CompatibilityTest implements IDeviceTest, IShardableTest, IBuildReceiver, 91 IStrictShardableTest, ISystemStatusCheckerReceiver, ITestCollector, 92 IInvocationContextReceiver { 93 94 public static final String INCLUDE_FILTER_OPTION = "include-filter"; 95 public static final String EXCLUDE_FILTER_OPTION = "exclude-filter"; 96 public static final String SUBPLAN_OPTION = "subplan"; 97 public static final String MODULE_OPTION = "module"; 98 public static final String TEST_OPTION = "test"; 99 public static final String PRECONDITION_ARG_OPTION = "precondition-arg"; 100 public static final String MODULE_ARG_OPTION = "module-arg"; 101 public static final String TEST_ARG_OPTION = "test-arg"; 102 public static final char TEST_OPTION_SHORT_NAME = 't'; 103 public static final String RETRY_OPTION = "retry"; 104 public static final String RETRY_TYPE_OPTION = "retry-type"; 105 public static final String ABI_OPTION = "abi"; 106 public static final String SHARD_OPTION = "shards"; 107 public static final String SKIP_DEVICE_INFO_OPTION = "skip-device-info"; 108 public static final String SKIP_PRECONDITIONS_OPTION = "skip-preconditions"; 109 public static final String SKIP_HOST_ARCH_CHECK = "skip-host-arch-check"; 110 public static final String PRIMARY_ABI_RUN = "primary-abi-only"; 111 public static final String DEVICE_TOKEN_OPTION = "device-token"; 112 public static final String LOGCAT_ON_FAILURE_SIZE_OPTION = "logcat-on-failure-size"; 113 114 // Constants for checking invocation or preconditions preparation failure 115 private static final int NUM_PREP_ATTEMPTS = 10; 116 private static final int MINUTES_PER_PREP_ATTEMPT = 2; 117 118 @Option(name = SUBPLAN_OPTION, 119 description = "the subplan to run", 120 importance = Importance.IF_UNSET) 121 private String mSubPlan; 122 123 @Option(name = INCLUDE_FILTER_OPTION, 124 description = "the include module filters to apply.", 125 importance = Importance.ALWAYS) 126 private Set<String> mIncludeFilters = new HashSet<>(); 127 128 @Option(name = EXCLUDE_FILTER_OPTION, 129 description = "the exclude module filters to apply.", 130 importance = Importance.ALWAYS) 131 private Set<String> mExcludeFilters = new HashSet<>(); 132 133 @Option(name = MODULE_OPTION, 134 shortName = 'm', 135 description = "the test module to run.", 136 importance = Importance.IF_UNSET) 137 private String mModuleName = null; 138 139 @Option(name = TEST_OPTION, 140 shortName = TEST_OPTION_SHORT_NAME, 141 description = "the test run.", 142 importance = Importance.IF_UNSET) 143 private String mTestName = null; 144 145 @Option(name = PRECONDITION_ARG_OPTION, 146 description = "the arguments to pass to a precondition. The expected format is" 147 + "\"<arg-name>:<arg-value>\"", 148 importance = Importance.ALWAYS) 149 private List<String> mPreconditionArgs = new ArrayList<>(); 150 151 @Option(name = MODULE_ARG_OPTION, 152 description = "the arguments to pass to a module. The expected format is" 153 + "\"<module-name>:<arg-name>:[<arg-key>:=]<arg-value>\"", 154 importance = Importance.ALWAYS) 155 private List<String> mModuleArgs = new ArrayList<>(); 156 157 @Option(name = TEST_ARG_OPTION, 158 description = "the arguments to pass to a test. The expected format is" 159 + "\"<test-class>:<arg-name>:[<arg-key>:=]<arg-value>\"", 160 importance = Importance.ALWAYS) 161 private List<String> mTestArgs = new ArrayList<>(); 162 163 @Option(name = RETRY_OPTION, 164 shortName = 'r', 165 description = "retry a previous session's failed and not executed tests.", 166 importance = Importance.IF_UNSET) 167 private Integer mRetrySessionId = null; 168 169 @Option(name = RETRY_TYPE_OPTION, 170 description = "used with " + RETRY_OPTION + ", retry tests of a certain status. " 171 + "Possible values include \"failed\", \"not_executed\", and \"custom\".", 172 importance = Importance.IF_UNSET) 173 private RetryType mRetryType = null; 174 175 @Option(name = ABI_OPTION, 176 shortName = 'a', 177 description = "the abi to test.", 178 importance = Importance.IF_UNSET) 179 private String mAbiName = null; 180 181 @Option(name = SHARD_OPTION, 182 description = "split the modules up to run on multiple devices concurrently. " 183 + "Deprecated, use --shard-count instead.") 184 @Deprecated 185 private int mShards = 1; 186 187 @Option(name = SKIP_DEVICE_INFO_OPTION, 188 shortName = 'd', 189 description = "Whether device info collection should be skipped") 190 private boolean mSkipDeviceInfo = false; 191 192 @Option(name = SKIP_HOST_ARCH_CHECK, 193 description = "Whether host architecture check should be skipped") 194 private boolean mSkipHostArchCheck = false; 195 196 @Option(name = SKIP_PRECONDITIONS_OPTION, 197 shortName = 'o', 198 description = "Whether preconditions should be skipped") 199 private boolean mSkipPreconditions = false; 200 201 @Option(name = PRIMARY_ABI_RUN, 202 description = "Whether to run tests with only the device primary abi. " 203 + "This override the --abi option.") 204 private boolean mPrimaryAbiRun = false; 205 206 @Option(name = DEVICE_TOKEN_OPTION, 207 description = "Holds the devices' tokens, used when scheduling tests that have" 208 + "prerequisites such as requiring a SIM card. Format is <serial>:<token>", 209 importance = Importance.ALWAYS) 210 private List<String> mDeviceTokens = new ArrayList<>(); 211 212 @Option(name = "bugreport-on-failure", 213 description = "Take a bugreport on every test failure. " + 214 "Warning: can potentially use a lot of disk space.") 215 private boolean mBugReportOnFailure = false; 216 217 @Option(name = "logcat-on-failure", 218 description = "Take a logcat snapshot on every test failure.") 219 private boolean mLogcatOnFailure = false; 220 221 @Option(name = LOGCAT_ON_FAILURE_SIZE_OPTION, 222 description = "The max number of logcat data in bytes to capture when " 223 + "--logcat-on-failure is on. Should be an amount that can comfortably fit in memory.") 224 private int mMaxLogcatBytes = 500 * 1024; // 500K 225 226 @Option(name = "screenshot-on-failure", 227 description = "Take a screenshot on every test failure.") 228 private boolean mScreenshotOnFailure = false; 229 230 @Option(name = "reboot-before-test", 231 description = "Reboot the device before the test suite starts.") 232 private boolean mRebootBeforeTest = false; 233 234 @Option(name = "reboot-on-failure", 235 description = "Reboot the device after every test failure.") 236 private boolean mRebootOnFailure = false; 237 238 @Option(name = "reboot-per-module", 239 description = "Reboot the device before every module run.") 240 private boolean mRebootPerModule = false; 241 242 @Option(name = "skip-connectivity-check", 243 description = "Don't verify device connectivity between module execution.") 244 private boolean mSkipConnectivityCheck = false; 245 246 @Option(name = "preparer-whitelist", 247 description = "Only run specific preparers." 248 + "Specify zero or more ITargetPreparers as canonical class names. " 249 + "e.g. \"com.android.compatibility.common.tradefed.targetprep.ApkInstaller\" " 250 + "If not specified, all configured preparers are run.") 251 private Set<String> mPreparerWhitelist = new HashSet<>(); 252 253 @Option(name = "skip-all-system-status-check", 254 description = "Whether all system status check between modules should be skipped") 255 private boolean mSkipAllSystemStatusCheck = false; 256 257 @Option(name = "skip-system-status-check", 258 description = "Disable specific system status checkers." 259 + "Specify zero or more SystemStatusChecker as canonical class names. e.g. " 260 + "\"com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker\" " 261 + "If not specified, all configured or whitelisted system status checkers are run.") 262 private Set<String> mSystemStatusCheckBlacklist = new HashSet<>(); 263 264 @Option(name = "system-status-check-whitelist", 265 description = "Only run specific system status checkers." 266 + "Specify zero or more SystemStatusChecker as canonical class names. e.g. " 267 + "\"com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker\" " 268 + "If not specified, all configured system status checkers are run.") 269 private Set<String> mSystemStatusCheckWhitelist = new HashSet<>(); 270 271 private List<ISystemStatusChecker> mListCheckers = new ArrayList<>(); 272 273 @Option(name = "collect-tests-only", 274 description = "Only invoke the suite to collect list of applicable test cases. All " 275 + "test run callbacks will be triggered, but test execution will not be " 276 + "actually carried out.") 277 private Boolean mCollectTestsOnly = null; 278 279 @Option(name = "module-metadata-include-filter", 280 description = "Include modules for execution based on matching of metadata fields: " 281 + "for any of the specified filter name and value, if a module has a metadata " 282 + "field with the same name and value, it will be included. When both module " 283 + "inclusion and exclusion rules are applied, inclusion rules will be " 284 + "evaluated first. Using this together with test filter inclusion rules may " 285 + "result in no tests to execute if the rules don't overlap.") 286 private MultiMap<String, String> mModuleMetadataIncludeFilter = new MultiMap<>(); 287 288 @Option(name = "module-metadata-exclude-filter", 289 description = "Exclude modules for execution based on matching of metadata fields: " 290 + "for any of the specified filter name and value, if a module has a metadata " 291 + "field with the same name and value, it will be excluded. When both module " 292 + "inclusion and exclusion rules are applied, inclusion rules will be " 293 + "evaluated first.") 294 private MultiMap<String, String> mModuleMetadataExcludeFilter = new MultiMap<>(); 295 296 private int mTotalShards; 297 private Integer mShardIndex = null; 298 private IModuleRepo mModuleRepo; 299 private ITestDevice mDevice; 300 private CompatibilityBuildHelper mBuildHelper; 301 302 // variables used for local sharding scenario 303 private static CountDownLatch sPreparedLatch; 304 private boolean mIsLocalSharding = false; 305 private boolean mIsSharded = false; 306 307 private IInvocationContext mInvocationContext; 308 309 /** 310 * Create a new {@link CompatibilityTest} that will run the default list of 311 * modules. 312 */ 313 public CompatibilityTest() { 314 this(1 /* totalShards */, new ModuleRepo(), 0); 315 } 316 317 /** 318 * Create a new {@link CompatibilityTest} that will run a sublist of 319 * modules. 320 */ 321 public CompatibilityTest(int totalShards, IModuleRepo moduleRepo, Integer shardIndex) { 322 if (totalShards < 1) { 323 throw new IllegalArgumentException( 324 "Must be at least 1 shard. Given:" + totalShards); 325 } 326 mTotalShards = totalShards; 327 mModuleRepo = moduleRepo; 328 mShardIndex = shardIndex; 329 } 330 331 /** 332 * {@inheritDoc} 333 */ 334 @Override 335 public ITestDevice getDevice() { 336 return mDevice; 337 } 338 339 /** 340 * {@inheritDoc} 341 */ 342 @Override 343 public void setDevice(ITestDevice device) { 344 mDevice = device; 345 } 346 347 /** 348 * {@inheritDoc} 349 */ 350 @Override 351 public void setBuild(IBuildInfo buildInfo) { 352 mBuildHelper = new CompatibilityBuildHelper(buildInfo); 353 } 354 355 /** 356 * {@inheritDoc} 357 */ 358 @Override 359 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 360 try { 361 List<ISystemStatusChecker> checkers = new ArrayList<>(); 362 // Get system status checkers 363 if (mSkipAllSystemStatusCheck) { 364 CLog.d("Skipping system status checkers"); 365 } else { 366 checkSystemStatusBlackAndWhiteList(); 367 for (ISystemStatusChecker checker : mListCheckers) { 368 if(shouldIncludeSystemStatusChecker(checker)) { 369 checkers.add(checker); 370 } 371 } 372 } 373 374 LinkedList<IModuleDef> modules = initializeModuleRepo(); 375 376 mExcludeFilters.clear(); 377 mIncludeFilters.clear(); 378 // Update BuildInfo in each shard to store the original command-line arguments from 379 // the session to be retried. These arguments will be serialized in the report later. 380 if (mRetrySessionId != null) { 381 loadRetryCommandLineArgs(mRetrySessionId); 382 } 383 384 listener = new FailureListener(listener, getDevice(), mBugReportOnFailure, 385 mLogcatOnFailure, mScreenshotOnFailure, mRebootOnFailure, mMaxLogcatBytes); 386 int moduleCount = modules.size(); 387 if (moduleCount == 0) { 388 CLog.logAndDisplay(LogLevel.INFO, "No module to run on %s.", 389 mDevice.getSerialNumber()); 390 // Make sure we unlock other shards. 391 if (sPreparedLatch != null) { 392 sPreparedLatch.countDown(); 393 } 394 return; 395 } else { 396 int uniqueModuleCount = UniqueModuleCountUtil.countUniqueModules(modules); 397 CLog.logAndDisplay(LogLevel.INFO, "Starting %d test sub-module%s on %s", 398 uniqueModuleCount, (uniqueModuleCount > 1) ? "s" : "", 399 mDevice.getSerialNumber()); 400 } 401 402 if (mRebootBeforeTest) { 403 CLog.d("Rebooting device before test starts as requested."); 404 mDevice.reboot(); 405 } 406 407 if (mSkipConnectivityCheck) { 408 String clazz = NetworkConnectivityChecker.class.getCanonicalName(); 409 CLog.logAndDisplay(LogLevel.INFO, "\"--skip-connectivity-check\" is deprecated, " 410 + "please use \"--skip-system-status-check %s\" instead", clazz); 411 mSystemStatusCheckBlacklist.add(clazz); 412 } 413 414 // Set values and run preconditions 415 boolean isPrepared = true; // whether the device has been successfully prepared 416 for (int i = 0; i < moduleCount; i++) { 417 IModuleDef module = modules.get(i); 418 module.setBuild(mBuildHelper.getBuildInfo()); 419 module.setDevice(mDevice); 420 module.setPreparerWhitelist(mPreparerWhitelist); 421 // don't set a value if unspecified 422 if (mCollectTestsOnly != null) { 423 module.setCollectTestsOnly(mCollectTestsOnly); 424 } 425 isPrepared &= (module.prepare(mSkipPreconditions, mPreconditionArgs)); 426 } 427 if (!isPrepared) { 428 throw new RuntimeException(String.format("Failed preconditions on %s", 429 mDevice.getSerialNumber())); 430 } 431 if (mIsLocalSharding) { 432 try { 433 sPreparedLatch.countDown(); 434 int attempt = 1; 435 while(!sPreparedLatch.await(MINUTES_PER_PREP_ATTEMPT, TimeUnit.MINUTES)) { 436 if (attempt > NUM_PREP_ATTEMPTS || 437 InvocationFailureHandler.hasFailed(mBuildHelper)) { 438 CLog.logAndDisplay(LogLevel.ERROR, 439 "Incorrect preparation detected, exiting test run from %s", 440 mDevice.getSerialNumber()); 441 return; 442 } 443 CLog.logAndDisplay(LogLevel.WARN, "waiting on preconditions"); 444 attempt++; 445 } 446 } catch (InterruptedException e) { 447 throw new RuntimeException(e); 448 } 449 } 450 // Module Repo is not useful anymore 451 mModuleRepo.tearDown(); 452 mModuleRepo = null; 453 // Run the tests 454 while (!modules.isEmpty()) { 455 // Make sure we remove the modules from the reference list when we are done with 456 // them. 457 IModuleDef module = modules.poll(); 458 long start = System.currentTimeMillis(); 459 460 if (mRebootPerModule) { 461 if ("user".equals(mDevice.getProperty("ro.build.type"))) { 462 CLog.e("reboot-per-module should only be used during development, " 463 + "this is a\" user\" build device"); 464 } else { 465 CLog.logAndDisplay(LogLevel.INFO, "Rebooting device before starting next " 466 + "module"); 467 mDevice.reboot(); 468 } 469 } 470 471 // execute pre module execution checker 472 if (checkers != null && !checkers.isEmpty()) { 473 runPreModuleCheck(module.getName(), checkers, mDevice, listener); 474 } 475 IInvocationContext moduleContext = new InvocationContext(); 476 moduleContext.setConfigurationDescriptor(module.getConfigurationDescriptor()); 477 moduleContext.addInvocationAttribute(IModuleDef.MODULE_NAME, module.getName()); 478 moduleContext.addInvocationAttribute(IModuleDef.MODULE_ABI, 479 module.getAbi().getName()); 480 mInvocationContext.setModuleInvocationContext(moduleContext); 481 // Populate the module context with devices and builds 482 for (String deviceName : mInvocationContext.getDeviceConfigNames()) { 483 moduleContext.addAllocatedDevice( 484 deviceName, mInvocationContext.getDevice(deviceName)); 485 moduleContext.addDeviceBuildInfo( 486 deviceName, mInvocationContext.getBuildInfo(deviceName)); 487 } 488 module.setInvocationContext(moduleContext); 489 try { 490 listener.testModuleStarted(moduleContext); 491 module.run(listener); 492 } catch (DeviceUnresponsiveException due) { 493 // being able to catch a DeviceUnresponsiveException here implies that recovery 494 // was successful, and test execution should proceed to next module 495 ByteArrayOutputStream stack = new ByteArrayOutputStream(); 496 due.printStackTrace(new PrintWriter(stack, true)); 497 StreamUtil.close(stack); 498 CLog.w("Ignored DeviceUnresponsiveException because recovery was successful, " 499 + "proceeding with next module. Stack trace: %s", 500 stack.toString()); 501 CLog.w("This may be due to incorrect timeout setting on module %s", 502 module.getName()); 503 } finally { 504 // clear out module invocation context since we are now done with module 505 // execution 506 mInvocationContext.setModuleInvocationContext(null); 507 listener.testModuleEnded(); 508 } 509 long duration = System.currentTimeMillis() - start; 510 long expected = module.getRuntimeHint(); 511 long delta = Math.abs(duration - expected); 512 // Show warning if delta is more than 10% of expected 513 if (expected > 0 && ((float)delta / (float)expected) > 0.1f) { 514 CLog.logAndDisplay(LogLevel.WARN, 515 "Inaccurate runtime hint for %s, expected %s was %s", 516 module.getId(), 517 TimeUtil.formatElapsedTime(expected), 518 TimeUtil.formatElapsedTime(duration)); 519 } 520 if (checkers != null && !checkers.isEmpty()) { 521 runPostModuleCheck(module.getName(), checkers, mDevice, listener); 522 } 523 module = null; 524 } 525 } catch (FileNotFoundException fnfe) { 526 throw new RuntimeException("Failed to initialize modules", fnfe); 527 } 528 } 529 530 /** 531 * Initialize module repo. 532 * 533 * @return A list of module definition 534 * @throws DeviceNotAvailableException 535 * @throws FileNotFoundException 536 */ 537 protected LinkedList<IModuleDef> initializeModuleRepo() 538 throws DeviceNotAvailableException, FileNotFoundException { 539 // FIXME: Each shard will do a full initialization which is not optimal. Need a way 540 // to be more specific on what to initialize. 541 synchronized (mModuleRepo) { 542 if (!mModuleRepo.isInitialized()) { 543 setupFilters(); 544 // Initialize the repository, {@link CompatibilityBuildHelper#getTestsDir} can 545 // throw a {@link FileNotFoundException} 546 mModuleRepo.initialize(mTotalShards, mShardIndex, mBuildHelper.getTestsDir(), 547 getAbis(), mDeviceTokens, mTestArgs, mModuleArgs, mIncludeFilters, 548 mExcludeFilters, mModuleMetadataIncludeFilter, mModuleMetadataExcludeFilter, 549 mBuildHelper.getBuildInfo()); 550 551 // Add the entire list of modules to the CompatibilityBuildHelper for reporting 552 mBuildHelper.setModuleIds(mModuleRepo.getModuleIds()); 553 554 int count = UniqueModuleCountUtil.countUniqueModules(mModuleRepo.getTokenModules()) 555 + UniqueModuleCountUtil.countUniqueModules( 556 mModuleRepo.getNonTokenModules()); 557 CLog.logAndDisplay(LogLevel.INFO, "========================================"); 558 CLog.logAndDisplay(LogLevel.INFO, "Starting a run with %s unique modules.", count); 559 CLog.logAndDisplay(LogLevel.INFO, "========================================"); 560 } else { 561 CLog.d("ModuleRepo already initialized."); 562 } 563 // Get the tests to run in this shard 564 return mModuleRepo.getModules(getDevice().getSerialNumber(), mShardIndex); 565 } 566 } 567 568 /** 569 * Gets the set of ABIs supported by both Compatibility and the device under test 570 * 571 * @return The set of ABIs to run the tests on 572 * @throws DeviceNotAvailableException 573 */ 574 Set<IAbi> getAbis() throws DeviceNotAvailableException { 575 Set<IAbi> abis = new LinkedHashSet<>(); 576 Set<String> archAbis = getAbisForBuildTargetArch(); 577 if (mPrimaryAbiRun) { 578 if (mAbiName == null) { 579 // Get the primary from the device and make it the --abi to run. 580 mAbiName = mDevice.getProperty("ro.product.cpu.abi").trim(); 581 } else { 582 CLog.d("Option --%s supersedes the option --%s, using abi: %s", ABI_OPTION, 583 PRIMARY_ABI_RUN, mAbiName); 584 } 585 } 586 if (mAbiName != null) { 587 // A particular abi was requested, it still need to be supported by the build. 588 if ((!mSkipHostArchCheck && !archAbis.contains(mAbiName)) || 589 !AbiUtils.isAbiSupportedByCompatibility(mAbiName)) { 590 throw new IllegalArgumentException(String.format("Your CTS hasn't been built with " 591 + "abi '%s' support, this CTS currently supports '%s'.", 592 mAbiName, archAbis)); 593 } else { 594 abis.add(new Abi(mAbiName, AbiUtils.getBitness(mAbiName))); 595 return abis; 596 } 597 } else { 598 // Run on all abi in common between the device and CTS. 599 List<String> deviceAbis = Arrays.asList(AbiFormatter.getSupportedAbis(mDevice, "")); 600 for (String abi : deviceAbis) { 601 if ((mSkipHostArchCheck || archAbis.contains(abi)) && 602 AbiUtils.isAbiSupportedByCompatibility(abi)) { 603 abis.add(new Abi(abi, AbiUtils.getBitness(abi))); 604 } else { 605 CLog.d("abi '%s' is supported by device but not by this CTS build (%s), tests " 606 + "will not run against it.", abi, archAbis); 607 } 608 } 609 if (abis.isEmpty()) { 610 throw new IllegalArgumentException(String.format("None of the abi supported by this" 611 + " CTS build ('%s') are supported by the device ('%s').", 612 archAbis, deviceAbis)); 613 } 614 return abis; 615 } 616 } 617 618 /** 619 * Return the abis supported by the Host build target architecture. 620 * Exposed for testing. 621 */ 622 protected Set<String> getAbisForBuildTargetArch() { 623 return AbiUtils.getAbisForArch(TestSuiteInfo.getInstance().getTargetArch()); 624 } 625 626 /** 627 * Check that the system status checker specified by option are valid. 628 */ 629 protected void checkSystemStatusBlackAndWhiteList() { 630 for (String checker : mSystemStatusCheckWhitelist) { 631 try { 632 Class.forName(checker); 633 } catch (ClassNotFoundException e) { 634 ConfigurationException ex = new ConfigurationException( 635 String.format("--system-status-check-whitelist must contains valid class, " 636 + "%s was not found", checker), e); 637 throw new RuntimeException(ex); 638 } 639 } 640 for (String checker : mSystemStatusCheckBlacklist) { 641 try { 642 Class.forName(checker); 643 } catch (ClassNotFoundException e) { 644 ConfigurationException ex = new ConfigurationException( 645 String.format("--skip-system-status-check must contains valid class, " 646 + "%s was not found", checker), e); 647 throw new RuntimeException(ex); 648 } 649 } 650 } 651 652 /** 653 * Resolve the inclusion and exclusion logic of system status checkers 654 * 655 * @param s the {@link ISystemStatusChecker} to perform filtering logic on 656 * @return True if the {@link ISystemStatusChecker} should be included, false otherwise. 657 */ 658 private boolean shouldIncludeSystemStatusChecker(ISystemStatusChecker s) { 659 String clazz = s.getClass().getCanonicalName(); 660 boolean shouldInclude = mSystemStatusCheckWhitelist.isEmpty() 661 || mSystemStatusCheckWhitelist.contains(clazz); 662 boolean shouldExclude = !mSystemStatusCheckBlacklist.isEmpty() 663 && mSystemStatusCheckBlacklist.contains(clazz); 664 return shouldInclude && !shouldExclude; 665 } 666 667 @VisibleForTesting 668 void runPreModuleCheck(String moduleName, List<ISystemStatusChecker> checkers, 669 ITestDevice device, ITestLogger logger) throws DeviceNotAvailableException { 670 CLog.i("Running system status checker before module execution: %s", moduleName); 671 List<String> failures = new ArrayList<>(); 672 for (ISystemStatusChecker checker : checkers) { 673 StatusCheckerResult result = checker.preExecutionCheck(device); 674 if (!CheckStatus.SUCCESS.equals(result.getStatus())) { 675 failures.add(checker.getClass().getCanonicalName()); 676 CLog.w("System status checker [%s] failed", checker.getClass().getCanonicalName()); 677 } 678 } 679 if (!failures.isEmpty()) { 680 CLog.w("There are failed system status checkers: %s capturing a bugreport", 681 failures.toString()); 682 try (InputStreamSource bugSource = device.getBugreport()) { 683 logger.testLog(String.format("bugreport-checker-pre-module-%s", moduleName), 684 LogDataType.BUGREPORT, bugSource); 685 } 686 } 687 } 688 689 @VisibleForTesting 690 void runPostModuleCheck(String moduleName, List<ISystemStatusChecker> checkers, 691 ITestDevice device, ITestLogger logger) throws DeviceNotAvailableException { 692 CLog.i("Running system status checker after module execution: %s", moduleName); 693 List<String> failures = new ArrayList<>(); 694 for (ISystemStatusChecker checker : checkers) { 695 StatusCheckerResult result = checker.postExecutionCheck(device); 696 if (!CheckStatus.SUCCESS.equals(result.getStatus())) { 697 failures.add(checker.getClass().getCanonicalName()); 698 CLog.w("System status checker [%s] failed", checker.getClass().getCanonicalName()); 699 } 700 } 701 if (!failures.isEmpty()) { 702 CLog.w("There are failed system status checkers: %s capturing a bugreport", 703 failures.toString()); 704 try (InputStreamSource bugSource = device.getBugreport()) { 705 logger.testLog(String.format("bugreport-checker-post-module-%s", moduleName), 706 LogDataType.BUGREPORT, bugSource); 707 } 708 } 709 } 710 711 /** 712 * Sets the retry command-line args to be stored in the BuildInfo and serialized into the 713 * report upon completion of the invocation. 714 */ 715 void loadRetryCommandLineArgs(Integer sessionId) { 716 IInvocationResult result = null; 717 try { 718 result = ResultHandler.findResult(mBuildHelper.getResultsDir(), sessionId); 719 } catch (FileNotFoundException e) { 720 // We should never reach this point, because this method should only be called 721 // after setupFilters(), so result exists if we've gotten this far 722 throw new RuntimeException(e); 723 } 724 if (result == null) { 725 // Again, this should never happen 726 throw new IllegalArgumentException(String.format( 727 "Could not find session with id %d", sessionId)); 728 } 729 String retryCommandLineArgs = result.getCommandLineArgs(); 730 if (retryCommandLineArgs != null) { 731 mBuildHelper.setRetryCommandLineArgs(retryCommandLineArgs); 732 } 733 } 734 735 /** 736 * Sets the include/exclude filters up based on if a module name was given or whether this is a 737 * retry run. 738 */ 739 void setupFilters() throws DeviceNotAvailableException { 740 if (mRetrySessionId != null) { 741 // Load the invocation result 742 RetryFilterHelper helper = createRetryFilterHelper(mRetrySessionId); 743 helper.validateBuildFingerprint(mDevice); 744 helper.setCommandLineOptionsFor(this); 745 helper.populateRetryFilters(); 746 mIncludeFilters = helper.getIncludeFilters(); 747 mExcludeFilters = helper.getExcludeFilters(); 748 helper.tearDown(); 749 } else { 750 if (mSubPlan != null) { 751 ISubPlan subPlan = SubPlanHelper.getSubPlanByName(mBuildHelper, mSubPlan); 752 mIncludeFilters.addAll(subPlan.getIncludeFilters()); 753 mExcludeFilters.addAll(subPlan.getExcludeFilters()); 754 } 755 if (mModuleName != null) { 756 try { 757 List<String> modules = ModuleRepo.getModuleNamesMatching( 758 mBuildHelper.getTestsDir(), mModuleName); 759 if (modules.size() == 0) { 760 throw new IllegalArgumentException( 761 String.format("No modules found matching %s", mModuleName)); 762 } else if (modules.size() > 1) { 763 throw new IllegalArgumentException(String.format("Multiple modules found" 764 + " matching %s:\n%s\nWhich one did you mean?\n", 765 mModuleName, ArrayUtil.join("\n", modules))); 766 } else { 767 String module = modules.get(0); 768 cleanFilters(mIncludeFilters, module); 769 cleanFilters(mExcludeFilters, module); 770 mIncludeFilters.add( 771 new TestFilter(mAbiName, module, mTestName).toString()); 772 } 773 } catch (FileNotFoundException e) { 774 throw new RuntimeException(e); 775 } 776 } else if (mTestName != null) { 777 throw new IllegalArgumentException( 778 "Test name given without module name. Add --module <module-name>"); 779 } 780 } 781 } 782 783 /* Creates a new {@link RetryFilterHelper} from attributes of this object. */ 784 protected RetryFilterHelper createRetryFilterHelper(Integer retrySessionId) { 785 return new RetryFilterHelper(mBuildHelper, retrySessionId, 786 mSubPlan, mIncludeFilters, mExcludeFilters, mAbiName, mModuleName, mTestName, 787 mRetryType); 788 } 789 790 /* Helper method designed to remove filters in a list not applicable to the given module */ 791 private static void cleanFilters(Set<String> filters, String module) { 792 Set<String> cleanedFilters = new HashSet<String>(); 793 for (String filter : filters) { 794 if (module.equals(TestFilter.createFrom(filter).getName())) { 795 cleanedFilters.add(filter); // Module name matches, filter passes 796 } 797 } 798 filters.clear(); 799 filters.addAll(cleanedFilters); 800 } 801 802 /** 803 * {@inheritDoc} 804 */ 805 @Override 806 public Collection<IRemoteTest> split() { 807 if (mShards <= 1) { 808 return null; 809 } 810 mIsLocalSharding = true; 811 List<IRemoteTest> shardQueue = new LinkedList<>(); 812 for (int i = 0; i < mShards; i++) { 813 CompatibilityTest test = (CompatibilityTest) getTestShard(mShards, i); 814 test.mIsLocalSharding = true; 815 shardQueue.add(test); 816 } 817 sPreparedLatch = new CountDownLatch(shardQueue.size()); 818 return shardQueue; 819 } 820 821 /** 822 * {@inheritDoc} 823 */ 824 @Override 825 public Collection<IRemoteTest> split(int shardCount) { 826 if (shardCount <= 1 || mIsSharded) { 827 return null; 828 } 829 mIsSharded = true; 830 List<IRemoteTest> shardQueue = new LinkedList<>(); 831 for (int i = 0; i < shardCount; i++) { 832 CompatibilityTest test = (CompatibilityTest) getTestShard(shardCount, i); 833 shardQueue.add(test); 834 test.mIsSharded = true; 835 } 836 return shardQueue; 837 } 838 839 /** 840 * {@inheritDoc} 841 */ 842 @Override 843 public IRemoteTest getTestShard(int shardCount, int shardIndex) { 844 CompatibilityTest test = new CompatibilityTest(shardCount, mModuleRepo, shardIndex); 845 OptionCopier.copyOptionsNoThrow(this, test); 846 // Set the shard count because the copy option on the previous line 847 // copies over the mShard value 848 test.mShards = 0; 849 return test; 850 } 851 852 /** 853 * {@inheritDoc} 854 */ 855 @Override 856 public void setSystemStatusChecker(List<ISystemStatusChecker> systemCheckers) { 857 mListCheckers = systemCheckers; 858 } 859 860 @Override 861 public void setCollectTestsOnly(boolean collectTestsOnly) { 862 mCollectTestsOnly = collectTestsOnly; 863 } 864 865 /** 866 * Sets include-filters for the compatibility test 867 */ 868 public void setIncludeFilter(Set<String> includeFilters) { 869 mIncludeFilters.addAll(includeFilters); 870 } 871 872 /** 873 * Sets exclude-filters for the compatibility test 874 */ 875 public void setExcludeFilter(Set<String> excludeFilters) { 876 mExcludeFilters.addAll(excludeFilters); 877 } 878 879 @Override 880 public void setInvocationContext(IInvocationContext invocationContext) { 881 mInvocationContext = invocationContext; 882 } 883 884 /** 885 * @return the mSubPlan 886 */ 887 protected String getSubPlan() { 888 return mSubPlan; 889 } 890 891 /** 892 * @return the mIncludeFilters 893 */ 894 protected Set<String> getIncludeFilters() { 895 return mIncludeFilters; 896 } 897 898 /** 899 * @return the mExcludeFilters 900 */ 901 protected Set<String> getExcludeFilters() { 902 return mExcludeFilters; 903 } 904 905 /** 906 * @return the mModuleName 907 */ 908 protected String getModuleName() { 909 return mModuleName; 910 } 911 912 /** 913 * @return the mTestName 914 */ 915 protected String getTestName() { 916 return mTestName; 917 } 918 919 /** 920 * @return the mModuleArgs 921 */ 922 protected List<String> getModuleArgs() { 923 return mModuleArgs; 924 } 925 926 /** 927 * @return the mTestArgs 928 */ 929 protected List<String> getTestArgs() { 930 return mTestArgs; 931 } 932 933 /** 934 * @return the mRetryType 935 */ 936 protected RetryType getRetryType() { 937 return mRetryType; 938 } 939 940 /** 941 * @return the mAbiName 942 */ 943 protected String getAbiName() { 944 return mAbiName; 945 } 946 947 /** 948 * @return the mDeviceTokens 949 */ 950 protected List<String> getDeviceTokens() { 951 return mDeviceTokens; 952 } 953 954 /** 955 * @return the mModuleMetadataIncludeFilter 956 */ 957 protected MultiMap<String, String> getModuleMetadataIncludeFilter() { 958 return mModuleMetadataIncludeFilter; 959 } 960 961 /** 962 * @return the mModuleMetadataExcludeFilter 963 */ 964 protected MultiMap<String, String> getModuleMetadataExcludeFilter() { 965 return mModuleMetadataExcludeFilter; 966 } 967 968 /** 969 * @return the mTotalShards 970 */ 971 protected int getTotalShards() { 972 return mTotalShards; 973 } 974 975 /** 976 * @return the mShardIndex 977 */ 978 protected Integer getShardIndex() { 979 return mShardIndex; 980 } 981 982 /** 983 * @return the mBuildHelper 984 */ 985 protected CompatibilityBuildHelper getBuildHelper() { 986 return mBuildHelper; 987 } 988 989 /** 990 * @return the mInvocationContext 991 */ 992 protected IInvocationContext getInvocationContext() { 993 return mInvocationContext; 994 } 995 996 /** 997 * @return the mModuleRepo 998 */ 999 protected IModuleRepo getModuleRepo() { 1000 return mModuleRepo; 1001 } 1002 } 1003