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