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