1 /* 2 * Copyright (C) 2018 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.tradefed.build.BuildInfoKey.BuildInfoFileKey; 19 import com.android.tradefed.build.IBuildInfo; 20 import com.android.tradefed.config.IConfiguration; 21 import com.android.tradefed.config.Option; 22 import com.android.tradefed.config.Option.Importance; 23 import com.android.tradefed.config.OptionClass; 24 import com.android.tradefed.device.DeviceFoldableState; 25 import com.android.tradefed.device.DeviceNotAvailableException; 26 import com.android.tradefed.device.ITestDevice; 27 import com.android.tradefed.device.StubDevice; 28 import com.android.tradefed.error.HarnessRuntimeException; 29 import com.android.tradefed.invoker.logger.InvocationMetricLogger; 30 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey; 31 import com.android.tradefed.log.LogUtil.CLog; 32 import com.android.tradefed.result.FileInputStreamSource; 33 import com.android.tradefed.result.LogDataType; 34 import com.android.tradefed.result.error.InfraErrorIdentifier; 35 import com.android.tradefed.testtype.IAbi; 36 import com.android.tradefed.testtype.IRemoteTest; 37 import com.android.tradefed.testtype.ITestFileFilterReceiver; 38 import com.android.tradefed.testtype.ITestFilterReceiver; 39 import com.android.tradefed.testtype.suite.params.FoldableExpandingHandler; 40 import com.android.tradefed.testtype.suite.params.IModuleParameterHandler; 41 import com.android.tradefed.testtype.suite.params.ModuleParameters; 42 import com.android.tradefed.testtype.suite.params.ModuleParametersHelper; 43 import com.android.tradefed.testtype.suite.params.NegativeHandler; 44 import com.android.tradefed.util.ArrayUtil; 45 import com.android.tradefed.util.FileUtil; 46 47 import com.google.common.annotations.VisibleForTesting; 48 49 import java.io.File; 50 import java.io.FileNotFoundException; 51 import java.io.IOException; 52 import java.util.ArrayList; 53 import java.util.HashSet; 54 import java.util.LinkedHashMap; 55 import java.util.LinkedHashSet; 56 import java.util.List; 57 import java.util.Map; 58 import java.util.Map.Entry; 59 import java.util.Set; 60 import java.util.stream.Collectors; 61 62 /** A Test for running Compatibility Test Suite with new suite system. */ 63 @OptionClass(alias = "base-suite") 64 public class BaseTestSuite extends ITestSuite { 65 66 public static final String INCLUDE_FILTER_OPTION = "include-filter"; 67 public static final String EXCLUDE_FILTER_OPTION = "exclude-filter"; 68 public static final String MODULE_OPTION = "module"; 69 public static final char MODULE_OPTION_SHORT_NAME = 'm'; 70 public static final String TEST_ARG_OPTION = "test-arg"; 71 public static final String TEST_OPTION = "test"; 72 public static final char TEST_OPTION_SHORT_NAME = 't'; 73 public static final String CONFIG_PATTERNS_OPTION = "config-patterns"; 74 private static final String MODULE_ARG_OPTION = "module-arg"; 75 private static final String REVERSE_EXCLUDE_FILTERS = "reverse-exclude-filters"; 76 private static final int MAX_FILTER_DISPLAY = 20; 77 78 @Option( 79 name = INCLUDE_FILTER_OPTION, 80 description = 81 "the include module filters to apply. Format: '[abi] <module-name> [test]'. See" 82 + " documentation:" 83 + "https://source.android.com/docs/core/tests/tradefed/testing/through-suite/option-passing", 84 importance = Importance.ALWAYS) 85 private Set<String> mIncludeFilters = new LinkedHashSet<>(); 86 87 @Option( 88 name = EXCLUDE_FILTER_OPTION, 89 description = 90 "the exclude module filters to apply. Format: '[abi] <module-name> [test]'. See" 91 + " documentation:" 92 + "https://source.android.com/docs/core/tests/tradefed/testing/through-suite/option-passing", 93 importance = Importance.ALWAYS) 94 private Set<String> mExcludeFilters = new LinkedHashSet<>(); 95 96 @Option( 97 name = REVERSE_EXCLUDE_FILTERS, 98 description = 99 "Flip exclude-filters into include-filters, in order to run only the excluded " 100 + "set.") 101 private boolean mReverseExcludeFilters = false; 102 103 @Option( 104 name = MODULE_OPTION, 105 shortName = MODULE_OPTION_SHORT_NAME, 106 description = "the test module to run. Only works for configuration in the tests dir.", 107 importance = Importance.IF_UNSET) 108 private String mModuleName = null; 109 110 @Option( 111 name = TEST_OPTION, 112 shortName = TEST_OPTION_SHORT_NAME, 113 description = "the test to run.", 114 importance = Importance.IF_UNSET) 115 private String mTestName = null; 116 117 @Option( 118 name = MODULE_ARG_OPTION, 119 description = 120 "the arguments to pass to a module. The expected format is" 121 + "\"<module-name>:[{alias}]<arg-name>:[<arg-key>:=]<arg-value>\"", 122 importance = Importance.ALWAYS) 123 private List<String> mModuleArgs = new ArrayList<>(); 124 125 @Option( 126 name = TEST_ARG_OPTION, 127 description = 128 "The arguments to pass to a test or its preparers. The expected format is" 129 + "\"<test-class>:<arg-name>:[<arg-key>:=]<arg-value>\"", 130 importance = Importance.ALWAYS) 131 private List<String> mTestArgs = new ArrayList<>(); 132 133 @Option( 134 name = "run-suite-tag", 135 description = 136 "The tag that must be run. If specified, only configurations containing the " 137 + "matching suite tag will be able to run.") 138 private String mSuiteTag = null; 139 140 @Option( 141 name = "prioritize-host-config", 142 description = 143 "If there are duplicate test configs for host/target, prioritize the host" 144 + " config, otherwise use the target config.") 145 private boolean mPrioritizeHostConfig = false; 146 147 @Option( 148 name = "suite-config-prefix", 149 description = "Search only configs with given prefix for suite tags.") 150 private String mSuitePrefix = null; 151 152 @Option( 153 name = "skip-loading-config-jar", 154 description = 155 "Whether or not to skip loading configurations from the JAR on the classpath.") 156 private boolean mSkipJarLoading = false; 157 158 @Option( 159 name = CONFIG_PATTERNS_OPTION, 160 description = 161 "The pattern(s) of the configurations that should be loaded from a directory." 162 + " If none is explicitly specified, .*.xml and .*.config will be used." 163 + " Can be repeated.") 164 private List<String> mConfigPatterns = new ArrayList<>(); 165 166 @Option( 167 name = "enable-parameterized-modules", 168 description = 169 "Whether or not to enable parameterized modules. This is a feature flag for" 170 + " work in development.") 171 private boolean mEnableParameter = false; 172 173 @Option( 174 name = "enable-mainline-parameterized-modules", 175 description = 176 "Whether or not to enable mainline parameterized modules. This is a feature" 177 + " flag for work in development.") 178 private boolean mEnableMainlineParameter = false; 179 180 @Option( 181 name = "enable-optional-parameterization", 182 description = 183 "Whether or not to enable optional parameters. Optional parameters are " 184 + "parameters not usually used by default.") 185 private boolean mEnableOptionalParameter = false; 186 187 @Option( 188 name = "module-parameter", 189 description = 190 "Allows to run only one module parameter type instead of all the combinations." 191 + " For example: 'instant_app' would only run the instant_app version of " 192 + "modules") 193 private ModuleParameters mForceParameter = null; 194 195 @Option( 196 name = "exclude-module-parameters", 197 description = 198 "Exclude some modules parameter from being evaluated in the run" 199 + " combinations.For example: 'instant_app' would exclude all the" 200 + " instant_app version of modules.") 201 private Set<ModuleParameters> mExcludedModuleParameters = new HashSet<>(); 202 203 @Option( 204 name = "fail-on-everything-filtered", 205 description = 206 "Whether or not to fail the invocation in case test filter returns" 207 + " an empty result.") 208 private boolean mFailOnEverythingFiltered = false; 209 210 @Option( 211 name = "ignore-non-preloaded-mainline-module", 212 description = 213 "Skip installing the module(s) when the module(s) that are not" 214 + "preloaded on device. Otherwise an exception will be thrown.") 215 private boolean mIgnoreNonPreloadedMainlineModule = false; 216 217 @Option( 218 name = "load-configs-with-include-filters", 219 description = 220 "An experimental flag to improve the performance of loading test configs with " 221 + "given module defined in include-filter.") 222 private boolean mLoadConfigsWithIncludeFilters = false; 223 224 private SuiteModuleLoader mModuleRepo; 225 private Map<String, LinkedHashSet<SuiteTestFilter>> mIncludeFiltersParsed = 226 new LinkedHashMap<>(); 227 private Map<String, LinkedHashSet<SuiteTestFilter>> mExcludeFiltersParsed = 228 new LinkedHashMap<>(); 229 private List<File> mConfigPaths = new ArrayList<>(); 230 private Set<IAbi> mAbis = new LinkedHashSet<>(); 231 private Set<DeviceFoldableState> mFoldableStates = new LinkedHashSet<>(); 232 setSkipjarLoading(boolean skipJarLoading)233 public void setSkipjarLoading(boolean skipJarLoading) { 234 mSkipJarLoading = skipJarLoading; 235 } 236 237 /** {@inheritDoc} */ 238 @Override loadTests()239 public LinkedHashMap<String, IConfiguration> loadTests() { 240 try { 241 File testsDir = getTestsDir(); 242 try { 243 mFoldableStates = getFoldableStates(getDevice()); 244 } catch (UnsupportedOperationException e) { 245 // Foldable state isn't always supported 246 CLog.e(e); 247 } 248 setupFilters(testsDir); 249 mAbis = getAbis(getDevice()); 250 251 if (mReverseExcludeFilters) { 252 if (mExcludeFilters.isEmpty()) { 253 return new LinkedHashMap<String, IConfiguration>(); 254 } 255 mIncludeFilters.clear(); 256 mIncludeFilters.addAll(mExcludeFilters); 257 mExcludeFilters.clear(); 258 } 259 260 // Create and populate the filters here 261 SuiteModuleLoader.addFilters( 262 mIncludeFilters, mIncludeFiltersParsed, mAbis, mFoldableStates); 263 SuiteModuleLoader.addFilters( 264 mExcludeFilters, mExcludeFiltersParsed, mAbis, mFoldableStates); 265 266 String includeFilters = ""; 267 if (mIncludeFiltersParsed.size() > MAX_FILTER_DISPLAY) { 268 if (isSplitting()) { 269 includeFilters = "Includes: <too long to display>"; 270 } else { 271 File suiteIncludeFilters = null; 272 try { 273 suiteIncludeFilters = 274 FileUtil.createTempFile("suite-include-filters", ".txt"); 275 FileUtil.writeToFile(mIncludeFiltersParsed.toString(), suiteIncludeFilters); 276 logFilterFile( 277 suiteIncludeFilters, 278 suiteIncludeFilters.getName(), 279 LogDataType.TEXT); 280 includeFilters = 281 String.format("Includes: See %s", suiteIncludeFilters.getName()); 282 } catch (IOException e) { 283 CLog.e(e); 284 } finally { 285 FileUtil.deleteFile(suiteIncludeFilters); 286 } 287 } 288 } else if (mIncludeFiltersParsed.size() > 0) { 289 includeFilters = String.format("Includes: %s", mIncludeFiltersParsed.toString()); 290 } 291 292 String excludeFilters = ""; 293 if (mExcludeFiltersParsed.size() > MAX_FILTER_DISPLAY) { 294 if (isSplitting()) { 295 excludeFilters = "Excludes: <too long to display>"; 296 } else { 297 File suiteExcludeFilters = null; 298 try { 299 suiteExcludeFilters = 300 FileUtil.createTempFile("suite-exclude-filters", ".txt"); 301 FileUtil.writeToFile(mExcludeFiltersParsed.toString(), suiteExcludeFilters); 302 logFilterFile( 303 suiteExcludeFilters, 304 suiteExcludeFilters.getName(), 305 LogDataType.TEXT); 306 excludeFilters = 307 String.format("Excludes: See %s", suiteExcludeFilters.getName()); 308 } catch (IOException e) { 309 CLog.e(e); 310 } finally { 311 FileUtil.deleteFile(suiteExcludeFilters); 312 } 313 } 314 } else if (mExcludeFiltersParsed.size() > 0) { 315 excludeFilters = String.format("Excludes: %s", mExcludeFiltersParsed.toString()); 316 } 317 318 CLog.d( 319 "Initializing ModuleRepo\nABIs:%s\n" + "Test Args:%s\nModule Args:%s\n%s\n%s", 320 mAbis, mTestArgs, mModuleArgs, includeFilters, excludeFilters); 321 if (!mFoldableStates.isEmpty()) { 322 CLog.d("Foldable states: %s", mFoldableStates); 323 } 324 325 mModuleRepo = 326 createModuleLoader( 327 mIncludeFiltersParsed, mExcludeFiltersParsed, mTestArgs, mModuleArgs); 328 if (mForceParameter != null && !mEnableParameter) { 329 throw new IllegalArgumentException( 330 "'module-parameter' option was specified without " 331 + "'enable-parameterized-modules'"); 332 } 333 if (mEnableOptionalParameter && !mEnableParameter) { 334 throw new IllegalArgumentException( 335 "'enable-optional-parameterization' option was specified without " 336 + "'enable-parameterized-modules'"); 337 } 338 339 if (mEnableMainlineParameter) { 340 mModuleRepo.setMainlineParameterizedModules(mEnableMainlineParameter); 341 mModuleRepo.setInvocationContext(getInvocationContext()); 342 mModuleRepo.setOptimizeMainlineTest( 343 getConfiguration().getCommandOptions().getOptimizeMainlineTest()); 344 mModuleRepo.setIgnoreNonPreloadedMainlineModule(mIgnoreNonPreloadedMainlineModule); 345 } 346 347 mModuleRepo.setParameterizedModules(mEnableParameter); 348 mModuleRepo.setOptionalParameterizedModules(mEnableOptionalParameter); 349 mModuleRepo.setModuleParameter(mForceParameter); 350 mModuleRepo.setExcludedModuleParameters(mExcludedModuleParameters); 351 mModuleRepo.setFoldableStates(mFoldableStates); 352 mModuleRepo.setLoadConfigsWithIncludeFilters(mLoadConfigsWithIncludeFilters); 353 354 List<File> testsDirectories = new ArrayList<>(); 355 356 // Include host or target first in the search if it exists, we have to this in 357 // BaseTestSuite because it's the only one with the BuildInfo knowledge of linked files 358 if (mPrioritizeHostConfig) { 359 File hostSubDir = getBuildInfo().getFile(BuildInfoFileKey.HOST_LINKED_DIR); 360 if (hostSubDir != null && hostSubDir.exists()) { 361 testsDirectories.add(hostSubDir); 362 } 363 } else { 364 File targetSubDir = getBuildInfo().getFile(BuildInfoFileKey.TARGET_LINKED_DIR); 365 if (targetSubDir != null && targetSubDir.exists()) { 366 testsDirectories.add(targetSubDir); 367 } 368 } 369 370 // Finally add the full test cases directory in case there is no special sub-dir. 371 testsDirectories.add(testsDir); 372 // Actual loading of the configurations. 373 long start = System.currentTimeMillis(); 374 LinkedHashMap<String, IConfiguration> loadedTests = 375 loadingStrategy(mAbis, testsDirectories, mSuitePrefix, mSuiteTag); 376 long duration = System.currentTimeMillis() - start; 377 InvocationMetricLogger.addInvocationMetrics( 378 InvocationMetricKey.LOAD_TEST_CONFIGS_TIME, duration); 379 if (mFailOnEverythingFiltered 380 && loadedTests.isEmpty() 381 && !mIncludeFiltersParsed.isEmpty()) { 382 // remove modules with empty filters from the message 383 Map<String, LinkedHashSet<SuiteTestFilter>> includeFiltersCleaned = 384 mIncludeFiltersParsed.entrySet().stream() 385 .filter( 386 entry -> 387 entry.getValue() != null 388 && !entry.getValue().isEmpty()) 389 .collect( 390 Collectors.toMap( 391 Map.Entry::getKey, 392 Map.Entry::getValue, 393 (x, y) -> y, 394 LinkedHashMap::new)); 395 String errorMessage = 396 String.format( 397 "Include filter '%s' was specified but resulted in an empty test" 398 + " set.", 399 includeFiltersCleaned.toString()); 400 if (errorMessage.length() > 1000) { 401 CLog.e(errorMessage); 402 errorMessage = 403 String.format( 404 "Include filter was specified for %d modules but resulted in an" 405 + " empty test set. Check host log for complete list of" 406 + " include filters.", 407 includeFiltersCleaned.size()); 408 } 409 throw new HarnessRuntimeException( 410 errorMessage, InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 411 } 412 return loadedTests; 413 } catch (DeviceNotAvailableException e) { 414 throw new HarnessRuntimeException(e.getMessage(), e); 415 } catch (FileNotFoundException fnfe) { 416 throw new HarnessRuntimeException( 417 fnfe.getMessage(), fnfe, InfraErrorIdentifier.ARTIFACT_NOT_FOUND); 418 } 419 } 420 421 /** 422 * Default loading strategy will load from the resources and the tests directory. Can be 423 * extended or replaced. 424 * 425 * @param abis The set of abis to run against. 426 * @param testsDirs The tests directory. 427 * @param suitePrefix A prefix to filter the resource directory. 428 * @param suiteTag The suite tag a module should have to be included. Can be null. 429 * @return A list of loaded configuration for the suite. 430 */ loadingStrategy( Set<IAbi> abis, List<File> testsDirs, String suitePrefix, String suiteTag)431 public LinkedHashMap<String, IConfiguration> loadingStrategy( 432 Set<IAbi> abis, List<File> testsDirs, String suitePrefix, String suiteTag) { 433 LinkedHashMap<String, IConfiguration> loadedConfigs = new LinkedHashMap<>(); 434 // Load and return directly the specific config files. 435 if (!mConfigPaths.isEmpty()) { 436 CLog.d( 437 "Loading the specified configs path '%s' and skip loading from the resources.", 438 mConfigPaths); 439 return getModuleLoader().loadConfigsFromSpecifiedPaths(mConfigPaths, abis, suiteTag); 440 } 441 442 // Load configs that are part of the resources 443 if (!mSkipJarLoading) { 444 loadedConfigs.putAll( 445 getModuleLoader().loadConfigsFromJars(abis, suitePrefix, suiteTag)); 446 } 447 448 // Load the configs that are part of the tests dir 449 if (mConfigPatterns.isEmpty()) { 450 // If no special pattern was configured, use the default configuration patterns we know 451 mConfigPatterns.add(".*\\.config$"); 452 mConfigPatterns.add(".*\\.xml$"); 453 } 454 455 loadedConfigs.putAll( 456 getModuleLoader() 457 .loadConfigsFromDirectory( 458 testsDirs, abis, suitePrefix, suiteTag, mConfigPatterns)); 459 return loadedConfigs; 460 } 461 462 /** {@inheritDoc} */ 463 @Override setBuild(IBuildInfo buildInfo)464 public void setBuild(IBuildInfo buildInfo) { 465 super.setBuild(buildInfo); 466 } 467 468 /** Sets include-filters for the compatibility test */ setIncludeFilter(Set<String> includeFilters)469 public void setIncludeFilter(Set<String> includeFilters) { 470 mIncludeFilters.addAll(includeFilters); 471 } 472 473 /** Gets a copy of include-filters for the compatibility test */ getIncludeFilter()474 public Set<String> getIncludeFilter() { 475 return new LinkedHashSet<String>(mIncludeFilters); 476 } 477 clearIncludeFilter()478 public void clearIncludeFilter() { 479 mIncludeFilters.clear(); 480 } 481 482 /** Sets exclude-filters for the compatibility test */ setExcludeFilter(Set<String> excludeFilters)483 public void setExcludeFilter(Set<String> excludeFilters) { 484 mExcludeFilters.addAll(excludeFilters); 485 } 486 clearExcludeFilter()487 public void clearExcludeFilter() { 488 mExcludeFilters.clear(); 489 } 490 491 /** Gets a copy of exclude-filters for the compatibility test */ getExcludeFilter()492 public Set<String> getExcludeFilter() { 493 return new HashSet<String>(mExcludeFilters); 494 } 495 496 /** Returns the current {@link SuiteModuleLoader}. */ getModuleLoader()497 public SuiteModuleLoader getModuleLoader() { 498 return mModuleRepo; 499 } 500 reevaluateFilters()501 public void reevaluateFilters() { 502 SuiteModuleLoader.addFilters( 503 mIncludeFilters, mIncludeFiltersParsed, mAbis, mFoldableStates); 504 SuiteModuleLoader.addFilters( 505 mExcludeFilters, mExcludeFiltersParsed, mAbis, mFoldableStates); 506 if (getDirectModule() != null) { 507 // Remove all entries for unrelated modules 508 mExcludeFiltersParsed.keySet().removeIf(key -> !key.equals(getDirectModule().getId())); 509 // Also clean exclude filters left over 510 for (String filterString : new HashSet<>(mExcludeFilters)) { 511 SuiteTestFilter parentFilter = SuiteTestFilter.createFrom(filterString); 512 if (!parentFilter.getModuleId().equals(getDirectModule().getId())) { 513 mExcludeFilters.remove(filterString); 514 } 515 } 516 } 517 } 518 519 /** Adds module args */ addModuleArgs(Set<String> moduleArgs)520 public void addModuleArgs(Set<String> moduleArgs) { 521 mModuleArgs.addAll(moduleArgs); 522 } 523 524 /** Clear the stored module args out */ clearModuleArgs()525 void clearModuleArgs() { 526 mModuleArgs.clear(); 527 } 528 529 /** Add config patterns */ addConfigPatterns(List<String> patterns)530 public void addConfigPatterns(List<String> patterns) { 531 mConfigPatterns.addAll(patterns); 532 } 533 534 /** Set whether or not parameterized modules are enabled or not. */ setEnableParameterizedModules(boolean enableParameter)535 public void setEnableParameterizedModules(boolean enableParameter) { 536 mEnableParameter = enableParameter; 537 } 538 539 /** Set whether or not optional parameterized modules are enabled or not. */ setEnableOptionalParameterizedModules(boolean enableOptionalParameter)540 public void setEnableOptionalParameterizedModules(boolean enableOptionalParameter) { 541 mEnableOptionalParameter = enableOptionalParameter; 542 } 543 setModuleParameter(ModuleParameters forceParameter)544 public void setModuleParameter(ModuleParameters forceParameter) { 545 mForceParameter = forceParameter; 546 } 547 548 /** 549 * Create the {@link SuiteModuleLoader} responsible to load the {@link IConfiguration} and 550 * assign them some of the options. 551 * 552 * @param includeFiltersFormatted The formatted and parsed include filters. 553 * @param excludeFiltersFormatted The formatted and parsed exclude filters. 554 * @param testArgs the list of test ({@link IRemoteTest}) arguments. 555 * @param moduleArgs the list of module arguments. 556 * @return the created {@link SuiteModuleLoader}. 557 */ createModuleLoader( Map<String, LinkedHashSet<SuiteTestFilter>> includeFiltersFormatted, Map<String, LinkedHashSet<SuiteTestFilter>> excludeFiltersFormatted, List<String> testArgs, List<String> moduleArgs)558 public SuiteModuleLoader createModuleLoader( 559 Map<String, LinkedHashSet<SuiteTestFilter>> includeFiltersFormatted, 560 Map<String, LinkedHashSet<SuiteTestFilter>> excludeFiltersFormatted, 561 List<String> testArgs, 562 List<String> moduleArgs) { 563 return new SuiteModuleLoader( 564 includeFiltersFormatted, excludeFiltersFormatted, testArgs, moduleArgs); 565 } 566 567 /** 568 * Sets the include/exclude filters up based on if a module name was given. 569 * 570 * @throws FileNotFoundException if any file is not found. 571 */ setupFilters(File testsDir)572 protected void setupFilters(File testsDir) throws FileNotFoundException { 573 if (mModuleName == null) { 574 if (mTestName != null) { 575 throw new IllegalArgumentException( 576 "Test name given without module name. Add --module <module-name>"); 577 } 578 return; 579 } 580 // If this option (-m / --module) is set only the matching unique module should run. 581 Set<File> modules = 582 SuiteModuleLoader.getModuleNamesMatching( 583 testsDir, mSuitePrefix, String.format(".*%s.*.config", mModuleName)); 584 // If multiple modules match, do exact match. 585 if (modules.size() > 1) { 586 Set<File> newModules = new HashSet<>(); 587 String exactModuleName = String.format("%s.config", mModuleName); 588 for (File module : modules) { 589 if (module.getName().equals(exactModuleName)) { 590 newModules.add(module); 591 modules = newModules; 592 break; 593 } 594 } 595 } 596 if (modules.size() == 0) { 597 throw new HarnessRuntimeException( 598 String.format("No modules found matching %s", mModuleName), 599 InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 600 } else if (modules.size() > 1) { 601 throw new HarnessRuntimeException( 602 String.format( 603 "Multiple modules found matching %s:\n%s\nWhich one did you " 604 + "mean?\n", 605 mModuleName, ArrayUtil.join("\n", modules)), 606 InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 607 } else { 608 File mod = modules.iterator().next(); 609 String moduleName = mod.getName().replace(".config", ""); 610 checkFilters(mIncludeFilters, moduleName); 611 checkFilters(mExcludeFilters, moduleName); 612 mIncludeFilters.add( 613 new SuiteTestFilter(getRequestedAbi(), moduleName, mTestName).toString()); 614 // Create the matching filters for the parameterized version of it if needed. 615 if (mEnableParameter) { 616 for (ModuleParameters param : ModuleParameters.values()) { 617 Map<ModuleParameters, IModuleParameterHandler> moduleParamExpanded = 618 ModuleParametersHelper.resolveParam(param, mEnableOptionalParameter); 619 if (moduleParamExpanded == null) { 620 continue; 621 } 622 for (Entry<ModuleParameters, IModuleParameterHandler> moduleParam : 623 moduleParamExpanded.entrySet()) { 624 if (moduleParam.getValue() instanceof NegativeHandler) { 625 continue; 626 } 627 if (moduleParam.getValue() instanceof FoldableExpandingHandler) { 628 List<IModuleParameterHandler> foldableHandlers = 629 ((FoldableExpandingHandler) moduleParam.getValue()) 630 .expandHandler(mFoldableStates); 631 for (IModuleParameterHandler foldableHandler : foldableHandlers) { 632 String paramModuleName = 633 String.format( 634 "%s[%s]", 635 moduleName, 636 foldableHandler.getParameterIdentifier()); 637 mIncludeFilters.add( 638 new SuiteTestFilter( 639 getRequestedAbi(), 640 paramModuleName, 641 mTestName) 642 .toString()); 643 } 644 continue; 645 } 646 String paramModuleName = 647 String.format( 648 "%s[%s]", 649 moduleName, 650 moduleParam.getValue().getParameterIdentifier()); 651 mIncludeFilters.add( 652 new SuiteTestFilter(getRequestedAbi(), paramModuleName, mTestName) 653 .toString()); 654 } 655 } 656 } 657 } 658 } 659 660 @Override cleanUpSuiteSetup()661 public void cleanUpSuiteSetup() { 662 super.cleanUpSuiteSetup(); 663 // Clean the filters because at that point they have been applied to the runners. 664 // This can save several GB of memories during sharding. 665 mIncludeFilters.clear(); 666 mExcludeFilters.clear(); 667 mIncludeFiltersParsed.clear(); 668 mExcludeFiltersParsed.clear(); 669 } 670 671 /** 672 * Add the config path for {@link SuiteModuleLoader} to limit the search loading configurations. 673 * 674 * @param configPath A {@code File} with the absolute path of the configuration. 675 */ addConfigPaths(File configPath)676 void addConfigPaths(File configPath) { 677 mConfigPaths.add(configPath); 678 } 679 680 /** Clear the stored config paths out. */ clearConfigPaths()681 void clearConfigPaths() { 682 mConfigPaths.clear(); 683 } 684 685 /* Helper method designed to remove filters in a list not applicable to the given module */ checkFilters(Set<String> filters, String moduleName)686 private static void checkFilters(Set<String> filters, String moduleName) { 687 Set<String> cleanedFilters = new HashSet<String>(); 688 for (String filter : filters) { 689 SuiteTestFilter filterObject = SuiteTestFilter.createFrom(filter); 690 String filterName = filterObject.getName(); 691 String filterBaseName = filterObject.getBaseName(); 692 if (moduleName.equals(filterName) || moduleName.equals(filterBaseName)) { 693 cleanedFilters.add(filter); // Module name matches, filter passes 694 } 695 } 696 filters.clear(); 697 filters.addAll(cleanedFilters); 698 } 699 700 /* Return a {@link boolean} for the setting of prioritize-host-config.*/ getPrioritizeHostConfig()701 boolean getPrioritizeHostConfig() { 702 return mPrioritizeHostConfig; 703 } 704 705 /** 706 * Set option prioritize-host-config. 707 * 708 * @param prioritizeHostConfig true to prioritize host config, i.e., run host test if possible. 709 */ 710 @VisibleForTesting setPrioritizeHostConfig(boolean prioritizeHostConfig)711 protected void setPrioritizeHostConfig(boolean prioritizeHostConfig) { 712 mPrioritizeHostConfig = prioritizeHostConfig; 713 } 714 715 /** Log a file directly to the result reporter. */ logFilterFile(File filterFile, String dataName, LogDataType type)716 private void logFilterFile(File filterFile, String dataName, LogDataType type) { 717 if (getCurrentTestLogger() == null) { 718 return; 719 } 720 try (FileInputStreamSource source = new FileInputStreamSource(filterFile)) { 721 getCurrentTestLogger().testLog(dataName, type, source); 722 } 723 } 724 725 @Override shouldModuleRun(ModuleDefinition module)726 protected boolean shouldModuleRun(ModuleDefinition module) { 727 String moduleId = module.getId(); 728 LinkedHashSet<SuiteTestFilter> excludeFilters = mExcludeFiltersParsed.get(moduleId); 729 CLog.d("Filters for '%s': %s", moduleId, excludeFilters); 730 if (excludeFilters == null || excludeFilters.isEmpty()) { 731 return true; 732 } 733 for (SuiteTestFilter filter : excludeFilters) { 734 if (filter.getTest() == null) { 735 CLog.d("Skipping %s, it previously passed.", moduleId); 736 return false; 737 } 738 for (IRemoteTest test : module.getTests()) { 739 if (test instanceof ITestFileFilterReceiver) { 740 File excludeFilterFile = ((ITestFileFilterReceiver) test).getExcludeTestFile(); 741 if (excludeFilterFile == null) { 742 try { 743 excludeFilterFile = FileUtil.createTempFile("exclude-filter", ".txt"); 744 } catch (IOException e) { 745 throw new HarnessRuntimeException( 746 e.getMessage(), e, InfraErrorIdentifier.FAIL_TO_CREATE_FILE); 747 } 748 ((ITestFileFilterReceiver) test).setExcludeTestFile(excludeFilterFile); 749 } 750 try { 751 FileUtil.writeToFile(filter.getTest() + "\n", excludeFilterFile, true); 752 } catch (IOException e) { 753 CLog.e(e); 754 continue; 755 } 756 } else if (test instanceof ITestFilterReceiver) { 757 ((ITestFilterReceiver) test).addExcludeFilter(filter.getTest()); 758 } 759 } 760 } 761 return true; 762 } 763 getFoldableStates(ITestDevice device)764 protected Set<DeviceFoldableState> getFoldableStates(ITestDevice device) 765 throws DeviceNotAvailableException { 766 if (device == null || device.getIDevice() instanceof StubDevice) { 767 return mFoldableStates; 768 } 769 if (!mFoldableStates.isEmpty()) { 770 return mFoldableStates; 771 } 772 mFoldableStates = device.getFoldableStates(); 773 return mFoldableStates; 774 } 775 getRunSuiteTag()776 public String getRunSuiteTag() { 777 return mSuiteTag; 778 } 779 reverseExcludeFilters()780 public boolean reverseExcludeFilters() { 781 return mReverseExcludeFilters; 782 } 783 } 784