blob: c0dc16a3df66892f0a6c72cb5c98a993fec181a9 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.testtype.suite;
import com.android.tradefed.config.ConfigurationDescriptor;
import com.android.tradefed.config.IConfiguration;
import com.android.tradefed.config.Option;
import com.android.tradefed.util.testmapping.TestInfo;
import com.android.tradefed.util.testmapping.TestMapping;
import com.android.tradefed.util.testmapping.TestOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Implementation of {@link BaseTestSuite} to run tests specified by option include-filter, or
* TEST_MAPPING files from build, as a suite.
*/
public class TestMappingSuiteRunner extends BaseTestSuite {
@Option(
name = "test-mapping-test-group",
description =
"Group of tests to run, e.g., presubmit, postsubmit. The suite runner "
+ "shall load the tests defined in all TEST_MAPPING files in the source "
+ "code, through build artifact test_mappings.zip."
)
private String mTestGroup = null;
@Option(
name = "test-mapping-keyword",
description =
"Keyword to be matched to the `keywords` setting of a test configured in "
+ "a TEST_MAPPING file. The test will only run if it has all the keywords "
+ "specified in the option. If option test-mapping-test-group is not set, "
+ "test-mapping-keyword option is ignored as the tests to run are not "
+ "loaded directly from TEST_MAPPING files but is supplied via the "
+ "--include-filter arg."
)
private Set<String> mKeywords = new HashSet<>();
/** Special definition in the test mapping structure. */
private static final String TEST_MAPPING_INCLUDE_FILTER = "include-filter";
private static final String TEST_MAPPING_EXCLUDE_FILTER = "exclude-filter";
/**
* Load the tests configuration that will be run. Each tests is defined by a {@link
* IConfiguration} and a unique name under which it will report results. There are 2 ways to
* load tests for {@link TestMappingSuiteRunner}:
*
* <p>1. --test-mapping-test-group, which specifies the group of tests in TEST_MAPPING files.
* The runner will parse all TEST_MAPPING files in the source code through build artifact
* test_mappings.zip, and load tests grouped under the given test group.
*
* <p>2. --include-filter, which specifies the name of the test to run. The use case is for
* presubmit check to only run a list of tests related to the Cls to be verifies. The list of
* tests are compiled from the related TEST_MAPPING files in modified source code.
*
* @return a map of test name to the {@link IConfiguration} object of each test.
*/
@Override
public LinkedHashMap<String, IConfiguration> loadTests() {
// Map between test names and a list of test sources for each test.
Map<String, List<String>> testsInTestMapping = new HashMap<>();
Set<String> includeFilter = getIncludeFilter();
if (mTestGroup == null && includeFilter.isEmpty()) {
throw new RuntimeException(
"At least one of the options, --test-mapping-test-group or --include-filter, "
+ "should be set.");
}
if (mTestGroup == null && !mKeywords.isEmpty()) {
throw new RuntimeException(
"Must specify --test-mapping-test-group when applying --test-mapping-keyword.");
}
if (mTestGroup != null && !includeFilter.isEmpty()) {
throw new RuntimeException(
"If options --test-mapping-test-group is set, option --include-filter should "
+ "not be set.");
}
if (mTestGroup != null) {
Set<TestInfo> testsToRun =
TestMapping.getTests(
getBuildInfo(), mTestGroup, getPrioritizeHostConfig(), mKeywords);
if (testsToRun.isEmpty()) {
throw new RuntimeException(
String.format("No test found for the given group: %s.", mTestGroup));
}
// Name of the tests
Set<String> testNames = new HashSet<>();
Set<String> mappingIncludeFilters = new HashSet<>();
Set<String> mappingExcludeFilters = new HashSet<>();
// module-arg options compiled from test options for each test.
Set<String> moduleArgs = new HashSet<>();
for (TestInfo test : testsToRun) {
boolean hasIncludeFilters = false;
for (TestOption option : test.getOptions()) {
switch (option.getName()) {
// Handle include and exclude filter at the suite level to hide each
// test runner specific implementation and option names related to filtering
case TEST_MAPPING_INCLUDE_FILTER:
hasIncludeFilters = true;
mappingIncludeFilters.add(
String.format("%s %s", test.getName(), option.getValue()));
break;
case TEST_MAPPING_EXCLUDE_FILTER:
mappingExcludeFilters.add(
String.format("%s %s", test.getName(), option.getValue()));
break;
default:
String moduleArg =
String.format("%s:%s", test.getName(), option.getName());
if (option.getValue() != null && !option.getValue().isEmpty()) {
moduleArg = String.format("%s:%s", moduleArg, option.getValue());
}
moduleArgs.add(moduleArg);
break;
}
}
if (!hasIncludeFilters) {
testNames.add(test.getName());
}
}
if (mappingIncludeFilters.isEmpty()) {
setIncludeFilter(testNames);
} else {
mappingIncludeFilters.addAll(testNames);
setIncludeFilter(mappingIncludeFilters);
}
if (!mappingExcludeFilters.isEmpty()) {
setExcludeFilter(mappingExcludeFilters);
}
addModuleArgs(moduleArgs);
for (TestInfo test : testsToRun) {
List<String> testSources = null;
// TODO(b/117880789): tests may not be grouped by name once that bug is fixed.
// Update the dictionary with better keys.
if (testsInTestMapping.containsKey(test.getName())) {
testSources = testsInTestMapping.get(test.toString());
} else {
testSources = new ArrayList<String>();
testsInTestMapping.put(test.getName(), testSources);
}
testSources.addAll(test.getSources());
}
}
LinkedHashMap<String, IConfiguration> testConfigs = super.loadTests();
for (Map.Entry<String, IConfiguration> entry : testConfigs.entrySet()) {
ConfigurationDescriptor configDescriptor =
entry.getValue().getConfigurationDescription();
if (testsInTestMapping.containsKey(configDescriptor.getModuleName())) {
configDescriptor.addMetaData(
TestMapping.TEST_SOURCES,
testsInTestMapping.get(configDescriptor.getModuleName()));
}
}
return testConfigs;
}
}