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