• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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