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