1# Copyright 2018, The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15""" 16Test Finder Handler module. 17""" 18 19# pylint: disable=line-too-long 20# pylint: disable=import-outside-toplevel 21# pylint: disable=protected-access 22 23import inspect 24import logging 25 26from enum import unique, Enum 27 28from atest import constants 29 30from atest.test_finders import cache_finder 31from atest.test_finders import test_finder_base 32from atest.test_finders import test_finder_utils 33from atest.test_finders import suite_plan_finder 34from atest.test_finders import tf_integration_finder 35from atest.test_finders import module_finder 36 37# List of default test finder classes. 38_TEST_FINDERS = { 39 suite_plan_finder.SuitePlanFinder, 40 tf_integration_finder.TFIntegrationFinder, 41 module_finder.ModuleFinder, 42 cache_finder.CacheFinder, 43} 44 45@unique 46class FinderMethod(Enum): 47 """An enum object for test finders. 48 49 Explanation of FinderMethod: 50 0. MODULE: LOCAL_MODULE or LOCAL_PACKAGE_NAME value in Android.mk/Android.bp. 51 1. MAINLINE_MODULE: module[mod1.apk+mod2.apex] pattern in TEST_MAPPING files. 52 2. CLASS: Names which the same with a ClassName.java/kt file. 53 3. QUALIFIED_CLASS: String like "a.b.c.ClassName". 54 4. MODULE_CLASS: Combo of MODULE and CLASS as "module:class". 55 5. PACKAGE: Package in java file. Same as file path to java file. 56 6. MODULE_PACKAGE: Combo of MODULE and PACKAGE as "module:package". 57 7. MODULE_FILE_PATH: File path to dir of tests or test itself. 58 8. INTEGRATION_FILE_PATH: File path to config xml in one of the 4 integration 59 config directories. 60 9. INTEGRATION: xml file name in one of the 4 integration config directories. 61 10. SUITE: Value of the "run-suite-tag" in xml config file in 4 config dirs. 62 Same as value of "test-suite-tag" in AndroidTest.xml files. 63 11. CC_CLASS: Test case in cc file. 64 12. SUITE_PLAN: Suite name such as cts. 65 13. SUITE_PLAN_FILE_PATH: File path to config xml in the suite config 66 directories. 67 14. CACHE: A pseudo type that runs cache_finder without finding test in real. 68 15: CONFIG: Find tests by the given AndroidTest.xml file path. 69 """ 70 MODULE = ('MODULE', 71 module_finder.ModuleFinder.find_test_by_module_name) 72 MAINLINE_MODULE = ( 73 'MAINLINE_MODULE', 74 module_finder.MainlineModuleFinder.find_test_by_module_name) 75 CLASS = ('CLASS', module_finder.ModuleFinder.find_test_by_class_name) 76 MODULE_CLASS = ( 77 'MODULE_CLASS', 78 module_finder.ModuleFinder.find_test_by_module_and_class) 79 QUALIFIED_CLASS = ( 80 'QUALIFIED_CLASS', module_finder.ModuleFinder.find_test_by_class_name) 81 PACKAGE = ('PACKAGE', module_finder.ModuleFinder.find_test_by_package_name) 82 MODULE_PACKAGE = ( 83 'MODULE_PACKAGE', 84 module_finder.ModuleFinder.find_test_by_module_and_package) 85 MODULE_FILE_PATH = ( 86 'MODULE_FILE_PATH', module_finder.ModuleFinder.find_test_by_path) 87 INTEGRATION_FILE_PATH = ( 88 'INTEGRATION_FILE_PATH', 89 tf_integration_finder.TFIntegrationFinder.find_int_test_by_path) 90 INTEGRATION = ( 91 'INTEGRATION', 92 tf_integration_finder.TFIntegrationFinder.find_test_by_integration_name) 93 CC_CLASS = ('CC_CLASS', 94 module_finder.ModuleFinder.find_test_by_cc_class_name) 95 SUITE_PLAN = ('SUITE_PLAN', 96 suite_plan_finder.SuitePlanFinder.find_test_by_suite_name) 97 SUITE_PLAN_FILE_PATH = ( 98 'SUITE_PLAN_FILE_PATH', 99 suite_plan_finder.SuitePlanFinder.find_test_by_suite_path) 100 CACHE = ('CACHE', cache_finder.CacheFinder.find_test_by_cache) 101 CONFIG = ('CONFIG', module_finder.ModuleFinder.find_test_by_config_name) 102 103 def __init__(self, name, method): 104 self._name = name 105 self._method = method 106 107 def get_name(self): 108 """Return finder's name.""" 109 return self._name 110 111 def get_method(self): 112 """Return finder's method.""" 113 return self._method 114 115def _get_finder_instance_dict(module_info): 116 """Return dict of finder instances. 117 118 Args: 119 module_info: ModuleInfo for finder classes to use. 120 121 Returns: 122 Dict of finder instances keyed by their name. 123 """ 124 instance_dict = {} 125 for finder in _get_test_finders(): 126 instance_dict[finder.NAME] = finder(module_info=module_info) 127 return instance_dict 128 129 130def _get_test_finders(): 131 """Returns the test finders. 132 133 If external test types are defined outside atest, they can be try-except 134 imported into here. 135 136 Returns: 137 Set of test finder classes. 138 """ 139 test_finders_list = _TEST_FINDERS 140 # Example import of external test finder: 141 try: 142 from test_finders import example_finder 143 test_finders_list.add(example_finder.ExampleFinder) 144 except ImportError: 145 pass 146 return test_finders_list 147 148# pylint: disable=too-many-branches 149# pylint: disable=too-many-return-statements 150def _get_test_reference_types(ref): 151 """Determine type of test reference based on the content of string. 152 153 Examples: 154 The string 'SequentialRWTest' could be a reference to 155 a Module or a Class name. 156 157 The string 'cts/tests/filesystem' could be a Path, Integration 158 or Suite reference. 159 160 Args: 161 ref: A string referencing a test. 162 163 Returns: 164 A list of possible REFERENCE_TYPEs (ints) for reference string. 165 """ 166 if ref.startswith('.') or '..' in ref: 167 return [FinderMethod.CACHE, 168 FinderMethod.MODULE_FILE_PATH, 169 FinderMethod.INTEGRATION_FILE_PATH, 170 FinderMethod.SUITE_PLAN_FILE_PATH] 171 if '/' in ref: 172 if ref.startswith('/'): 173 return [FinderMethod.CACHE, 174 FinderMethod.MODULE_FILE_PATH, 175 FinderMethod.INTEGRATION_FILE_PATH, 176 FinderMethod.SUITE_PLAN_FILE_PATH] 177 if ':' in ref: 178 return [FinderMethod.CACHE, 179 FinderMethod.MODULE_FILE_PATH, 180 FinderMethod.INTEGRATION_FILE_PATH, 181 FinderMethod.INTEGRATION, 182 FinderMethod.SUITE_PLAN_FILE_PATH, 183 FinderMethod.MODULE_CLASS] 184 return [FinderMethod.CACHE, 185 FinderMethod.MODULE_FILE_PATH, 186 FinderMethod.INTEGRATION_FILE_PATH, 187 FinderMethod.INTEGRATION, 188 FinderMethod.SUITE_PLAN_FILE_PATH, 189 FinderMethod.CC_CLASS, 190 # TODO: Uncomment in SUITE when it's supported 191 # FinderMethod.SUITE 192 ] 193 if constants.TEST_WITH_MAINLINE_MODULES_RE.match(ref): 194 return [FinderMethod.CACHE, FinderMethod.MAINLINE_MODULE] 195 if '.' in ref: 196 ref_end = ref.rsplit('.', 1)[-1] 197 ref_end_is_upper = ref_end[0].isupper() 198 # parse_test_reference() will return none empty dictionary if input test 199 # reference match $module:$package_class. 200 if test_finder_utils.parse_test_reference(ref): 201 if '.' in ref: 202 if ref_end_is_upper: 203 # Module:fully.qualified.Class or Integration:fully.q.Class 204 return [FinderMethod.CACHE, 205 FinderMethod.MODULE_CLASS, 206 FinderMethod.INTEGRATION] 207 # Module:some.package 208 return [FinderMethod.CACHE, FinderMethod.MODULE_PACKAGE, 209 FinderMethod.MODULE_CLASS] 210 # Module:Class or IntegrationName:Class 211 return [FinderMethod.CACHE, 212 FinderMethod.MODULE_CLASS, 213 FinderMethod.INTEGRATION] 214 if '.' in ref: 215 # The string of ref_end possibly includes specific mathods, e.g. 216 # foo.java#method, so let ref_end be the first part of splitting '#'. 217 if "#" in ref_end: 218 ref_end = ref_end.split('#')[0] 219 if ref_end in ('java', 'kt', 'bp', 'mk', 'cc', 'cpp'): 220 return [FinderMethod.CACHE, FinderMethod.MODULE_FILE_PATH] 221 if ref_end == 'xml': 222 return [FinderMethod.CACHE, 223 FinderMethod.INTEGRATION_FILE_PATH, 224 FinderMethod.SUITE_PLAN_FILE_PATH] 225 # (b/207327349) ref_end_is_upper does not guarantee a classname anymore. 226 return [FinderMethod.CACHE, 227 FinderMethod.MODULE, 228 FinderMethod.QUALIFIED_CLASS, 229 FinderMethod.PACKAGE] 230 # Note: We assume that if you're referencing a file in your cwd, 231 # that file must have a '.' in its name, i.e. foo.java, foo.xml. 232 # If this ever becomes not the case, then we need to include path below. 233 return [FinderMethod.CACHE, 234 FinderMethod.MODULE, 235 FinderMethod.INTEGRATION, 236 # TODO: Uncomment in SUITE when it's supported 237 # FinderMethod.SUITE, 238 FinderMethod.CONFIG, 239 FinderMethod.SUITE_PLAN, 240 FinderMethod.CLASS, 241 FinderMethod.CC_CLASS] 242 243 244def _get_registered_find_methods(module_info): 245 """Return list of registered find methods. 246 247 This is used to return find methods that were not listed in the 248 default find methods but just registered in the finder classes. These 249 find methods will run before the default find methods. 250 251 Args: 252 module_info: ModuleInfo for finder classes to instantiate with. 253 254 Returns: 255 List of registered find methods. 256 """ 257 find_methods = [] 258 finder_instance_dict = _get_finder_instance_dict(module_info) 259 for finder in _get_test_finders(): 260 finder_instance = finder_instance_dict[finder.NAME] 261 for find_method_info in finder_instance.get_all_find_methods(): 262 find_methods.append(test_finder_base.Finder( 263 finder_instance, find_method_info.find_method, finder.NAME)) 264 return find_methods 265 266 267def _get_default_find_methods(module_info, test): 268 """Default find methods to be used based on the given test name. 269 270 Args: 271 module_info: ModuleInfo for finder instances to use. 272 test: String of test name to help determine which find methods 273 to utilize. 274 275 Returns: 276 List of find methods to use. 277 """ 278 find_methods = [] 279 finder_instance_dict = _get_finder_instance_dict(module_info) 280 test_ref_types = _get_test_reference_types(test) 281 logging.debug('Resolved input to possible references: %s', ', '.join([ 282 t.get_name() for t in test_ref_types])) 283 for test_ref_type in test_ref_types: 284 find_method = test_ref_type.get_method() 285 finder_instance = finder_instance_dict[inspect._findclass(find_method).NAME] 286 finder_info = test_ref_type.get_name() 287 find_methods.append(test_finder_base.Finder(finder_instance, 288 find_method, 289 finder_info)) 290 return find_methods 291 292 293def get_find_methods_for_test(module_info, test): 294 """Return a list of ordered find methods. 295 296 Args: 297 test: String of test name to get find methods for. 298 299 Returns: 300 List of ordered find methods. 301 """ 302 registered_find_methods = _get_registered_find_methods(module_info) 303 default_find_methods = _get_default_find_methods(module_info, test) 304 return registered_find_methods + default_find_methods 305