• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.config.filter;
17 
18 import com.android.tradefed.config.IConfiguration;
19 import com.android.tradefed.config.Option;
20 import com.android.tradefed.config.OptionClass;
21 import com.android.tradefed.log.LogUtil.CLog;
22 import com.android.tradefed.service.TradefedFeatureClient;
23 import com.android.tradefed.testtype.IRemoteTest;
24 import com.android.tradefed.testtype.ITestFilterReceiver;
25 import com.android.tradefed.testtype.suite.BaseTestSuite;
26 import com.android.tradefed.testtype.suite.SuiteTestFilter;
27 import com.android.tradefed.testtype.suite.TestMappingSuiteRunner;
28 
29 import com.google.common.annotations.VisibleForTesting;
30 import com.google.common.base.Strings;
31 import com.proto.tradefed.feature.FeatureResponse;
32 import com.proto.tradefed.feature.PartResponse;
33 
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.HashMap;
37 import java.util.LinkedHashSet;
38 import java.util.List;
39 import java.util.Set;
40 
41 /** Filter options applied to the invocation. */
42 @OptionClass(alias = "global-filters")
43 public final class GlobalTestFilter {
44 
45     public static final String INCLUDE_FILTER_OPTION = "include-filter";
46     public static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
47     public static final String STRICT_INCLUDE_FILTER_OPTION = "strict-include-filter";
48     public static final String DELIMITER_NAME = "delimiter";
49 
50     @Option(
51             name = INCLUDE_FILTER_OPTION,
52             description =
53                     "Filters applied to the invocation. Format: [abi] [module-name]"
54                             + " [test-class][#method-name]")
55     private Set<String> mIncludeFilters = new LinkedHashSet<>();
56 
57     @Option(
58             name = EXCLUDE_FILTER_OPTION,
59             description =
60                     "Filters applied to the invocation. Format: [abi] [module-name]"
61                             + " [test-class][#method-name]")
62     private Set<String> mExcludeFilters = new LinkedHashSet<>();
63 
64     @Option(
65             name = STRICT_INCLUDE_FILTER_OPTION,
66             description =
67                     "Filters applied to the invocation. Format: [abi] [module-name]"
68                             + " [test-class][#method-name]. All other filters "
69                             + "will be ignored to strictly run this set."
70                             + "This is still best-effort as not all runners "
71                             + "support filtering equally.")
72     private Set<String> mStrictIncludeFilters = new LinkedHashSet<>();
73 
74     @Option(
75             name = "disable-global-filters",
76             description = "Feature flag to enable the global filters")
77     private boolean mDisable = false;
78 
79     private TradefedFeatureClient mClient;
80     private boolean mSetupDone = false;
81 
GlobalTestFilter()82     public GlobalTestFilter() {}
83 
84     @VisibleForTesting
GlobalTestFilter(TradefedFeatureClient client)85     GlobalTestFilter(TradefedFeatureClient client) {
86         mClient = client;
87     }
88 
89     /** Returns the Set of global include filters. */
getIncludeFilters()90     public Set<String> getIncludeFilters() {
91         return new LinkedHashSet<>(mIncludeFilters);
92     }
93 
94     /** Returns the Set of global exclude filters. */
getExcludeFilters()95     public Set<String> getExcludeFilters() {
96         return new LinkedHashSet<>(mExcludeFilters);
97     }
98 
99     /** Returns the Set of global strict include filters. */
getStrictIncludeFilters()100     public Set<String> getStrictIncludeFilters() {
101         return new LinkedHashSet<>(mStrictIncludeFilters);
102     }
103 
addPreviousPassedTests(Set<String> previousPassed)104     public void addPreviousPassedTests(Set<String> previousPassed) {
105         if (!previousPassed.isEmpty()) {
106             CLog.d("Adding following exclusion to GlobalTestFilter: %s", previousPassed);
107         }
108         mExcludeFilters.addAll(previousPassed);
109     }
110 
111     /** Initialize the global filters by passing them to the tests. */
setUpFilters(IConfiguration config, Set<String> demotedList)112     public void setUpFilters(IConfiguration config, Set<String> demotedList) {
113         if (mDisable) {
114             CLog.d("Global filters are disabled.");
115             return;
116         }
117         if (mSetupDone) {
118             CLog.d("Global filters already set.");
119             return;
120         }
121         CLog.d("Setting up global filters");
122         // If it's a subprocess, fetch filters
123         if (config.getCommandOptions().getInvocationData().containsKey("subprocess")) {
124             populateGlobalFilters();
125         } else {
126             if (!demotedList.isEmpty()) {
127                 CLog.d("Adding demoted list to global filters: %s", demotedList);
128                 mExcludeFilters.addAll(demotedList);
129             }
130         }
131         // Apply filters
132         if (mStrictIncludeFilters.isEmpty()) {
133             for (IRemoteTest test : config.getTests()) {
134                 if (test instanceof BaseTestSuite) {
135                     ((BaseTestSuite) test).setIncludeFilter(mIncludeFilters);
136                     ((BaseTestSuite) test).setExcludeFilter(mExcludeFilters);
137                 } else if (test instanceof ITestFilterReceiver) {
138                     ITestFilterReceiver filterableTest = (ITestFilterReceiver) test;
139                     applyFiltersToTest(filterableTest);
140                 }
141             }
142         } else {
143             CLog.d("Strict include filters specified: %s", mStrictIncludeFilters);
144             for (IRemoteTest test : config.getTests()) {
145                 if (test instanceof BaseTestSuite) {
146                     applyGlobalStrictFilters((BaseTestSuite) test, mStrictIncludeFilters);
147                 } else if (test instanceof ITestFilterReceiver) {
148                     ITestFilterReceiver filterableTest = (ITestFilterReceiver) test;
149                     applyFiltersToTest(filterableTest);
150                 }
151             }
152         }
153         mSetupDone = true;
154     }
155 
applyGlobalStrictFilters(BaseTestSuite test, Set<String> strictFilters)156     public static void applyGlobalStrictFilters(BaseTestSuite test, Set<String> strictFilters) {
157         ((BaseTestSuite) test).clearExcludeFilter();
158         ((BaseTestSuite) test).clearIncludeFilter();
159         ((BaseTestSuite) test).setIncludeFilter(strictFilters);
160         if (test instanceof TestMappingSuiteRunner) {
161             ((TestMappingSuiteRunner) test).clearTestGroup();
162             ((TestMappingSuiteRunner) test).clearKeywords();
163             ((TestMappingSuiteRunner) test).clearTestMappingPaths();
164         }
165     }
166 
167     /** Apply the global filters to the test. */
applyFiltersToTest(ITestFilterReceiver filterableTest)168     public void applyFiltersToTest(ITestFilterReceiver filterableTest) {
169         if (mStrictIncludeFilters.isEmpty()) {
170             Set<String> includeFilters = new LinkedHashSet<>(filterableTest.getIncludeFilters());
171             includeFilters.addAll(filtersFromGlobal(mIncludeFilters));
172             filterableTest.clearIncludeFilters();
173             filterableTest.addAllIncludeFilters(includeFilters);
174 
175             Set<String> excludeFilters = new LinkedHashSet<>(filterableTest.getExcludeFilters());
176             excludeFilters.addAll(filtersFromGlobal(mExcludeFilters));
177             filterableTest.clearExcludeFilters();
178             filterableTest.addAllExcludeFilters(excludeFilters);
179         } else {
180             filterableTest.clearExcludeFilters();
181             filterableTest.clearIncludeFilters();
182             filterableTest.addAllIncludeFilters(filtersFromGlobal(mStrictIncludeFilters));
183         }
184     }
185 
186     /** Apply global filters to the suite */
applyFiltersToTest(BaseTestSuite suite)187     public void applyFiltersToTest(BaseTestSuite suite) {
188         if (mStrictIncludeFilters.isEmpty()) {
189             suite.setIncludeFilter(mIncludeFilters);
190             suite.setExcludeFilter(mExcludeFilters);
191         } else {
192             CLog.d("Applying strict filters to suite.");
193             suite.clearExcludeFilter();
194             suite.clearIncludeFilter();
195             suite.setIncludeFilter(mStrictIncludeFilters);
196             if (suite instanceof TestMappingSuiteRunner) {
197                 ((TestMappingSuiteRunner) suite).clearTestGroup();
198                 ((TestMappingSuiteRunner) suite).clearKeywords();
199                 ((TestMappingSuiteRunner) suite).clearTestMappingPaths();
200             }
201         }
202         suite.reevaluateFilters();
203     }
204 
205     /** Fetch and populate global filters if needed. */
populateGlobalFilters()206     private void populateGlobalFilters() {
207         if (mClient == null) {
208             mClient = new TradefedFeatureClient();
209         }
210         try {
211             FeatureResponse globalFilters =
212                     mClient.triggerFeature(
213                             GlobalFilterGetter.GLOBAL_FILTER_GETTER, new HashMap<>());
214             if (globalFilters.hasMultiPartResponse()) {
215                 String delimiter = ",";
216                 for (PartResponse rep :
217                         globalFilters.getMultiPartResponse().getResponsePartList()) {
218                     if (rep.getKey().equals(DELIMITER_NAME)) {
219                         delimiter = rep.getValue().trim();
220                     }
221                 }
222 
223                 for (PartResponse rep :
224                         globalFilters.getMultiPartResponse().getResponsePartList()) {
225                     if (rep.getKey().equals(INCLUDE_FILTER_OPTION)) {
226                         mIncludeFilters.addAll(splitStringFilters(delimiter, rep.getValue()));
227                     } else if (rep.getKey().equals(EXCLUDE_FILTER_OPTION)) {
228                         mExcludeFilters.addAll(splitStringFilters(delimiter, rep.getValue()));
229                     } else if (rep.getKey().equals(STRICT_INCLUDE_FILTER_OPTION)) {
230                         mStrictIncludeFilters.addAll(splitStringFilters(delimiter, rep.getValue()));
231                     } else if (rep.getKey().equals(DELIMITER_NAME)) {
232                         // Ignore
233                     } else {
234                         CLog.w("Unexpected response key '%s' for global filters", rep.getKey());
235                     }
236                 }
237             } else {
238                 CLog.w("Unexpected response for global filters: %s", globalFilters);
239             }
240         } finally {
241             mClient.close();
242         }
243     }
244 
splitStringFilters(String delimiter, String value)245     private List<String> splitStringFilters(String delimiter, String value) {
246         if (Strings.isNullOrEmpty(value)) {
247             return new ArrayList<String>();
248         }
249         return Arrays.asList(value.split(delimiter));
250     }
251 
filtersFromGlobal(Set<String> filters)252     private Set<String> filtersFromGlobal(Set<String> filters) {
253         Set<String> globalFilters = new LinkedHashSet<>();
254         filters.forEach(
255                 f -> {
256                     SuiteTestFilter suiteFilter = SuiteTestFilter.createFrom(f);
257                     if (!Strings.isNullOrEmpty(suiteFilter.getTest())) {
258                         globalFilters.add(suiteFilter.getTest());
259                     } else if (!Strings.isNullOrEmpty(suiteFilter.getName())) {
260                         // For non-suite, if the test isn't present due to no module, fallback to
261                         // name
262                         globalFilters.add(suiteFilter.getName());
263                     }
264                 });
265         return globalFilters;
266     }
267 }
268