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