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.SuiteInfo; 20 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 21 import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker; 22 import com.android.compatibility.common.tradefed.targetprep.SystemStatusChecker; 23 import com.android.compatibility.common.tradefed.util.OptionHelper; 24 import com.android.compatibility.common.util.AbiUtils; 25 import com.android.compatibility.common.util.ICaseResult; 26 import com.android.compatibility.common.util.IInvocationResult; 27 import com.android.compatibility.common.util.IModuleResult; 28 import com.android.compatibility.common.util.ITestResult; 29 import com.android.compatibility.common.util.ResultHandler; 30 import com.android.compatibility.common.util.TestFilter; 31 import com.android.compatibility.common.util.TestStatus; 32 import com.android.ddmlib.Log.LogLevel; 33 import com.android.tradefed.build.IBuildInfo; 34 import com.android.tradefed.config.ArgsOptionParser; 35 import com.android.tradefed.config.ConfigurationException; 36 import com.android.tradefed.config.ConfigurationFactory; 37 import com.android.tradefed.config.IConfiguration; 38 import com.android.tradefed.config.IConfigurationFactory; 39 import com.android.tradefed.config.Option; 40 import com.android.tradefed.config.Option.Importance; 41 import com.android.tradefed.config.OptionClass; 42 import com.android.tradefed.config.OptionCopier; 43 import com.android.tradefed.device.DeviceNotAvailableException; 44 import com.android.tradefed.device.DeviceUnresponsiveException; 45 import com.android.tradefed.device.ITestDevice; 46 import com.android.tradefed.log.ITestLogger; 47 import com.android.tradefed.log.LogUtil.CLog; 48 import com.android.tradefed.result.ITestInvocationListener; 49 import com.android.tradefed.result.InputStreamSource; 50 import com.android.tradefed.result.LogDataType; 51 import com.android.tradefed.targetprep.ITargetPreparer; 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.IRemoteTest; 56 import com.android.tradefed.testtype.IShardableTest; 57 import com.android.tradefed.util.AbiFormatter; 58 import com.android.tradefed.util.ArrayUtil; 59 import com.android.tradefed.util.TimeUtil; 60 61 import java.io.ByteArrayOutputStream; 62 import java.io.FileNotFoundException; 63 import java.io.IOException; 64 import java.io.PrintWriter; 65 import java.util.ArrayList; 66 import java.util.Collection; 67 import java.util.HashSet; 68 import java.util.LinkedList; 69 import java.util.List; 70 import java.util.Set; 71 72 /** 73 * A Test for running Compatibility Suites 74 */ 75 @OptionClass(alias = "compatibility") 76 public class CompatibilityTest implements IDeviceTest, IShardableTest, IBuildReceiver { 77 78 public static final String INCLUDE_FILTER_OPTION = "include-filter"; 79 public static final String EXCLUDE_FILTER_OPTION = "exclude-filter"; 80 private static final String PLAN_OPTION = "plan"; 81 private static final String MODULE_OPTION = "module"; 82 private static final String TEST_OPTION = "test"; 83 private static final String MODULE_ARG_OPTION = "module-arg"; 84 private static final String TEST_ARG_OPTION = "test-arg"; 85 public static final String RETRY_OPTION = "retry"; 86 private static final String ABI_OPTION = "abi"; 87 private static final String SHARD_OPTION = "shards"; 88 public static final String SKIP_DEVICE_INFO_OPTION = "skip-device-info"; 89 public static final String SKIP_PRECONDITIONS_OPTION = "skip-preconditions"; 90 public static final String DEVICE_TOKEN_OPTION = "device-token"; 91 private static final String URL = "dynamic-config-url"; 92 93 /* API Key for compatibility test project, used for dynamic configuration */ 94 private static final String API_KEY = "AIzaSyAbwX5JRlmsLeygY2WWihpIJPXFLueOQ3U"; 95 96 97 @Option(name = PLAN_OPTION, 98 description = "the test suite plan to run, such as \"everything\" or \"cts\"", 99 importance = Importance.ALWAYS) 100 private String mSuitePlan; 101 102 @Option(name = INCLUDE_FILTER_OPTION, 103 description = "the include module filters to apply.", 104 importance = Importance.ALWAYS) 105 private List<String> mIncludeFilters = new ArrayList<>(); 106 107 @Option(name = EXCLUDE_FILTER_OPTION, 108 description = "the exclude module filters to apply.", 109 importance = Importance.ALWAYS) 110 private List<String> mExcludeFilters = new ArrayList<>(); 111 112 @Option(name = MODULE_OPTION, 113 shortName = 'm', 114 description = "the test module to run.", 115 importance = Importance.IF_UNSET) 116 private String mModuleName = null; 117 118 @Option(name = TEST_OPTION, 119 shortName = 't', 120 description = "the test run.", 121 importance = Importance.IF_UNSET) 122 private String mTestName = null; 123 124 @Option(name = MODULE_ARG_OPTION, 125 description = "the arguments to pass to a module. The expected format is" 126 + "\"<module-name>:<arg-name>:<arg-value>\"", 127 importance = Importance.ALWAYS) 128 private List<String> mModuleArgs = new ArrayList<>(); 129 130 @Option(name = TEST_ARG_OPTION, 131 description = "the arguments to pass to a test. The expected format is" 132 + "\"<test-class>:<arg-name>:<arg-value>\"", 133 importance = Importance.ALWAYS) 134 private List<String> mTestArgs = new ArrayList<>(); 135 136 @Option(name = RETRY_OPTION, 137 shortName = 'r', 138 description = "retry a previous session.", 139 importance = Importance.IF_UNSET) 140 private Integer mRetrySessionId = null; 141 142 @Option(name = ABI_OPTION, 143 shortName = 'a', 144 description = "the abi to test.", 145 importance = Importance.IF_UNSET) 146 private String mAbiName = null; 147 148 @Option(name = SHARD_OPTION, 149 description = "split the modules up to run on multiple devices concurrently.") 150 private int mShards = 1; 151 152 @Option(name = URL, 153 description = "Specify the url for override config") 154 private String mURL = "https://androidpartner.googleapis.com/v1/dynamicconfig/" 155 + "suites/{suite-name}/modules/{module}/version/{version}?key=" + API_KEY; 156 157 @Option(name = SKIP_DEVICE_INFO_OPTION, 158 shortName = 'd', 159 description = "Whether device info collection should be skipped") 160 private boolean mSkipDeviceInfo = false; 161 162 @Option(name = SKIP_PRECONDITIONS_OPTION, 163 shortName = 'o', 164 description = "Whether preconditions should be skipped") 165 private boolean mSkipPreconditions = false; 166 167 @Option(name = DEVICE_TOKEN_OPTION, 168 description = "Holds the devices' tokens, used when scheduling tests that have" 169 + "prerequisits such as requiring a SIM card. Format is <serial>:<token>", 170 importance = Importance.ALWAYS) 171 private List<String> mDeviceTokens = new ArrayList<>(); 172 173 @Option(name = "bugreport-on-failure", 174 description = "Take a bugreport on every test failure. " + 175 "Warning: can potentially use a lot of disk space.") 176 private boolean mBugReportOnFailure = false; 177 178 @Option(name = "logcat-on-failure", 179 description = "Take a logcat snapshot on every test failure.") 180 private boolean mLogcatOnFailure = false; 181 182 @Option(name = "screenshot-on-failure", 183 description = "Take a screenshot on every test failure.") 184 private boolean mScreenshotOnFailure = false; 185 186 @Option(name = "reboot-before-test", 187 description = "Reboot the device before the test suite starts.") 188 private boolean mRebootBeforeTest = false; 189 190 @Option(name = "reboot-on-failure", 191 description = "Reboot the device after every test failure.") 192 private boolean mRebootOnFailure = false; 193 194 @Option(name = "reboot-per-module", 195 description = "Reboot the device before every module run.") 196 private boolean mRebootPerModule = false; 197 198 @Option(name = "skip-connectivity-check", 199 description = "Don't verify device connectivity between module execution.") 200 private boolean mSkipConnectivityCheck = false; 201 202 @Option(name = "preparer-whitelist", 203 description = "Only run specific preparers." 204 + "Specify zero or more ITargetPreparers as canonical class names. " 205 + "e.g. \"com.android.compatibility.common.tradefed.targetprep.ApkInstaller\" " 206 + "If not specified, all configured preparers are run.") 207 private Set<String> mPreparerWhitelist = new HashSet<>(); 208 209 @Option(name = "skip-all-system-status-check", 210 description = "Whether all system status check between modules should be skipped") 211 private boolean mSkipAllSystemStatusCheck = false; 212 213 @Option(name = "skip-system-status-check", 214 description = "Disable specific system status checkers." 215 + "Specify zero or more SystemStatusChecker as canonical class names. e.g. " 216 + "\"com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker\" " 217 + "If not specified, all configured or whitelisted system status checkers are run.") 218 private Set<String> mSystemStatusCheckBlacklist = new HashSet<>(); 219 220 @Option(name = "system-status-check-whitelist", 221 description = "Only run specific system status checkers." 222 + "Specify zero or more SystemStatusChecker as canonical class names. e.g. " 223 + "\"com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker\" " 224 + "If not specified, all configured system status checkers are run.") 225 private Set<String> mSystemStatusCheckWhitelist = new HashSet<>(); 226 227 @Option(name = "system-status-checker-config", description = "Configuration file for system " 228 + "status checkers invoked between module execution.") 229 private String mSystemStatusCheckerConfig = "system-status-checkers"; 230 231 private int mTotalShards; 232 private IModuleRepo mModuleRepo; 233 private ITestDevice mDevice; 234 private CompatibilityBuildHelper mBuildHelper; 235 236 /** 237 * Create a new {@link CompatibilityTest} that will run the default list of 238 * modules. 239 */ CompatibilityTest()240 public CompatibilityTest() { 241 this(1 /* totalShards */, new ModuleRepo()); 242 } 243 244 /** 245 * Create a new {@link CompatibilityTest} that will run a sublist of 246 * modules. 247 */ CompatibilityTest(int totalShards, IModuleRepo moduleRepo)248 public CompatibilityTest(int totalShards, IModuleRepo moduleRepo) { 249 if (totalShards < 1) { 250 throw new IllegalArgumentException( 251 "Must be at least 1 shard. Given:" + totalShards); 252 } 253 mTotalShards = totalShards; 254 mModuleRepo = moduleRepo; 255 } 256 257 /** 258 * {@inheritDoc} 259 */ 260 @Override getDevice()261 public ITestDevice getDevice() { 262 return mDevice; 263 } 264 265 /** 266 * {@inheritDoc} 267 */ 268 @Override setDevice(ITestDevice device)269 public void setDevice(ITestDevice device) { 270 mDevice = device; 271 } 272 273 /** 274 * {@inheritDoc} 275 */ 276 @Override setBuild(IBuildInfo buildInfo)277 public void setBuild(IBuildInfo buildInfo) { 278 mBuildHelper = new CompatibilityBuildHelper(buildInfo); 279 // Initializing the mBuildHelper also updates properties in buildInfo. 280 // TODO(nicksauer): Keeping invocation properties around via buildInfo 281 // is confusing and would be better done in an "InvocationInfo". 282 // Note, the current time is used to generated the result directory. 283 mBuildHelper.init(mSuitePlan, mURL, System.currentTimeMillis()); 284 } 285 286 /** 287 * {@inheritDoc} 288 */ 289 @Override run(ITestInvocationListener listener)290 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 291 try { 292 // Synchronized so only one shard enters and sets up the moduleRepo. When the other 293 // shards enter after this, moduleRepo is already initialized so they dont do anything 294 synchronized (mModuleRepo) { 295 if (!mModuleRepo.isInitialized()) { 296 setupFilters(); 297 // Initialize the repository, {@link CompatibilityBuildHelper#getTestsDir} can 298 // throw a {@link FileNotFoundException} 299 mModuleRepo.initialize(mTotalShards, mBuildHelper.getTestsDir(), getAbis(), 300 mDeviceTokens, mTestArgs, mModuleArgs, mIncludeFilters, 301 mExcludeFilters, mBuildHelper.getBuildInfo()); 302 303 // Add the entire list of modules to the CompatibilityBuildHelper for reporting 304 mBuildHelper.setModuleIds(mModuleRepo.getModuleIds()); 305 } 306 307 } 308 // Get the tests to run in this shard 309 List<IModuleDef> modules = mModuleRepo.getModules(getDevice().getSerialNumber()); 310 311 listener = new FailureListener(listener, getDevice(), mBugReportOnFailure, 312 mLogcatOnFailure, mScreenshotOnFailure, mRebootOnFailure); 313 int moduleCount = modules.size(); 314 CLog.logAndDisplay(LogLevel.INFO, "Starting %d module%s on %s", moduleCount, 315 (moduleCount > 1) ? "s" : "", mDevice.getSerialNumber()); 316 if (mRebootBeforeTest) { 317 CLog.d("Rebooting device before test starts as requested."); 318 mDevice.reboot(); 319 } 320 321 if (mSkipConnectivityCheck) { 322 String clazz = NetworkConnectivityChecker.class.getCanonicalName(); 323 CLog.logAndDisplay(LogLevel.INFO, "\"--skip-connectivity-check\" is deprecated, " 324 + "please use \"--skip-system-status-check %s\" instead", clazz); 325 mSystemStatusCheckBlacklist.add(clazz); 326 } 327 328 // Get system status checkers 329 List<SystemStatusChecker> checkers = null; 330 if (!mSkipAllSystemStatusCheck) { 331 try { 332 checkers = initSystemStatusCheckers(); 333 } catch (ConfigurationException ce) { 334 throw new RuntimeException("failed to load system status checker config", ce); 335 } 336 } 337 338 // Set values and run preconditions 339 for (int i = 0; i < moduleCount; i++) { 340 IModuleDef module = modules.get(i); 341 module.setBuild(mBuildHelper.getBuildInfo()); 342 module.setDevice(mDevice); 343 module.setPreparerWhitelist(mPreparerWhitelist); 344 module.prepare(mSkipPreconditions); 345 } 346 // Run the tests 347 for (int i = 0; i < moduleCount; i++) { 348 IModuleDef module = modules.get(i); 349 long start = System.currentTimeMillis(); 350 351 if (mRebootPerModule) { 352 if ("user".equals(mDevice.getProperty("ro.build.type"))) { 353 CLog.e("reboot-per-module should only be used during development, " 354 + "this is a\" user\" build device"); 355 } else { 356 CLog.logAndDisplay(LogLevel.INFO, "Rebooting device before starting next " 357 + "module"); 358 mDevice.reboot(); 359 } 360 } 361 362 // execute pre module execution checker 363 runPreModuleCheck(module.getName(), checkers, mDevice, listener); 364 try { 365 module.run(listener); 366 } catch (DeviceUnresponsiveException due) { 367 // being able to catch a DeviceUnresponsiveException here implies that recovery 368 // was successful, and test execution should proceed to next module 369 ByteArrayOutputStream stack = new ByteArrayOutputStream(); 370 due.printStackTrace(new PrintWriter(stack, true)); 371 try { 372 stack.close(); 373 } catch (IOException ioe) { 374 // won't happen on BAOS 375 } 376 CLog.w("Ignored DeviceUnresponsiveException because recovery was successful, " 377 + "proceeding with next module. Stack trace: %s", 378 stack.toString()); 379 CLog.w("This may be due to incorrect timeout setting on module %s", 380 module.getName()); 381 } 382 long duration = System.currentTimeMillis() - start; 383 long expected = module.getRuntimeHint(); 384 long delta = Math.abs(duration - expected); 385 // Show warning if delta is more than 10% of expected 386 if (expected > 0 && ((float)delta / (float)expected) > 0.1f) { 387 CLog.logAndDisplay(LogLevel.WARN, 388 "Inaccurate runtime hint for %s, expected %s was %s", 389 module.getId(), 390 TimeUtil.formatElapsedTime(expected), 391 TimeUtil.formatElapsedTime(duration)); 392 } 393 runPostModuleCheck(module.getName(), checkers, mDevice, listener); 394 } 395 } catch (FileNotFoundException fnfe) { 396 throw new RuntimeException("Failed to initialize modules", fnfe); 397 } 398 } 399 400 /** 401 * Gets the set of ABIs supported by both Compatibility and the device under test 402 * 403 * @return The set of ABIs to run the tests on 404 * @throws DeviceNotAvailableException 405 */ getAbis()406 Set<IAbi> getAbis() throws DeviceNotAvailableException { 407 Set<IAbi> abis = new HashSet<>(); 408 Set<String> archAbis = AbiUtils.getAbisForArch(SuiteInfo.TARGET_ARCH); 409 for (String abi : AbiFormatter.getSupportedAbis(mDevice, "")) { 410 // Only test against ABIs supported by Compatibility, and if the 411 // --abi option was given, it must match. 412 if (AbiUtils.isAbiSupportedByCompatibility(abi) && archAbis.contains(abi) 413 && (mAbiName == null || mAbiName.equals(abi))) { 414 abis.add(new Abi(abi, AbiUtils.getBitness(abi))); 415 } 416 } 417 if (abis == null || abis.isEmpty()) { 418 if (mAbiName == null) { 419 throw new IllegalArgumentException("Could not get device's ABIs"); 420 } else { 421 throw new IllegalArgumentException(String.format( 422 "Device %s doesn't support %s", mDevice.getSerialNumber(), mAbiName)); 423 } 424 } 425 return abis; 426 } 427 initSystemStatusCheckers()428 private List<SystemStatusChecker> initSystemStatusCheckers() throws ConfigurationException { 429 IConfigurationFactory cf = ConfigurationFactory.getInstance(); 430 IConfiguration config = cf.createConfigurationFromArgs( 431 new String[]{mSystemStatusCheckerConfig}); 432 // only checks the target preparers from the config 433 List<ITargetPreparer> preparers = config.getTargetPreparers(); 434 List<SystemStatusChecker> checkers = new ArrayList<>(); 435 for (ITargetPreparer p : preparers) { 436 if (p instanceof SystemStatusChecker) { 437 SystemStatusChecker s = (SystemStatusChecker)p; 438 if (shouldIncludeSystemStatusChecker(s)) { 439 checkers.add(s); 440 } else { 441 CLog.i("%s skipped because it's not whitelisted.", 442 s.getClass().getCanonicalName()); 443 } 444 } else { 445 CLog.w("Preparer %s does not have type %s, ignored ", 446 p.getClass().getCanonicalName(), 447 SystemStatusChecker.class.getCanonicalName()); 448 } 449 } 450 return checkers; 451 } 452 453 /** 454 * Resolve the inclusion and exclusion logic of system status checkers 455 * 456 * @param s the {@link SystemStatusChecker} to perform filtering logic on 457 * @return 458 */ shouldIncludeSystemStatusChecker(SystemStatusChecker s)459 private boolean shouldIncludeSystemStatusChecker(SystemStatusChecker s) { 460 String clazz = s.getClass().getCanonicalName(); 461 boolean shouldInclude = mSystemStatusCheckWhitelist.isEmpty() 462 || mSystemStatusCheckWhitelist.contains(clazz); 463 boolean shouldExclude = !mSystemStatusCheckBlacklist.isEmpty() 464 && mSystemStatusCheckBlacklist.contains(clazz); 465 return shouldInclude && !shouldExclude; 466 } 467 runPreModuleCheck(String moduleName, List<SystemStatusChecker> checkers, ITestDevice device, ITestLogger logger)468 private void runPreModuleCheck(String moduleName, List<SystemStatusChecker> checkers, 469 ITestDevice device, ITestLogger logger) throws DeviceNotAvailableException { 470 CLog.i("Running system status checker before module execution: %s", moduleName); 471 List<String> failures = new ArrayList<>(); 472 for (SystemStatusChecker checker : checkers) { 473 boolean result = checker.preExecutionCheck(device); 474 if (!result) { 475 failures.add(checker.getClass().getCanonicalName()); 476 CLog.w("System status checker [%s] failed with message: %s", 477 checker.getClass().getCanonicalName(), checker.getFailureMessage()); 478 } 479 } 480 if (!failures.isEmpty()) { 481 CLog.w("There are failed system status checkers: %s capturing a bugreport", 482 failures.toString()); 483 InputStreamSource bugSource = device.getBugreport(); 484 logger.testLog(String.format("bugreport-checker-pre-module-%s", moduleName), 485 LogDataType.TEXT, bugSource); 486 bugSource.cancel(); 487 } 488 } 489 runPostModuleCheck(String moduleName, List<SystemStatusChecker> checkers, ITestDevice device, ITestLogger logger)490 private void runPostModuleCheck(String moduleName, List<SystemStatusChecker> checkers, 491 ITestDevice device, ITestLogger logger) throws DeviceNotAvailableException { 492 CLog.i("Running system status checker after module execution: %s", moduleName); 493 List<String> failures = new ArrayList<>(); 494 for (SystemStatusChecker checker : checkers) { 495 boolean result = checker.postExecutionCheck(device); 496 if (!result) { 497 failures.add(checker.getClass().getCanonicalName()); 498 CLog.w("System status checker [%s] failed with message: %s", 499 checker.getClass().getCanonicalName(), checker.getFailureMessage()); 500 } 501 } 502 if (!failures.isEmpty()) { 503 CLog.w("There are failed system status checkers: %s capturing a bugreport", 504 failures.toString()); 505 InputStreamSource bugSource = device.getBugreport(); 506 logger.testLog(String.format("bugreport-checker-post-module-%s", moduleName), 507 LogDataType.TEXT, bugSource); 508 bugSource.cancel(); 509 } 510 } 511 512 /** 513 * Sets the include/exclude filters up based on if a module name was given or whether this is a 514 * retry run. 515 */ setupFilters()516 void setupFilters() throws DeviceNotAvailableException { 517 if (mRetrySessionId != null) { 518 // We're retrying so clear -m and -t options 519 // eventually reset these options with values given in the previous session 520 mModuleName = null; 521 mTestName = null; 522 // Load the invocation result 523 IInvocationResult result = null; 524 try { 525 result = ResultHandler.findResult(mBuildHelper.getResultsDir(), mRetrySessionId); 526 } catch (FileNotFoundException e) { 527 throw new RuntimeException(e); 528 } 529 if (result == null) { 530 throw new IllegalArgumentException(String.format( 531 "Could not find session with id %d", mRetrySessionId)); 532 } 533 534 String oldBuildFingerprint = result.getBuildFingerprint(); 535 String currentBuildFingerprint = mDevice.getProperty("ro.build.fingerprint"); 536 if (oldBuildFingerprint.equals(currentBuildFingerprint)) { 537 CLog.logAndDisplay(LogLevel.INFO, "Retrying session from: %s", 538 CompatibilityBuildHelper.getDirSuffix(result.getStartTime())); 539 } else { 540 throw new IllegalArgumentException(String.format( 541 "Device build fingerprint must match %s to retry session %d", 542 oldBuildFingerprint, mRetrySessionId)); 543 } 544 545 String retryCommandLineArgs = result.getCommandLineArgs(); 546 if (retryCommandLineArgs != null) { 547 // Copy the original command into the build helper so it can be serialized later 548 mBuildHelper.setRetryCommandLineArgs(retryCommandLineArgs); 549 try { 550 // parse the command-line string from the result file and set options 551 ArgsOptionParser parser = new ArgsOptionParser(this); 552 parser.parse(OptionHelper.getValidCliArgs(retryCommandLineArgs, this)); 553 } catch (ConfigurationException e) { 554 throw new RuntimeException(e); 555 } 556 } 557 // Append each test that failed or was not executed to the filters 558 for (IModuleResult module : result.getModules()) { 559 for (ICaseResult testResultList : module.getResults()) { 560 for (ITestResult testResult : testResultList.getResults(TestStatus.PASS)) { 561 // Create the filter for the test to be run. 562 TestFilter filter = new TestFilter( 563 module.getAbi(), module.getName(), testResult.getFullName()); 564 mExcludeFilters.add(filter.toString()); 565 } 566 } 567 } 568 } 569 if (mModuleName != null) { 570 mIncludeFilters.clear(); 571 try { 572 List<String> modules = ModuleRepo.getModuleNamesMatching( 573 mBuildHelper.getTestsDir(), mModuleName); 574 if (modules.size() == 0) { 575 throw new IllegalArgumentException( 576 String.format("No modules found matching %s", mModuleName)); 577 } else if (modules.size() > 1) { 578 throw new IllegalArgumentException(String.format( 579 "Multiple modules found matching %s:\n%s\nWhich one did you mean?\n", 580 mModuleName, ArrayUtil.join("\n", modules))); 581 } else { 582 String module = modules.get(0); 583 mIncludeFilters.add(new TestFilter(mAbiName, module, mTestName).toString()); 584 // We will run this module with previous exclusions, 585 // unless they exclude the whole module. 586 List<String> excludeFilters = new ArrayList<>(); 587 for (String excludeFilter : mExcludeFilters) { 588 TestFilter filter = TestFilter.createFrom(excludeFilter); 589 String name = filter.getName(); 590 // Add the filter if it applies to this module 591 if (module.equals(name)) { 592 excludeFilters.add(excludeFilter); 593 } 594 } 595 mExcludeFilters = excludeFilters; 596 } 597 } catch (FileNotFoundException e) { 598 e.printStackTrace(); 599 } 600 } else if (mTestName != null) { 601 throw new IllegalArgumentException( 602 "Test name given without module name. Add --module <module-name>"); 603 } else { 604 // If a module has an arg, assume it's included 605 for (String arg : mModuleArgs) { 606 mIncludeFilters.add(arg.split(":")[0]); 607 } 608 } 609 } 610 611 /** 612 * {@inheritDoc} 613 */ 614 @Override split()615 public Collection<IRemoteTest> split() { 616 if (mShards <= 1) { 617 return null; 618 } 619 620 List<IRemoteTest> shardQueue = new LinkedList<>(); 621 for (int i = 0; i < mShards; i++) { 622 CompatibilityTest test = new CompatibilityTest(mShards, mModuleRepo); 623 OptionCopier.copyOptionsNoThrow(this, test); 624 // Set the shard count because the copy option on the previous line 625 // copies over the mShard value 626 test.mShards = 0; 627 shardQueue.add(test); 628 } 629 630 return shardQueue; 631 } 632 633 } 634