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