1 /* 2 * Copyright (C) 2017 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.config.ConfigurationFactory; 19 import com.android.tradefed.config.IConfiguration; 20 import com.android.tradefed.config.IConfigurationFactory; 21 import com.android.tradefed.config.Option; 22 import com.android.tradefed.config.Option.Importance; 23 import com.android.tradefed.config.OptionCopier; 24 import com.android.tradefed.log.LogUtil.CLog; 25 import com.android.tradefed.result.ITestInvocationListener; 26 import com.android.tradefed.result.SubprocessResultsReporter; 27 import com.android.tradefed.targetprep.ITargetPreparer; 28 import com.android.tradefed.testtype.IAbi; 29 import com.android.tradefed.testtype.IRemoteTest; 30 import com.android.tradefed.testtype.ITestFilterReceiver; 31 import com.android.tradefed.testtype.InstrumentationTest; 32 import com.android.tradefed.testtype.IsolatedHostTest; 33 34 import java.io.File; 35 import java.util.ArrayList; 36 import java.util.HashSet; 37 import java.util.LinkedHashMap; 38 import java.util.LinkedHashSet; 39 import java.util.List; 40 import java.util.Set; 41 import java.util.regex.Matcher; 42 import java.util.regex.Pattern; 43 44 45 /** Implementation of {@link ITestSuite} */ 46 public class AtestRunner extends BaseTestSuite { 47 48 private static final Pattern CONFIG_RE = 49 Pattern.compile(".*/(?<config>[^/]+).config", Pattern.CASE_INSENSITIVE); 50 51 @Option(name = "all-abi", description = "Determine run all abi or not.") 52 private boolean mAllAbi = false; 53 54 @Option( 55 name = "wait-for-debugger", 56 description = "For InstrumentationTests, we pass debug to the instrumentation run." 57 ) 58 private boolean mDebug = false; 59 60 @Option( 61 name = "disable-target-preparers", 62 description = 63 "Skip the target preparer steps enumerated in test config. Skips the teardown step " 64 + "as well." 65 ) 66 private boolean mSkipSetUp = false; 67 68 @Option(name = "disable-teardown", description = "Skip the teardown of the target preparers.") 69 private boolean mSkipTearDown = false; 70 71 @Option( 72 name = "subprocess-report-port", 73 description = "the port where to connect to send the" + "events." 74 ) 75 private Integer mReportPort = null; 76 77 @Option( 78 name = "atest-include-filter", 79 description = 80 "the include filters to pass to a module. The expected format is" 81 + "\"<module-name>:<include-filter-value>\"", 82 importance = Importance.ALWAYS 83 ) 84 private List<String> mIncludeFilters = new ArrayList<>(); 85 86 @Option( 87 name = "tf-config-path", 88 description = 89 "Allows to run a specific TF configuration path." 90 ) 91 private List<String> mTfConfigPaths = new ArrayList<>(); 92 93 @Option( 94 name = "module-config-path", 95 description = 96 "Allows to run a specific module configuration path." 97 ) 98 private List<File> mModuleConfigPaths = new ArrayList<>(); 99 AtestRunner()100 public AtestRunner() { 101 setMultiDeviceStrategy(MultiDeviceModuleStrategy.RUN); 102 } 103 104 @Override loadingStrategy(Set<IAbi> abis, List<File> testsDirs, String suitePrefix, String suiteTag)105 public LinkedHashMap<String, IConfiguration> loadingStrategy(Set<IAbi> abis, 106 List<File> testsDirs, 107 String suitePrefix, String suiteTag) { 108 if (mTfConfigPaths.isEmpty() && mModuleConfigPaths.isEmpty()) { 109 return super.loadingStrategy(abis, testsDirs, suitePrefix, suiteTag); 110 } 111 LinkedHashMap<String, IConfiguration> loadedConfigs = new LinkedHashMap<>(); 112 loadedConfigs.putAll( 113 getModuleLoader().loadTfConfigsFromSpecifiedPaths(mTfConfigPaths, abis, suiteTag)); 114 loadedConfigs.putAll( 115 getModuleLoader().loadConfigsFromSpecifiedPaths( 116 mModuleConfigPaths, abis, suiteTag)); 117 return loadedConfigs; 118 } 119 120 @Override loadTests()121 public LinkedHashMap<String, IConfiguration> loadTests() { 122 // atest only needs to run on primary or specified abi. 123 if (getRequestedAbi() == null && !mAllAbi) { 124 setPrimaryAbiRun(true); 125 } 126 LinkedHashMap<String, IConfiguration> configMap = super.loadTests(); 127 LinkedHashMap<String, HashSet<String>> includeFilters = getIncludeFilters(); 128 for (IConfiguration testConfig : configMap.values()) { 129 if (mSkipSetUp || mSkipTearDown) { 130 disableTargetPreparers(testConfig, mSkipSetUp, mSkipTearDown); 131 } 132 if (mDebug) { 133 addDebugger(testConfig); 134 } 135 136 // Inject include-filter to test. 137 HashSet<String> moduleFilters = 138 includeFilters.get(canonicalizeConfigName(testConfig.getName())); 139 if (moduleFilters != null) { 140 for (String filter : moduleFilters) { 141 addFilter(testConfig, filter); 142 } 143 } 144 } 145 146 return configMap; 147 } 148 149 /** Get a collection of include filters grouped by module name. */ getIncludeFilters()150 private LinkedHashMap<String, HashSet<String>> getIncludeFilters() { 151 LinkedHashMap<String, HashSet<String>> includeFilters = 152 new LinkedHashMap<String, HashSet<String>>(); 153 for (String filter : mIncludeFilters) { 154 int moduleSep = filter.indexOf(":"); 155 if (moduleSep == -1) { 156 throw new RuntimeException("Expected delimiter ':' for module or class."); 157 } 158 String moduleName = canonicalizeConfigName(filter.substring(0, moduleSep)); 159 String moduleFilter = filter.substring(moduleSep + 1); 160 HashSet<String> moduleFilters = includeFilters.get(moduleName); 161 if (moduleFilters == null) { 162 moduleFilters = new LinkedHashSet<>(); 163 includeFilters.put(moduleName, moduleFilters); 164 } 165 moduleFilters.add(moduleFilter); 166 } 167 return includeFilters; 168 } 169 170 /** 171 * Non-integrated modules have full file paths as their name, .e.g /foo/bar/name.config, but all 172 * we want is the name. 173 */ canonicalizeConfigName(String originalName)174 private String canonicalizeConfigName(String originalName) { 175 Matcher match = CONFIG_RE.matcher(originalName); 176 if (match.find()) { 177 return match.group("config"); 178 } 179 return originalName; 180 } 181 182 /** 183 * Add filter to the tests in an IConfiguration. 184 * 185 * @param testConfig The configuration containing tests to filter. 186 * @param filter The filter to add to the tests in the testConfig. 187 */ addFilter(IConfiguration testConfig, String filter)188 private void addFilter(IConfiguration testConfig, String filter) { 189 List<IRemoteTest> tests = testConfig.getTests(); 190 for (IRemoteTest test : tests) { 191 if (test instanceof ITestFilterReceiver) { 192 CLog.d( 193 "%s:%s - Applying filter (%s)", 194 testConfig.getName(), test.getClass().getSimpleName(), filter); 195 ((ITestFilterReceiver) test).addIncludeFilter(filter); 196 } else { 197 CLog.e( 198 "Test Class (%s) does not support filtering. Cannot apply filter: %s.\n" 199 + "Please update test to use a class that implements " 200 + "ITestFilterReceiver. Running entire test module instead.", 201 test.getClass().getSimpleName(), filter); 202 } 203 } 204 } 205 206 /** Return a ConfigurationFactory instance. Organized this way for testing purposes. */ loadConfigFactory()207 public IConfigurationFactory loadConfigFactory() { 208 return ConfigurationFactory.getInstance(); 209 } 210 211 /** {@inheritDoc} */ 212 @Override createModuleListeners()213 protected List<ITestInvocationListener> createModuleListeners() { 214 List<ITestInvocationListener> listeners = super.createModuleListeners(); 215 if (mReportPort != null) { 216 SubprocessResultsReporter subprocessResult = new SubprocessResultsReporter(); 217 OptionCopier.copyOptionsNoThrow(this, subprocessResult); 218 listeners.add(subprocessResult); 219 } 220 return listeners; 221 } 222 223 /** Helper to attach the debugger to any Instrumentation tests in the config. */ addDebugger(IConfiguration testConfig)224 private void addDebugger(IConfiguration testConfig) { 225 for (IRemoteTest test : testConfig.getTests()) { 226 if (test instanceof InstrumentationTest) { 227 ((InstrumentationTest) test).setDebug(true); 228 } 229 if (test instanceof IsolatedHostTest) { 230 ((IsolatedHostTest) test).setDebug(true); 231 } 232 } 233 } 234 235 /** Helper to disable TargetPreparers of a test. */ disableTargetPreparers( IConfiguration testConfig, boolean skipSetUp, boolean skipTearDown)236 private void disableTargetPreparers( 237 IConfiguration testConfig, boolean skipSetUp, boolean skipTearDown) { 238 for (ITargetPreparer targetPreparer : testConfig.getTargetPreparers()) { 239 if (skipSetUp) { 240 CLog.d( 241 "%s: Disabling Target Preparer (%s)", 242 testConfig.getName(), targetPreparer.getClass().getSimpleName()); 243 targetPreparer.setDisable(true); 244 } else if (skipTearDown) { 245 CLog.d( 246 "%s: Disabling Target Preparer TearDown (%s)", 247 testConfig.getName(), targetPreparer.getClass().getSimpleName()); 248 targetPreparer.setDisableTearDown(true); 249 } 250 } 251 } 252 } 253