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.IBuildInfo; 19 import com.android.tradefed.config.ConfigurationDescriptor; 20 import com.android.tradefed.config.IConfiguration; 21 import com.android.tradefed.config.Option; 22 import com.android.tradefed.error.HarnessRuntimeException; 23 import com.android.tradefed.invoker.logger.InvocationMetricLogger; 24 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey; 25 import com.android.tradefed.invoker.tracing.CloseableTraceScope; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.result.error.InfraErrorIdentifier; 28 import com.android.tradefed.testtype.IAbi; 29 import com.android.tradefed.testtype.IRemoteTest; 30 import com.android.tradefed.util.FileUtil; 31 import com.android.tradefed.util.ZipUtil2; 32 import com.android.tradefed.util.testmapping.TestInfo; 33 import com.android.tradefed.util.testmapping.TestMapping; 34 import com.android.tradefed.util.testmapping.TestOption; 35 36 import com.google.common.annotations.VisibleForTesting; 37 import com.google.common.base.Joiner; 38 import com.google.common.io.Files; 39 40 import org.apache.commons.compress.archivers.zip.ZipFile; 41 42 import java.io.File; 43 import java.io.IOException; 44 import java.time.Duration; 45 import java.util.ArrayList; 46 import java.util.Comparator; 47 import java.util.HashSet; 48 import java.util.LinkedHashMap; 49 import java.util.LinkedHashSet; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.Set; 53 import java.util.TreeSet; 54 import java.util.regex.Matcher; 55 import java.util.regex.Pattern; 56 import java.util.stream.Collectors; 57 58 /** 59 * Implementation of {@link BaseTestSuite} to run tests specified by option include-filter, or 60 * TEST_MAPPING files from build, as a suite. 61 */ 62 public class TestMappingSuiteRunner extends BaseTestSuite { 63 64 @Option( 65 name = "test-mapping-test-group", 66 description = 67 "Group of tests to run, e.g., presubmit, postsubmit. The suite runner " 68 + "shall load the tests defined in all TEST_MAPPING files in the source " 69 + "code, through build artifact test_mappings.zip." 70 ) 71 private String mTestGroup = null; 72 73 @Option( 74 name = "test-mapping-keyword", 75 description = 76 "Keyword to be matched to the `keywords` setting of a test configured in a" 77 + " TEST_MAPPING file. The test will only run if it has all the keywords" 78 + " specified in the option. If option test-mapping-test-group is not set," 79 + " test-mapping-keyword option is ignored as the tests to run are not" 80 + " loaded directly from TEST_MAPPING files but is supplied via the" 81 + " --include-filter arg.") 82 private Set<String> mKeywords = new LinkedHashSet<>(); 83 84 @Option( 85 name = "test-mapping-ignore-keyword", 86 description = 87 "Keyword to be ignored to the `keywords` setting of a test configured in " 88 + "a TEST_MAPPING file. If a test entry has specified keywords in " 89 + "their `keywords` attribute, the given keywords will be ignored. " 90 + "This allows a test mapping suite to support test entries requiring " 91 + "a keyword without running them on a new test suite.") 92 private Set<String> mIgnoreKeywords = new LinkedHashSet<>(); 93 94 @Option( 95 name = "force-test-mapping-module", 96 description = 97 "Run the specified tests only. The tests loaded from all TEST_MAPPING files in " 98 + "the source code will be filtered again to force run the specified tests." 99 ) 100 private Set<String> mTestModulesForced = new HashSet<>(); 101 102 @Option( 103 name = "test-mapping-path", 104 description = "Run tests according to the test mapping path." 105 ) 106 private List<String> mTestMappingPaths = new ArrayList<>(); 107 108 @Option( 109 name = RemoteTestTimeOutEnforcer.REMOTE_TEST_TIMEOUT_OPTION, 110 description = RemoteTestTimeOutEnforcer.REMOTE_TEST_TIMEOUT_DESCRIPTION 111 ) 112 private Duration mRemoteTestTimeOut = null; 113 114 @Option( 115 name = "use-test-mapping-path", 116 description = "Whether or not to run tests based on the given test mapping path." 117 ) 118 private boolean mUseTestMappingPath = false; 119 120 @Option( 121 name = "ignore-test-mapping-imports", 122 description = "Whether or not to ignore test mapping import paths.") 123 private boolean mIgnoreTestMappingImports = true; 124 125 @Option( 126 name = "test-mapping-allowed-tests-list", 127 description = 128 "A list of artifacts that contains allowed tests. Only tests in the lists " 129 + "will be run. If no list is specified, the tests will not be " 130 + "filtered by allowed tests.") 131 private Set<String> mAllowedTestLists = new HashSet<>(); 132 133 @Option( 134 name = "additional-test-mapping-zip", 135 description = 136 "A list of additional test_mappings.zip that contains TEST_MAPPING files. The " 137 + "runner will collect tests based on them. If none is specified, " 138 + "only the tests on the triggering device build will be run.") 139 private List<String> mAdditionalTestMappingZips = new ArrayList<>(); 140 141 @Option( 142 name = "test-mapping-matched-pattern-paths", 143 description = 144 "A list of modified paths that matches with a certain file_pattern in " 145 + "the TEST_MAPPING file. This is used only for Work Node, and handled " 146 + "by provider service.") 147 private Set<String> mMatchedPatternPaths = new HashSet<>(); 148 149 @Option( 150 name = "allow-empty-tests", 151 description = 152 "Whether or not to raise an exception if no tests to be ran. This is to " 153 + "provide a feasibility for test mapping sampling.") 154 private boolean mAllowEmptyTests = false; 155 156 @Option( 157 name = "force-full-run", 158 description = 159 "Whether or not to run full tests. It is to provide a feasibility for tests on " 160 + "kernel branches. The option should only be used for kernel tests.") 161 private boolean mForceFullRun = false; 162 163 @Option( 164 name = "report-import-paths", 165 description = "Whether or not to report import paths into AnTS.") 166 private boolean mReportImportPaths = false; 167 168 @Option( 169 name = "skip-option-check-in-child-proc", 170 description = 171 "Whether or not to skip @Options validations only in the subprocess" 172 + "Note that this shouldn't be set True by default or GCL.") 173 private boolean mSkipOptionCheckInChildProc = false; 174 175 /** Special definition in the test mapping structure. */ 176 private static final String TEST_MAPPING_INCLUDE_FILTER = "include-filter"; 177 178 private static final String TEST_MAPPING_EXCLUDE_FILTER = "exclude-filter"; 179 180 private IBuildInfo mBuildInfo; 181 TestMappingSuiteRunner()182 public TestMappingSuiteRunner() { 183 setSkipjarLoading(true); 184 } 185 loadTestInfos()186 public Set<TestInfo> loadTestInfos() { 187 try (CloseableTraceScope ignored = new CloseableTraceScope("loadTestInfos")) { 188 // Name of the tests 189 Set<String> testNames = new LinkedHashSet<>(); 190 Set<TestInfo> testInfosToRun = new LinkedHashSet<>(); 191 mBuildInfo = getBuildInfo(); 192 checkTestMappingOptions(); 193 if (mTestGroup != null) { 194 if (mForceFullRun) { 195 CLog.d( 196 "--force-full-run is specified, all tests in test group %s will be" 197 + " ran.", 198 mTestGroup); 199 mTestMappingPaths.clear(); 200 } 201 TestMapping testMapping = 202 new TestMapping(mTestMappingPaths, mIgnoreTestMappingImports); 203 testInfosToRun = 204 testMapping.getTests( 205 mBuildInfo, 206 mTestGroup, 207 getPrioritizeHostConfig(), 208 mKeywords, 209 mIgnoreKeywords, 210 mAdditionalTestMappingZips, 211 mMatchedPatternPaths); 212 if (!mTestModulesForced.isEmpty()) { 213 CLog.i("Filtering tests for the given names: %s", mTestModulesForced); 214 testInfosToRun = 215 testInfosToRun.stream() 216 .filter( 217 testInfo -> 218 mTestModulesForced.contains(testInfo.getName())) 219 .collect(Collectors.toSet()); 220 } 221 if (!mAllowedTestLists.isEmpty()) { 222 CLog.i("Filtering tests from allowed test lists: %s", mAllowedTestLists); 223 testInfosToRun = filterByAllowedTestLists(mBuildInfo, testInfosToRun); 224 } 225 if (testInfosToRun.isEmpty() && !mAllowEmptyTests) { 226 throw new HarnessRuntimeException( 227 String.format("No test found for the given group: %s.", mTestGroup), 228 InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 229 } 230 for (TestInfo testInfo : testInfosToRun) { 231 testNames.add(testInfo.getName()); 232 } 233 setIncludeFilter(testNames); 234 } 235 return testInfosToRun; 236 } 237 } 238 239 /** 240 * Load the tests configuration that will be run. Each tests is defined by a {@link 241 * IConfiguration} and a unique name under which it will report results. There are 2 ways to 242 * load tests for {@link TestMappingSuiteRunner}: 243 * 244 * <p>1. --test-mapping-test-group, which specifies the group of tests in TEST_MAPPING files. 245 * The runner will parse all TEST_MAPPING files in the source code through build artifact 246 * test_mappings.zip, and load tests grouped under the given test group. 247 * 248 * <p>2. --include-filter, which specifies the name of the test to run. The use case is for 249 * presubmit check to only run a list of tests related to the Cls to be verifies. The list of 250 * tests are compiled from the related TEST_MAPPING files in modified source code. 251 * 252 * @return a map of test name to the {@link IConfiguration} object of each test. 253 */ 254 @Override loadTests()255 public LinkedHashMap<String, IConfiguration> loadTests() { 256 Set<TestInfo> testInfosToRun = loadTestInfos(); 257 if (testInfosToRun.isEmpty() && getIncludeFilter().isEmpty()) { 258 // No need to load any test configs as there is no test info to run based on 259 // TEST_MAPPING files and include-filters. 260 return new LinkedHashMap<String, IConfiguration>(); 261 } 262 263 // load all the configurations with include-filter injected. 264 LinkedHashMap<String, IConfiguration> testConfigs = super.loadTests(); 265 266 try (CloseableTraceScope ignored = new CloseableTraceScope("testmapping:loadTests")) { 267 // Create and inject individual tests by calling super.loadTests() with each test info. 268 for (Map.Entry<String, IConfiguration> entry : testConfigs.entrySet()) { 269 List<IRemoteTest> allTests = new ArrayList<>(); 270 IConfiguration moduleConfig = entry.getValue(); 271 ConfigurationDescriptor configDescriptor = 272 moduleConfig.getConfigurationDescription(); 273 IAbi abi = configDescriptor.getAbi(); 274 // Get the parameterized module name by striping the abi information out. 275 String moduleName = entry.getKey().replace(String.format("%s ", abi.getName()), ""); 276 Set<TestInfo> testInfos = getTestInfos(testInfosToRun, moduleName); 277 // Only keep the same matching abi runner 278 allTests.addAll(createIndividualTests(testInfos, moduleConfig, abi)); 279 if (!allTests.isEmpty()) { 280 // Set back to IConfiguration only if IRemoteTests are created. 281 moduleConfig.setTests(allTests); 282 // Set test sources to ConfigurationDescriptor. 283 List<String> testSources = getTestSources(testInfos); 284 configDescriptor.addMetadata(TestMapping.TEST_SOURCES, testSources); 285 } 286 if (mRemoteTestTimeOut != null) { 287 // Add the timeout to metadata so that it can be used in the ModuleDefinition. 288 configDescriptor.addMetadata( 289 RemoteTestTimeOutEnforcer.REMOTE_TEST_TIMEOUT_OPTION, 290 mRemoteTestTimeOut.toString()); 291 } 292 } 293 } 294 return testConfigs; 295 } 296 clearTestGroup()297 public void clearTestGroup() { 298 mTestGroup = null; 299 } 300 clearTestMappingPaths()301 public void clearTestMappingPaths() { 302 mTestMappingPaths.clear(); 303 } 304 clearKeywords()305 public void clearKeywords() { 306 mKeywords.clear(); 307 } 308 309 /** 310 * Create individual tests with test infos for a module. 311 * 312 * @param testInfos A {@code Set<TestInfo>} containing multiple test options. 313 * @param moduleConfig The {@link IConfiguration} of the module config. 314 * @param abi The {@link IAbi} of abi information. 315 * @return The {@link List} that are injected with the test options. 316 */ 317 @VisibleForTesting createIndividualTests( Set<TestInfo> testInfos, IConfiguration moduleConfig, IAbi abi)318 List<IRemoteTest> createIndividualTests( 319 Set<TestInfo> testInfos, IConfiguration moduleConfig, IAbi abi) { 320 List<IRemoteTest> tests = new ArrayList<>(); 321 String configPath = moduleConfig.getName(); 322 // Save top-level exclude-filter test options so that we can inject them back 323 // afterwards when creating individual test. 324 Set<String> excludeFilterSet = getExcludeFilter(); 325 if (configPath == null) { 326 throw new RuntimeException(String.format("Configuration path is null.")); 327 } 328 File configFile = new File(configPath); 329 if (!configFile.exists()) { 330 configFile = null; 331 } 332 // De-duplicate test infos so that there won't be duplicate test options. 333 testInfos = dedupTestInfos(configFile, testInfos); 334 if (testInfos.size() > 1) { 335 moduleConfig.getConfigurationDescription().setNotIRemoteTestShardable(true); 336 } 337 338 for (TestInfo testInfo : testInfos) { 339 // Clean up all the test options injected in SuiteModuleLoader. 340 super.cleanUpSuiteSetup(); 341 super.clearModuleArgs(); 342 // Inject back the original exclude-filter test options. 343 super.setExcludeFilter(excludeFilterSet); 344 if (configFile != null) { 345 clearConfigPaths(); 346 // Set config path to BaseTestSuite to limit the search. 347 addConfigPaths(configFile); 348 } 349 // Inject the test options from each test info to SuiteModuleLoader. 350 parseOptions(testInfo); 351 LinkedHashMap<String, IConfiguration> config = super.loadTests(); 352 for (Map.Entry<String, IConfiguration> entry : config.entrySet()) { 353 if (entry.getValue().getConfigurationDescription().getAbi() != null 354 && !entry.getValue().getConfigurationDescription().getAbi().equals(abi)) { 355 continue; 356 } 357 List<IRemoteTest> remoteTests = entry.getValue().getTests(); 358 addTestSourcesToConfig(moduleConfig, remoteTests, testInfo.getSources()); 359 if (mReportImportPaths && !mIgnoreTestMappingImports) { 360 addTestSourcesToConfig(moduleConfig, remoteTests, testInfo.getImportPaths()); 361 } 362 tests.addAll(remoteTests); 363 } 364 } 365 return tests; 366 } 367 368 /** 369 * Add test mapping's path into module configuration. 370 * 371 * @param config The {@link IConfiguration} of the module config. 372 * @param tests The {@link List<IRemoteTest>} of the tests. 373 * @param sources The {@link Set<String>} of test mapping sources. 374 */ addTestSourcesToConfig( IConfiguration config, List<IRemoteTest> tests, Set<String> sources)375 private void addTestSourcesToConfig( 376 IConfiguration config, List<IRemoteTest> tests, Set<String> sources) { 377 for (IRemoteTest test : tests) { 378 config.getConfigurationDescription().addMetadata( 379 Integer.toString(test.hashCode()), new ArrayList<>(sources) 380 ); 381 } 382 } 383 384 /** 385 * Get a list of path of TEST_MAPPING for a module. 386 * 387 * @param testInfos A {@code Set<TestInfo>} containing multiple test options. 388 * @return A {@code List<String>} of TEST_MAPPING path. 389 */ 390 @VisibleForTesting getTestSources(Set<TestInfo> testInfos)391 List<String> getTestSources(Set<TestInfo> testInfos) { 392 List<String> testSources = new ArrayList<>(); 393 for (TestInfo testInfo : testInfos) { 394 testSources.addAll(testInfo.getSources()); 395 } 396 return testSources; 397 } 398 399 /** 400 * Parse the test options for the test info. 401 * 402 * @param testInfo A {@code Set<TestInfo>} containing multiple test options. 403 */ 404 @VisibleForTesting parseOptions(TestInfo testInfo)405 void parseOptions(TestInfo testInfo) { 406 Set<String> mappingIncludeFilters = new HashSet<>(); 407 Set<String> mappingExcludeFilters = new HashSet<>(); 408 // module-arg options compiled from test options for each test. 409 Set<String> moduleArgs = new HashSet<>(); 410 Set<String> testNames = new HashSet<>(); 411 for (TestOption option : testInfo.getOptions()) { 412 switch (option.getName()) { 413 // Handle include and exclude filter at the suite level to hide each 414 // test runner specific implementation and option names related to filtering 415 case TEST_MAPPING_INCLUDE_FILTER: 416 mappingIncludeFilters.add( 417 String.format("%s %s", testInfo.getName(), option.getValue())); 418 break; 419 case TEST_MAPPING_EXCLUDE_FILTER: 420 mappingExcludeFilters.add( 421 String.format("%s %s", testInfo.getName(), option.getValue())); 422 break; 423 default: 424 String moduleArg = 425 String.format("%s:%s", testInfo.getName(), option.getName()); 426 if (option.getValue() != null && !option.getValue().isEmpty()) { 427 moduleArg = String.format("%s:%s", moduleArg, option.getValue()); 428 } 429 moduleArgs.add(moduleArg); 430 break; 431 } 432 } 433 434 if (mappingIncludeFilters.isEmpty()) { 435 testNames.add(testInfo.getName()); 436 setIncludeFilter(testNames); 437 } else { 438 setIncludeFilter(mappingIncludeFilters); 439 } 440 if (!mappingExcludeFilters.isEmpty()) { 441 setExcludeFilter(mappingExcludeFilters); 442 } 443 addModuleArgs(moduleArgs); 444 } 445 446 /** 447 * De-duplicate test infos and aggregate test-mapping sources with the same test options. 448 * 449 * @param config the config file being deduplicated 450 * @param testInfos A {@code Set<TestInfo>} containing multiple test options. 451 * @return A {@code Set<TestInfo>} of tests without duplicated test options. 452 */ 453 @VisibleForTesting dedupTestInfos(File config, Set<TestInfo> testInfos)454 Set<TestInfo> dedupTestInfos(File config, Set<TestInfo> testInfos) { 455 Set<String> nameOptions = new HashSet<>(); 456 Set<TestInfo> dedupTestInfos = new TreeSet<TestInfo>(new TestInfoComparator()); 457 Set<String> duplicateSources = new LinkedHashSet<String>(); 458 for (TestInfo testInfo : testInfos) { 459 String nameOption = testInfo.getNameOption(); 460 if (!nameOptions.contains(nameOption)) { 461 dedupTestInfos.add(testInfo); 462 duplicateSources.addAll(testInfo.getSources()); 463 nameOptions.add(nameOption); 464 } else { 465 aggregateTestInfo(testInfo, dedupTestInfos); 466 } 467 } 468 469 // If size above 1 that means we have duplicated modules with different options 470 if (dedupTestInfos.size() > 1) { 471 InvocationMetricLogger.addInvocationMetrics( 472 InvocationMetricKey.DUPLICATE_MAPPING_DIFFERENT_OPTIONS, 473 String.format("%s:" + Joiner.on("+").join(duplicateSources), config)); 474 } 475 476 return dedupTestInfos; 477 } 478 479 private class TestInfoComparator implements Comparator<TestInfo> { 480 481 @Override compare(TestInfo a, TestInfo b)482 public int compare(TestInfo a, TestInfo b) { 483 if (a.getNameOption().equals(b.getNameOption())) { 484 return 0; 485 } 486 // If a is subset of b 487 if (createComparableNames(a).equals(b.getNameOption())) { 488 return -1; 489 } 490 // If b is subset of a 491 if (a.getNameOption().equals(createComparableNames(b))) { 492 return 1; 493 } 494 return 1; 495 } 496 } 497 createComparableNames(TestInfo a)498 private static String createComparableNames(TestInfo a) { 499 List<TestOption> copyOptions = new ArrayList<>(a.getOptions()); 500 copyOptions.removeIf(o -> (o.isExclusive() || (!o.isExclusive() && !o.isInclusive()))); 501 return String.format("%s%s", a.getName(), copyOptions.toString()); 502 } 503 504 /** 505 * Aggregate test-mapping sources of the test info with the same test options 506 * 507 * @param testInfo A {@code TestInfo} of duplicated test to be aggregated. 508 * @param dedupTestInfos A {@code Set<TestInfo>} of tests without duplicated test options. 509 */ aggregateTestInfo(TestInfo testInfo, Set<TestInfo> dedupTestInfos)510 private void aggregateTestInfo(TestInfo testInfo, Set<TestInfo> dedupTestInfos) { 511 for (TestInfo dedupTestInfo : dedupTestInfos) { 512 if (testInfo.getNameOption().equals(dedupTestInfo.getNameOption())) { 513 dedupTestInfo.addSources(testInfo.getSources()); 514 } 515 } 516 } 517 518 /** 519 * Get the test infos for the given module name. 520 * 521 * @param testInfos A {@code Set<TestInfo>} containing multiple test options. 522 * @param moduleName A {@code String} name of a test module. 523 * @return A {@code Set<TestInfo>} of tests for a module. 524 */ 525 @VisibleForTesting getTestInfos(Set<TestInfo> testInfos, String moduleName)526 Set<TestInfo> getTestInfos(Set<TestInfo> testInfos, String moduleName) { 527 return testInfos 528 .stream() 529 .filter(testInfo -> moduleName.equals(testInfo.getName())) 530 .collect(Collectors.toSet()); 531 } 532 533 /** 534 * Filter test infos by the given allowed test lists. 535 * 536 * @param testInfos A {@code Set<TestInfo>} containing multiple test options. 537 * @return A {@code Set<TestInfo>} of tests matching the allowed test lists. 538 */ 539 @VisibleForTesting filterByAllowedTestLists(IBuildInfo info, Set<TestInfo> testInfos)540 Set<TestInfo> filterByAllowedTestLists(IBuildInfo info, Set<TestInfo> testInfos) { 541 // Read the list of allowed tests, and compile a set of allowed test module names. 542 Set<String> allowedTests = new HashSet<String>(); 543 for (String testList : mAllowedTestLists) { 544 File testListZip = null; 545 if (info == null) { 546 String envBackfill = System.getenv(testList); 547 if (envBackfill != null) { 548 testListZip = new File(envBackfill); 549 } 550 } else { 551 testListZip = info.getFile(testList); 552 } 553 if (testListZip == null) { 554 throw new RuntimeException("Failed to locate allowed test list " + testList); 555 } 556 File testListFile = null; 557 boolean deleteFile = true; 558 try { 559 if (testListZip.isDirectory()) { 560 // A single file should be under the zip 561 testListFile = testListZip.listFiles()[0]; 562 } else { 563 try (ZipFile zipFile = new ZipFile(testListZip)) { 564 testListFile = 565 ZipUtil2.extractFileFromZip( 566 zipFile, Files.getNameWithoutExtension(testList)); 567 deleteFile = true; 568 } 569 } 570 String content = FileUtil.readStringFromFile(testListFile); 571 final String pattern = "([^//]*).config$"; 572 Pattern namePattern = Pattern.compile(pattern); 573 for (String line : content.split("\n")) { 574 Matcher matcher = namePattern.matcher(line); 575 if (matcher.find()) { 576 allowedTests.add(matcher.group(1)); 577 } 578 } 579 } catch (IOException e) { 580 throw new RuntimeException( 581 String.format( 582 "IO exception (%s) when accessing allowed test list (%s)", 583 e.getMessage(), testList), 584 e); 585 } finally { 586 if (deleteFile) { 587 FileUtil.recursiveDelete(testListFile); 588 } 589 } 590 } 591 592 return testInfos.stream() 593 .filter(testInfo -> allowedTests.contains(testInfo.getName())) 594 .collect(Collectors.toSet()); 595 } 596 597 @VisibleForTesting checkTestMappingOptions()598 void checkTestMappingOptions() { 599 if (mSkipOptionCheckInChildProc) { 600 CLog.i("Skip checking options because they are already validated."); 601 return; 602 } 603 Set<String> includeFilter = getIncludeFilter(); 604 if (mTestGroup == null && includeFilter.isEmpty()) { 605 throw new HarnessRuntimeException( 606 "At least one of the options, --test-mapping-test-group or" 607 + " --include-filter, should be set.", 608 InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 609 } 610 if (mTestGroup == null && !mKeywords.isEmpty()) { 611 throw new HarnessRuntimeException( 612 "Must specify --test-mapping-test-group when applying" 613 + " --test-mapping-keyword.", 614 InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 615 } 616 if (!mIgnoreKeywords.isEmpty() && !mKeywords.isEmpty()) { 617 for (String keyword : mKeywords) { 618 if (mIgnoreKeywords.contains(keyword)) { 619 throw new HarnessRuntimeException( 620 "Keyword cannot be in both required and ignored.", 621 InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 622 } 623 } 624 } 625 if (mTestGroup == null && !mTestModulesForced.isEmpty()) { 626 throw new HarnessRuntimeException( 627 "Must specify --test-mapping-test-group when applying " 628 + "--force-test-mapping-module.", 629 InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 630 } 631 if (mTestGroup != null && !includeFilter.isEmpty()) { 632 throw new HarnessRuntimeException( 633 "If options --test-mapping-test-group is set, option --include-filter" 634 + " should not be set.", 635 InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 636 } 637 if (!includeFilter.isEmpty() && !mTestMappingPaths.isEmpty()) { 638 throw new HarnessRuntimeException( 639 "If option --include-filter is set, option --test-mapping-path should " 640 + "not be set.", 641 InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 642 } 643 if (mReportImportPaths && !mIgnoreTestMappingImports) { 644 CLog.i( 645 "Option \"no-ignore-test-mapping-imports\" and \"report-import-paths\" are" 646 + " enabled, TestMapping will report tests with sources and import" 647 + " paths to AnTS"); 648 } 649 CLog.i("Options check is done, set skip-option-check-in-child-proc to true."); 650 mSkipOptionCheckInChildProc = true; 651 } 652 } 653