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