blob: 04abe496fba2fa184e229e631740d376d0deb4f0 [file] [log] [blame]
/*
* Copyright (C) 2019 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.presubmit;
import static org.junit.Assert.fail;
import static java.lang.String.format;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.build.IDeviceBuildInfo;
import com.android.tradefed.config.ConfigurationException;
import com.android.tradefed.config.ConfigurationFactory;
import com.android.tradefed.config.ConfigurationUtil;
import com.android.tradefed.config.IConfiguration;
import com.android.tradefed.config.IConfigurationFactory;
import com.android.tradefed.config.IDeviceConfiguration;
import com.android.tradefed.config.Option;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.targetprep.ITargetPreparer;
import com.android.tradefed.targetprep.PushFilePreparer;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.suite.ITestSuite;
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.ModuleTestTypeUtil;
import com.android.tradefed.util.ZipUtil2;
import com.android.tradefed.util.testmapping.TestInfo;
import com.android.tradefed.util.testmapping.TestMapping;
import com.android.tradefed.util.testmapping.TestOption;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Validation tests to run against the TEST_MAPPING files in tests_mappings.zip to ensure they
* contains the essential suite settings and no conflict test options.
*
* <p>Do not add to UnitTests.java. This is meant to run standalone.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public class TestMappingsValidation implements IBuildReceiver {
@Option(name = "test-group-to-validate", description = "The test groups to be validated.")
private Set<String> mTestGroupToValidate =
new HashSet<>(Arrays.asList("presubmit", "postsubmit", "presubmit-large"));
@Option(name = "skip-modules", description = "Test modules that could be skipped.")
private Set<String> mSkipModules = new HashSet<>();
@Option(name = "enforce-module-name-check", description = "Enforce failing test if it is set.")
private boolean mEnforceModuleNameCheck = false;
// pattern used to identify java class names conforming to java naming conventions.
private static final Pattern CLASS_OR_METHOD_REGEX =
Pattern.compile(
"^([\\p{L}_$][\\p{L}\\p{N}_$]*\\.)*[\\p{Lu}_$][\\p{L}\\p{N}_$]*"
+ "(#[\\p{L}_$][\\p{L}\\p{N}_$]*)?$");
// pattern used to identify if this is regular expression with at least 1 '*' or '?'.
private static final Pattern REGULAR_EXPRESSION = Pattern.compile("(\\?+)|(\\*+)");
private static final String MODULE_INFO = "module-info.json";
private static final String TEST_MAPPINGS_ZIP = "test_mappings.zip";
private static final String INCLUDE_FILTER = "include-filter";
private static final String EXCLUDE_FILTER = "exclude-filter";
private static final String LOCAL_COMPATIBILITY_SUITES = "compatibility_suites";
private static final String GENERAL_TESTS = "general-tests";
private static final String DEVICE_TESTS = "device-tests";
private static final String TEST_MAPPING_BUILD_SCRIPT_LINK =
"https://source.android.com/compatibility/tests/development/test-mapping#packaging_build_script_rules";
private static final String MODULE_NAME = "module_name";
private static final String MAINLINE = "mainline";
// Pair that shouldn't be overlapping. Test can only exists in one or the other.
private static final Set<String> INCOMPATIBLE_PAIRS =
ImmutableSet.of("presubmit", "presubmit-large");
// Check the mainline parameter configured in a test config must end with .apk, .apks, or .apex.
private static final Set<String> MAINLINE_PARAMETERS_TO_VALIDATE =
new HashSet<>(Arrays.asList(".apk", ".apks", ".apex"));
private File testMappingsDir = null;
private IConfigurationFactory mConfigFactory = null;
private IDeviceBuildInfo deviceBuildInfo = null;
private IBuildInfo mBuild;
private JsonObject mergedModuleInfo = new JsonObject();
private Map<String, Set<TestInfo>> allTests = null;
/** Type of filters used in test options in TEST_MAPPING files. */
enum Filters {
// Test option is regular expression format.
REGEX,
// Test option is class/method format.
CLASS_OR_METHOD,
// Test option is package format.
PACKAGE
}
// TODO: Evaluate those modules if they can be moved out.
private static final Set<String> EXEMPTED_DUPLICATION_PRESUBMIT_LARGE =
ImmutableSet.of(
"CtsLibcoreOjTestCases",
"FrameworksCoreTests",
"CtsAppTestCases",
"CtsAppSecurityHostTestCases",
"FrameworksServicesTests",
"CtsLibcoreTestCases",
"CtsDevicePolicyManagerTestCases",
"CtsAutoFillServiceTestCases",
"CtsOsTestCases",
"CtsStatsdAtomHostTestCases",
"CtsPermission3TestCases",
"CtsPermissionUiTestCases",// Renamed from Permission3 in 2023
"CtsMediaAudioTestCases");
private static final Set<String> PRESUBMIT_LARGE_ALLOWLIST = ImmutableSet.of(
"binderRpcTestNoKernel",
"CtsTelecomTestCases",
"CtsAppTestCases",
"binderRpcTestSingleThreadedNoKernel",
"CtsSuspendAppsPermissionTestCases",
"CtsAppSecurityHostTestCases",
"CtsPackageManagerTestCases", // Renamed from CtsAppSecurityHostTestCases
"FrameworksServicesTests",
"NeuralNetworksTest_static",
"CtsNNAPITestCases",
"CtsLibcoreTestCases",
"OverlayRemountedTest",
"CtsPermission3TestCases",
"CtsPermissionUiTestCases",
"sharedlibs_host_tests",
"CtsDevicePolicyManagerTestCases",
"CtsMediaAudioTestCases",
"CtsScopedStoragePublicVolumeHostTest",
"CtsContentTestCases",
"CtsHostsideNetworkTests",
"vm-tests-tf",
"CtsStatsdAtomHostTestCases",
"CtsMediaPlayerTestCases",
"CtsMediaDecoderTestCases",
"CtsQuickAccessWalletTestCases",
"CtsMediaMiscTestCases",
"NetworkStagedRollbackTest",
"CtsUsesNativeLibraryTest",
"RollbackTest",
"CtsLibcoreOjTestCases",
"CtsMultiUserHostTestCases",
"binderRpcTestSingleThreaded",
"sdkextensions_e2e_tests",
"StagedRollbackTest",
"CtsMediaEncoderTestCases",
"CtsRootRollbackManagerHostTestCases",
"StagedInstallInternalTest",
"FrameworksWifiTests",
"MultiUserRollbackTest",
"binderRpcTest",
"CtsMediaCodecTestCases",
"CtsRollbackManagerHostTestCases",
"CtsAutoFillServiceTestCases",
"CtsOsTestCases",
"CtsDynamicMimeHostTestCases",
"VtsHalNeuralnetworksTargetTest",
"CtsWifiTestCases",
"SystemUITests",
"CellBroadcastReceiverComplianceTests",
"InputMethodStressTest",
"SystemUIClocksTests",
"GtsStagedInstallHostTestCases");
private static final Set<String> REMOUNT_MODULES =
ImmutableSet.of(
"libnativeloader_e2e_tests",
"BugreportManagerTestCases",
"CtsRootBugreportTestCases",
"ApexServiceTestCases",
"OverlayDeviceTests");
@Override
public void setBuild(IBuildInfo buildInfo) {
mBuild = buildInfo;
}
@Before
public void setUp() throws IOException {
Assume.assumeTrue(mBuild instanceof IDeviceBuildInfo);
mConfigFactory = ConfigurationFactory.getInstance();
deviceBuildInfo = (IDeviceBuildInfo) mBuild;
testMappingsDir =
TestMapping.extractTestMappingsZip(deviceBuildInfo.getFile(TEST_MAPPINGS_ZIP));
mergeModuleInfo(deviceBuildInfo.getFile(MODULE_INFO));
for (String fileKey : deviceBuildInfo.getVersionedFileKeys()) {
if (fileKey.contains("additional-module-info")) {
CLog.i("Merging additional %s.json", fileKey);
mergeModuleInfo(deviceBuildInfo.getFile(fileKey));
}
}
TestMapping testMapping = new TestMapping();
allTests = testMapping.getAllTests(testMappingsDir);
}
@After
public void tearDown() {
FileUtil.recursiveDelete(testMappingsDir);
}
/**
* Test all the test config files and make sure they are properly configured with parameterized
* mainline modules.
*/
@Test
public void testValidTestConfigForParameterizedMainlineModules() throws IOException {
File configZip = deviceBuildInfo.getFile("general-tests_configs.zip");
Assume.assumeTrue(configZip != null);
List<String> errors = new ArrayList<>();
List<String> testConfigs = new ArrayList<>();
File testConfigDir = ZipUtil2.extractZipToTemp(configZip, "general-tests_configs");
testConfigs.addAll(
ConfigurationUtil.getConfigNamesFromDirs(null, Arrays.asList(testConfigDir)));
for (String configName : testConfigs) {
try {
IConfiguration config =
mConfigFactory.createConfigurationFromArgs(new String[] {configName});
List<String> params = config.getConfigurationDescription().getMetaData(
ITestSuite.MAINLINE_PARAMETER_KEY);
if (params == null || params.isEmpty()) {
continue;
}
for (String param : params) {
String error = validateMainlineModuleConfig(param, Set.of(configName));
if (!Strings.isNullOrEmpty(error)){
errors.add(error);
}
}
} catch (ConfigurationException e) {
errors.add(String.format("\t%s: %s", configName, e.getMessage()));
}
}
if (!errors.isEmpty()) {
fail(String.format("Fail Test config check for parameterized mainline module: \n%s",
Joiner.on("\n").join(errors)));
}
}
/**
* This test can only be enabled for BVT only cause it needs many other build targets involved.
* Test all TEST_MAPPING files and make sure each test entry is properly configured in build
* targets.
*/
@Test
public void testValidateTestEntry() throws Exception {
List<String> errors = new ArrayList<>();
Set<String> checkedModule = new HashSet<>();
Map<String, Set<String>> incompatiblePairsTracking = new HashMap<>();
for (String s : INCOMPATIBLE_PAIRS) {
incompatiblePairsTracking.put(s, new HashSet<>());
}
for (String testGroup : allTests.keySet()) {
if (!mTestGroupToValidate.contains(testGroup)) {
CLog.d("Skip checking tests with group: %s", testGroup);
continue;
}
for (TestInfo testInfo : allTests.get(testGroup)) {
String moduleName = testInfo.getName();
if (mSkipModules.contains(moduleName)) {
CLog.w("Test Module: %s is in the skip list. Ignore checking...", moduleName);
continue;
}
if (testGroup.contains(MAINLINE)) {
errors.addAll(validateMainlineTest(testInfo));
} else {
if (!mergedModuleInfo.has(moduleName)) {
errors.add(
format(
"Test Module: %s doesn't exist in any build targets,"
+ " TEST_MAPPING file path: %s",
testInfo.getName(), testInfo.getSources()));
}
}
checkedModule.add(moduleName);
if (incompatiblePairsTracking.containsKey(testGroup)) {
incompatiblePairsTracking.get(testGroup).add(moduleName);
}
}
}
if (!errors.isEmpty()) {
String error =
format(
"Fail test entry check. Some test modules are not found or the module"
+ " name has been changed or been removed from build file. \n\n"
+ "To locate owner that is responsible for the breakage, try to do"
+ " code search on the test modules, check the changelog/blame of"
+ " the broken TEST_MAPPING file or Android.bp/mk to locate the"
+ " owner.\n\n"
+ "Details: \n"
+ "%s",
Joiner.on("\n").join(errors));
if (!mEnforceModuleNameCheck) {
CLog.w(error);
} else {
fail(error);
}
}
// Validate incompatible pairs
validateIncompatiblePairs(incompatiblePairsTracking);
// Validate basic configuration in shared pools
validateConfigsOfSharedPool(checkedModule);
}
/**
* Test all the TEST_MAPPING files and make sure they contain the suite setting in
* module-info.json.
*/
@Test
public void testTestSuiteSetting() {
List<String> errors = new ArrayList<>();
for (String testGroup : allTests.keySet()) {
if (!mTestGroupToValidate.contains(testGroup)) {
CLog.d("Skip checking tests with group: %s", testGroup);
continue;
}
for (TestInfo testInfo : allTests.get(testGroup)) {
String moduleName = testInfo.getName();
if (mSkipModules.contains(moduleName)) {
CLog.w("Test Module: %s is in the skip list. Ignore checking...", moduleName);
continue;
}
if (testGroup.contains(MAINLINE)) {
Matcher matcher = TestMapping.MAINLINE_REGEX.matcher(moduleName);
if (matcher.find()) {
moduleName = matcher.group(1);
}
}
if (!validateSuiteSetting(moduleName)) {
errors.add(
String.format(
"Missing test_suite setting for test: %s, test group: %s, "
+ "TEST_MAPPING file path: %s",
testInfo.getName(), testGroup, testInfo.getSources()));
}
}
}
if (!errors.isEmpty()) {
fail(
String.format(
"Fail test_suite setting check:\n%s\nPlease refer to following link " +
"for more details about test suite configuration.\n %s",
Joiner.on("\n").join(errors), TEST_MAPPING_BUILD_SCRIPT_LINK
)
);
}
}
/**
* Test all the tests by each test group and make sure the file options aren't conflict to AJUR
* rules.
*/
@Test
public void testFilterOptions() {
List<String> errors = new ArrayList<>();
for (String testGroup : allTests.keySet()) {
for (String moduleName : getModuleNames(testGroup)) {
errors.addAll(validateFilterOption(moduleName, INCLUDE_FILTER, testGroup));
errors.addAll(validateFilterOption(moduleName, EXCLUDE_FILTER, testGroup));
}
}
if (!errors.isEmpty()) {
fail(
String.format(
"Fail include/exclude filter setting check:\n%s",
Joiner.on("\n").join(errors)));
}
}
/** Test to ensure performance test modules are not included for test mapping. */
@Test
public void testNoPerformanceTests() throws IOException {
Set<String> modules = new HashSet<>();
for (String testGroup : allTests.keySet()) {
if (!mTestGroupToValidate.contains(testGroup)) {
CLog.d("Skip checking tests with group: %s", testGroup);
continue;
}
for (TestInfo testInfo : allTests.get(testGroup)) {
modules.add(testInfo.getName());
}
}
File testConfigDir = null;
File deviceTestConfigDir = null;
try {
File configZip = deviceBuildInfo.getFile("general-tests_configs.zip");
File deviceConfigZip = deviceBuildInfo.getFile("device-tests_configs.zip");
Assume.assumeTrue(configZip != null);
List<String> testConfigs = new ArrayList<>();
List<File> dirToLoad = new ArrayList<>();
testConfigDir = ZipUtil2.extractZipToTemp(configZip, "general-tests_configs");
dirToLoad.add(testConfigDir);
if (deviceConfigZip != null) {
deviceTestConfigDir =
ZipUtil2.extractZipToTemp(deviceConfigZip, "device-tests_configs");
dirToLoad.add(deviceTestConfigDir);
}
testConfigs.addAll(ConfigurationUtil.getConfigNamesFromDirs(null, dirToLoad));
CLog.d("Checking modules: %s. And configs: %s", modules, testConfigs);
List<String> performanceModules = new ArrayList<>();
for (String configName : testConfigs) {
String module = FileUtil.getBaseName(new File(configName).getName());
if (!modules.contains(module)) {
continue;
}
IConfiguration config =
mConfigFactory.createConfigurationFromArgs(new String[] {configName});
if (ModuleTestTypeUtil.isPerformanceModule(config)) {
performanceModules.add(module);
}
}
if (!performanceModules.isEmpty()) {
fail(
String.format(
"Performance modules not allowed in test mapping:\n%s",
Joiner.on("\n").join(performanceModules)));
}
} catch (ConfigurationException e) {
fail(e.toString());
} finally {
FileUtil.recursiveDelete(testConfigDir);
FileUtil.recursiveDelete(deviceTestConfigDir);
}
}
/**
* Validate if the filter option of a test contains both class/method and package. options.
*
* @param moduleName A {@code String} name of a test module.
* @param filterOption A {@code String} of the filter option defined in TEST MAPPING file.
* @param testGroup A {@code String} name of the test group.
* @return A {@code List<String>} of the validation errors.
*/
private List<String> validateFilterOption(
String moduleName, String filterOption, String testGroup) {
List<String> errors = new ArrayList<>();
for (TestInfo test : getTestInfos(moduleName, testGroup)) {
Set<Filters> filterTypes = new HashSet<>();
Map<Filters, Set<TestInfo>> filterTestInfos = new HashMap<>();
for (TestOption options : test.getOptions()) {
if (options.getName().equals(filterOption)) {
Filters optionType = getOptionType(options.getValue());
// Add optionType with each TestInfo to get the detailed information.
filterTestInfos.computeIfAbsent(optionType, k -> new HashSet<>()).add(test);
}
}
filterTypes = filterTestInfos.keySet();
// If the options of a test in one TEST_MAPPING file contain either REGEX,
// CLASS_OR_METHOD, or PACKAGE, it should be caught and output the tests
// information.
// TODO(b/128947872): List the type with fewest options first.
if (filterTypes.size() > 1) {
errors.add(
String.format(
"Mixed filter types found. Test: %s , TestGroup: %s, Details:\n"
+ "%s",
moduleName,
testGroup,
getDetailedErrors(filterOption, filterTestInfos)));
}
}
return errors;
}
/**
* Get the detailed validation errors.
*
* @param filterOption A {@code String} of the filter option defined in TEST MAPPING file.
* @param filterTestInfos A {@code Map<Filters, Set<TestInfo>>} of tests with the given filter
* type and its child test information.
* @return A {@code String} of the detailed errors.
*/
private String getDetailedErrors(
String filterOption, Map<Filters, Set<TestInfo>> filterTestInfos) {
StringBuilder errors = new StringBuilder("");
Set<Map.Entry<Filters, Set<TestInfo>>> entries = filterTestInfos.entrySet();
for (Map.Entry<Filters, Set<TestInfo>> entry : entries) {
Set<TestInfo> testInfos = entry.getValue();
StringBuilder detailedErrors = new StringBuilder("");
for (TestInfo test : testInfos) {
for (TestOption options : test.getOptions()) {
if (options.getName().equals(filterOption)) {
detailedErrors.append(
String.format(
" %s (%s)\n", options.getValue(), test.getSources()));
}
}
}
errors.append(
String.format(
"Options using %s filter:\n%s",
entry.getKey().toString(), detailedErrors));
}
return errors.toString();
}
/**
* Determine whether optionValue represents regrex, test class or method, or package.
*
* @param optionValue A {@code String} containing either an individual test regrex, class/method
* or a package.
* @return A {@code Filters} representing regrex, test class or method, or package.
*/
private Filters getOptionType(String optionValue) {
if (REGULAR_EXPRESSION.matcher(optionValue).find()) {
return Filters.REGEX;
} else if (CLASS_OR_METHOD_REGEX.matcher(optionValue).find()) {
return Filters.CLASS_OR_METHOD;
}
return Filters.PACKAGE;
}
/**
* Validate if the mainline module parameter is properly configured in the config.
*
* @param param A {@code String} name of the mainline module parameter.
* @param paths A {@code Set<String>} path of the test config or TEST_MAPPING file.
* @return A {@code String} of the errors.
*/
private String validateMainlineModuleConfig(String param, Set<String> paths) {
StringBuilder errors = new StringBuilder("");
if (!isInAlphabeticalOrder(param)) {
errors.append(
String.format(
"Illegal mainline module parameter: \"%s\" configured in the %s. " +
"Parameter must be configured in alphabetical order and with no " +
"duplicated modules.", param, paths));
}
else if (!isValidMainlineParam(param)) {
errors.append(
String.format(
"Illegal mainline module parameter: \"%s\" configured in the %s. " +
"Parameter must end with .apk/.apex/.apks and have no any spaces " +
"configured.", param, paths));
}
return errors.toString();
}
/** Whether a mainline parameter configured in a test config is in alphabetical order or not. */
boolean isInAlphabeticalOrder(String param) {
String previousString = "";
for (String currentString : param.split(String.format("\\+"))) {
// This is to check if the parameter is in alphabetical order or duplicated.
if (currentString.compareTo(previousString) <= 0) {
return false;
}
previousString = currentString;
}
return true;
}
/** Whether the mainline parameter configured in the test config is valid or not. */
boolean isValidMainlineParam(String param) {
if (param.contains(" ")) {
return false;
}
for (String m : param.split(String.format("\\+"))) {
if (!MAINLINE_PARAMETERS_TO_VALIDATE.stream().anyMatch(entry -> m.endsWith(entry))) {
return false;
}
}
return true;
}
/**
* Validate if the name exists in module-info.json and with the correct suite setting.
*
* @param name A {@code String} name of the test.
* @return true if name exists in module-info.json and matches either "general-tests" or
* "device-tests", or name doesn't exist in module-info.json.
*/
private boolean validateSuiteSetting(String name) {
if (!mergedModuleInfo.has(name)) {
CLog.w("Test Module: %s can't be found in module-info.json. Ignore checking...", name);
return true;
}
JsonArray compatibilitySuites =
mergedModuleInfo
.getAsJsonObject(name)
.get(LOCAL_COMPATIBILITY_SUITES)
.getAsJsonArray();
for (int i = 0; i < compatibilitySuites.size(); i++) {
String suite = compatibilitySuites.get(i).getAsString();
if (suite.equals(GENERAL_TESTS) || suite.equals(DEVICE_TESTS)) {
return true;
}
}
return false;
}
/**
* Get the module names for the given test group.
*
* @param testGroup A {@code String} name of the test group.
* @return A {@code Set<String>} containing the module names for the given test group.
*/
private Set<String> getModuleNames(String testGroup) {
Set<String> moduleNames = new HashSet<>();
for (TestInfo test : allTests.get(testGroup)) {
moduleNames.add(test.getName());
}
return moduleNames;
}
/**
* Get the test infos for the given module name and test group.
*
* @param moduleName A {@code String} name of a test module.
* @param testGroup A {@code String} name of the test group.
* @return A {@code Set<TestInfo>} of tests that each is for a unique test module.
*/
private Set<TestInfo> getTestInfos(String moduleName, String testGroup) {
Set<TestInfo> testInfos = new HashSet<>();
for (TestInfo test : allTests.get(testGroup)) {
if (test.getName().equals(moduleName)) {
testInfos.add(test);
}
}
return testInfos;
}
private void mergeModuleInfo(File file) throws IOException {
JsonObject json = new Gson().fromJson(FileUtil.readStringFromFile(file), JsonObject.class);
json.entrySet()
.forEach(
moduleInfo ->
mergeModuleInfoByName(moduleInfo.getValue().getAsJsonObject()));
}
private void mergeModuleInfoByName(JsonObject jsonObject) {
mergedModuleInfo.add(jsonObject.get(MODULE_NAME).getAsString(), jsonObject);
}
/** Validate mainline test with parameterized mainline modules is properly configured. */
private List<String> validateMainlineTest(TestInfo testInfo) {
List<String> errors = new ArrayList<>();
try {
Matcher matcher = TestMapping.getMainlineTestModuleName(testInfo);
if (!mergedModuleInfo.has(matcher.group(1))) {
errors.add(
format(
"Test Module: %s doesn't exist in any build targets,"
+ " TEST_MAPPING file path: %s",
testInfo.getName(), testInfo.getSources()));
}
String error = validateMainlineModuleConfig(matcher.group(2), testInfo.getSources());
if (!Strings.isNullOrEmpty(error)) {
errors.add(error);
}
} catch (ConfigurationException e) {
errors.add(e.getMessage());
}
return errors;
}
private void validateConfigsOfSharedPool(Set<String> checkedModule) throws Exception {
File configZip = deviceBuildInfo.getFile("general-tests_configs.zip");
File deviceConfigZip = deviceBuildInfo.getFile("device-tests_configs.zip");
Assume.assumeTrue(configZip != null);
List<String> testConfigs = new ArrayList<>();
List<File> dirToLoad = new ArrayList<>();
File testConfigDir = ZipUtil2.extractZipToTemp(configZip, "general-tests_configs");
File deviceTestConfigDir = null;
dirToLoad.add(testConfigDir);
if (deviceConfigZip != null) {
deviceTestConfigDir =
ZipUtil2.extractZipToTemp(deviceConfigZip, "device-tests_configs");
dirToLoad.add(deviceTestConfigDir);
}
try {
testConfigs.addAll(ConfigurationUtil.getConfigNamesFromDirs(null, dirToLoad));
CLog.d("Checking modules: %s. And configs: %s", checkedModule, testConfigs);
List<String> errors = new ArrayList<>();
for (String configName : testConfigs) {
String fileName = FileUtil.getBaseName(new File(configName).getName());
if (!checkedModule.contains(fileName)) {
continue;
}
try {
IConfiguration config =
mConfigFactory.createConfigurationFromArgs(new String[] {configName});
for (IDeviceConfiguration dConfig : config.getDeviceConfig()) {
for (ITargetPreparer prep : dConfig.getTargetPreparers()) {
if (prep instanceof PushFilePreparer) {
PushFilePreparer pushPreparer = (PushFilePreparer) prep;
if (pushPreparer.shouldRemountSystem()
|| pushPreparer.shouldRemountVendor()) {
if (REMOUNT_MODULES.contains(fileName)) {
continue;
}
throw new ConfigurationException(
String.format(
"%s: Shouldn't use 'remount-system' or"
+ " 'remount-vendor' in shared test mapping"
+ " pools.",
fileName));
}
}
}
}
} catch (Exception e) {
errors.add(e.toString());
}
}
if (!errors.isEmpty()) {
fail(Joiner.on("\n").join(errors));
}
} finally {
FileUtil.recursiveDelete(testConfigDir);
FileUtil.recursiveDelete(deviceTestConfigDir);
}
}
private void validateIncompatiblePairs(Map<String, Set<String>> incompatiblePairsTracking)
throws ConfigurationException {
Set<String> presubmitLarge = incompatiblePairsTracking.get("presubmit-large");
Set<String> presubmit = incompatiblePairsTracking.get("presubmit");
Set<String> dual =
presubmitLarge.stream()
.filter(e -> presubmit.contains(e))
.collect(Collectors.toSet());
dual.removeIf(e -> EXEMPTED_DUPLICATION_PRESUBMIT_LARGE.contains(e));
if (!dual.isEmpty()) {
throw new ConfigurationException(
String.format(
"the following entries exist in both presubmit and "
+ "presubmit-large groups: %s. Do not use "
+ "presubmit-large group to run larger tests. "
+ "Make sure your tests meet presubmit SLO "
+ "and use 'presubmit' group.",
dual));
}
presubmitLarge.removeAll(PRESUBMIT_LARGE_ALLOWLIST);
if (!presubmitLarge.isEmpty()) {
throw new ConfigurationException(
String.format(
"Adding new modules to presubmit-large "
+ "isn't allowed. Those tests: %s. Do not use "
+ "presubmit-large group to run larger tests. Make sure"
+ " your tests meet presubmit SLO and use 'presubmit'"
+ "group.",
presubmitLarge));
}
}
}