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-return-statements 123def _get_test_reference_types(ref): 124 """Determine type of test reference based on the content of string. 125 126 Examples: 127 The string 'SequentialRWTest' could be a reference to 128 a Module or a Class name. 129 130 The string 'cts/tests/filesystem' could be a Path, Integration 131 or Suite reference. 132 133 Args: 134 ref: A string referencing a test. 135 136 Returns: 137 A list of possible REFERENCE_TYPEs (ints) for reference string. 138 """ 139 if ref.startswith('.') or '..' in ref: 140 return [_REFERENCE_TYPE.CACHE, 141 _REFERENCE_TYPE.INTEGRATION_FILE_PATH, 142 _REFERENCE_TYPE.MODULE_FILE_PATH, 143 _REFERENCE_TYPE.SUITE_PLAN_FILE_PATH] 144 if '/' in ref: 145 if ref.startswith('/'): 146 return [_REFERENCE_TYPE.CACHE, 147 _REFERENCE_TYPE.INTEGRATION_FILE_PATH, 148 _REFERENCE_TYPE.MODULE_FILE_PATH, 149 _REFERENCE_TYPE.SUITE_PLAN_FILE_PATH] 150 return [_REFERENCE_TYPE.CACHE, 151 _REFERENCE_TYPE.INTEGRATION_FILE_PATH, 152 _REFERENCE_TYPE.MODULE_FILE_PATH, 153 _REFERENCE_TYPE.INTEGRATION, 154 _REFERENCE_TYPE.SUITE_PLAN_FILE_PATH, 155 # TODO: Uncomment in SUITE when it's supported 156 # _REFERENCE_TYPE.SUITE 157 ] 158 if '.' in ref: 159 ref_end = ref.rsplit('.', 1)[-1] 160 ref_end_is_upper = ref_end[0].isupper() 161 if ':' in ref: 162 if '.' in ref: 163 if ref_end_is_upper: 164 # Module:fully.qualified.Class or Integration:fully.q.Class 165 return [_REFERENCE_TYPE.CACHE, 166 _REFERENCE_TYPE.INTEGRATION, 167 _REFERENCE_TYPE.MODULE_CLASS] 168 # Module:some.package 169 return [_REFERENCE_TYPE.CACHE, _REFERENCE_TYPE.MODULE_PACKAGE, 170 _REFERENCE_TYPE.MODULE_CLASS] 171 # Module:Class or IntegrationName:Class 172 return [_REFERENCE_TYPE.CACHE, 173 _REFERENCE_TYPE.INTEGRATION, 174 _REFERENCE_TYPE.MODULE_CLASS] 175 if '.' in ref: 176 # The string of ref_end possibly includes specific mathods, e.g. 177 # foo.java#method, so let ref_end be the first part of splitting '#'. 178 if "#" in ref_end: 179 ref_end = ref_end.split('#')[0] 180 if ref_end in ('java', 'kt', 'bp', 'mk', 'cc', 'cpp'): 181 return [_REFERENCE_TYPE.CACHE, _REFERENCE_TYPE.MODULE_FILE_PATH] 182 if ref_end == 'xml': 183 return [_REFERENCE_TYPE.CACHE, 184 _REFERENCE_TYPE.INTEGRATION_FILE_PATH, 185 _REFERENCE_TYPE.SUITE_PLAN_FILE_PATH] 186 if ref_end_is_upper: 187 return [_REFERENCE_TYPE.CACHE, _REFERENCE_TYPE.QUALIFIED_CLASS] 188 return [_REFERENCE_TYPE.CACHE, 189 _REFERENCE_TYPE.MODULE, 190 _REFERENCE_TYPE.PACKAGE] 191 # Note: We assume that if you're referencing a file in your cwd, 192 # that file must have a '.' in its name, i.e. foo.java, foo.xml. 193 # If this ever becomes not the case, then we need to include path below. 194 return [_REFERENCE_TYPE.CACHE, 195 _REFERENCE_TYPE.INTEGRATION, 196 # TODO: Uncomment in SUITE when it's supported 197 # _REFERENCE_TYPE.SUITE, 198 _REFERENCE_TYPE.MODULE, 199 _REFERENCE_TYPE.SUITE_PLAN, 200 _REFERENCE_TYPE.CLASS, 201 _REFERENCE_TYPE.CC_CLASS] 202 203 204def _get_registered_find_methods(module_info): 205 """Return list of registered find methods. 206 207 This is used to return find methods that were not listed in the 208 default find methods but just registered in the finder classes. These 209 find methods will run before the default find methods. 210 211 Args: 212 module_info: ModuleInfo for finder classes to instantiate with. 213 214 Returns: 215 List of registered find methods. 216 """ 217 find_methods = [] 218 finder_instance_dict = _get_finder_instance_dict(module_info) 219 for finder in _get_test_finders(): 220 finder_instance = finder_instance_dict[finder.NAME] 221 for find_method_info in finder_instance.get_all_find_methods(): 222 find_methods.append(test_finder_base.Finder( 223 finder_instance, find_method_info.find_method, finder.NAME)) 224 return find_methods 225 226 227def _get_default_find_methods(module_info, test): 228 """Default find methods to be used based on the given test name. 229 230 Args: 231 module_info: ModuleInfo for finder instances to use. 232 test: String of test name to help determine which find methods 233 to utilize. 234 235 Returns: 236 List of find methods to use. 237 """ 238 find_methods = [] 239 finder_instance_dict = _get_finder_instance_dict(module_info) 240 test_ref_types = _get_test_reference_types(test) 241 logging.debug('Resolved input to possible references: %s', [ 242 _REFERENCE_TYPE[t] for t in test_ref_types]) 243 for test_ref_type in test_ref_types: 244 find_method = _REF_TYPE_TO_FUNC_MAP[test_ref_type] 245 finder_instance = finder_instance_dict[inspect._findclass(find_method).NAME] 246 finder_info = _REFERENCE_TYPE[test_ref_type] 247 find_methods.append(test_finder_base.Finder(finder_instance, 248 find_method, 249 finder_info)) 250 return find_methods 251 252 253def get_find_methods_for_test(module_info, test): 254 """Return a list of ordered find methods. 255 256 Args: 257 test: String of test name to get find methods for. 258 259 Returns: 260 List of ordered find methods. 261 """ 262 registered_find_methods = _get_registered_find_methods(module_info) 263 default_find_methods = _get_default_find_methods(module_info, test) 264 return registered_find_methods + default_find_methods 265