Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 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 | """ |
| 16 | Test Finder Handler module. |
| 17 | """ |
| 18 | |
mikehoran | 8bf6d08 | 2018-02-26 16:22:06 -0800 | [diff] [blame] | 19 | import logging |
| 20 | |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 21 | import atest_enum |
| 22 | from test_finders import test_finder_base |
easoncylee | 5f5cf03 | 2018-06-26 16:17:08 +0800 | [diff] [blame] | 23 | from test_finders import suite_plan_finder |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 24 | from test_finders import tf_integration_finder |
| 25 | from test_finders import module_finder |
| 26 | |
| 27 | # List of default test finder classes. |
| 28 | _TEST_FINDERS = { |
easoncylee | 5f5cf03 | 2018-06-26 16:17:08 +0800 | [diff] [blame] | 29 | suite_plan_finder.SuitePlanFinder, |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 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. |
kellyhung | f4b5947 | 2018-05-10 17:00:10 +0800 | [diff] [blame] | 46 | # 8. CC_CLASS: Test case in cc file. |
easoncylee | 5f5cf03 | 2018-06-26 16:17:08 +0800 | [diff] [blame] | 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. |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 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', |
easoncylee | 5f5cf03 | 2018-06-26 16:17:08 +0800 | [diff] [blame] | 53 | 'SUITE', 'CC_CLASS', 'SUITE_PLAN', |
| 54 | 'SUITE_PLAN_FILE_PATH']) |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 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, |
mikehoran | 6740a41 | 2018-02-20 15:04:16 -0800 | [diff] [blame] | 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, |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 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, |
kellyhung | f4b5947 | 2018-05-10 17:00:10 +0800 | [diff] [blame] | 68 | _REFERENCE_TYPE.CC_CLASS: |
| 69 | module_finder.ModuleFinder.find_test_by_cc_class_name, |
easoncylee | 5f5cf03 | 2018-06-26 16:17:08 +0800 | [diff] [blame] | 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, |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 73 | } |
| 74 | |
| 75 | |
| 76 | def _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 | |
| 91 | def _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 | |
mikehoran | 6740a41 | 2018-02-20 15:04:16 -0800 | [diff] [blame] | 109 | # pylint: disable=too-many-return-statements |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 110 | def _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, |
easoncylee | 5f5cf03 | 2018-06-26 16:17:08 +0800 | [diff] [blame] | 128 | _REFERENCE_TYPE.MODULE_FILE_PATH, |
| 129 | _REFERENCE_TYPE.SUITE_PLAN_FILE_PATH] |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 130 | if '/' in ref: |
| 131 | if ref.startswith('/'): |
| 132 | return [_REFERENCE_TYPE.INTEGRATION_FILE_PATH, |
easoncylee | 5f5cf03 | 2018-06-26 16:17:08 +0800 | [diff] [blame] | 133 | _REFERENCE_TYPE.MODULE_FILE_PATH, |
| 134 | _REFERENCE_TYPE.SUITE_PLAN_FILE_PATH] |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 135 | return [_REFERENCE_TYPE.INTEGRATION_FILE_PATH, |
| 136 | _REFERENCE_TYPE.MODULE_FILE_PATH, |
| 137 | _REFERENCE_TYPE.INTEGRATION, |
easoncylee | 5f5cf03 | 2018-06-26 16:17:08 +0800 | [diff] [blame] | 138 | _REFERENCE_TYPE.SUITE_PLAN_FILE_PATH, |
Kevin Cheng | dd8fba1 | 2018-02-20 10:17:33 -0800 | [diff] [blame] | 139 | # TODO: Comment in SUITE when it's supported |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 140 | # _REFERENCE_TYPE.SUITE |
| 141 | ] |
mikehoran | 6740a41 | 2018-02-20 15:04:16 -0800 | [diff] [blame] | 142 | if '.' in ref: |
| 143 | ref_end = ref.rsplit('.', 1)[-1] |
| 144 | ref_end_is_upper = ref_end[0].isupper() |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 145 | if ':' in ref: |
| 146 | if '.' in ref: |
mikehoran | 6740a41 | 2018-02-20 15:04:16 -0800 | [diff] [blame] | 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] |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 156 | if '.' in ref: |
kellyhung | f4b5947 | 2018-05-10 17:00:10 +0800 | [diff] [blame] | 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] |
yelinhsieh | e4580a5 | 2018-08-16 15:51:54 +0800 | [diff] [blame] | 161 | if ref_end in ('java', 'kt', 'bp', 'mk', 'cc', 'cpp'): |
mikehoran | 6740a41 | 2018-02-20 15:04:16 -0800 | [diff] [blame] | 162 | return [_REFERENCE_TYPE.MODULE_FILE_PATH] |
| 163 | if ref_end == 'xml': |
easoncylee | 5f5cf03 | 2018-06-26 16:17:08 +0800 | [diff] [blame] | 164 | return [_REFERENCE_TYPE.INTEGRATION_FILE_PATH, |
| 165 | _REFERENCE_TYPE.SUITE_PLAN_FILE_PATH] |
mikehoran | 6740a41 | 2018-02-20 15:04:16 -0800 | [diff] [blame] | 166 | if ref_end_is_upper: |
| 167 | return [_REFERENCE_TYPE.QUALIFIED_CLASS] |
Jim Tang | c8dadcd | 2019-04-22 11:53:42 +0800 | [diff] [blame] | 168 | return [_REFERENCE_TYPE.MODULE, |
| 169 | _REFERENCE_TYPE.PACKAGE] |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 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. |
easoncylee | 5f5cf03 | 2018-06-26 16:17:08 +0800 | [diff] [blame] | 173 | return [_REFERENCE_TYPE.INTEGRATION, |
Kevin Cheng | dd8fba1 | 2018-02-20 10:17:33 -0800 | [diff] [blame] | 174 | # TODO: Comment in SUITE when it's supported |
easoncylee | 2dd42a9 | 2018-04-26 15:49:56 +0800 | [diff] [blame] | 175 | # _REFERENCE_TYPE.SUITE, |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 176 | _REFERENCE_TYPE.MODULE, |
easoncylee | 5f5cf03 | 2018-06-26 16:17:08 +0800 | [diff] [blame] | 177 | _REFERENCE_TYPE.SUITE_PLAN, |
kellyhung | f4b5947 | 2018-05-10 17:00:10 +0800 | [diff] [blame] | 178 | _REFERENCE_TYPE.CLASS, |
| 179 | _REFERENCE_TYPE.CC_CLASS] |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 180 | |
| 181 | |
| 182 | def _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( |
nelsonli | 34997d5 | 2018-08-17 09:43:28 +0800 | [diff] [blame] | 201 | finder_instance, find_method_info.find_method, finder.NAME)) |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 202 | return find_methods |
| 203 | |
| 204 | |
| 205 | def _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) |
mikehoran | 8bf6d08 | 2018-02-26 16:22:06 -0800 | [diff] [blame] | 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: |
Kevin Cheng | dd8fba1 | 2018-02-20 10:17:33 -0800 | [diff] [blame] | 222 | find_method = _REF_TYPE_TO_FUNC_MAP[test_ref_type] |
| 223 | finder_instance = finder_instance_dict[find_method.im_class.NAME] |
nelsonli | 34997d5 | 2018-08-17 09:43:28 +0800 | [diff] [blame] | 224 | finder_info = _REFERENCE_TYPE[test_ref_type] |
Kevin Cheng | dd8fba1 | 2018-02-20 10:17:33 -0800 | [diff] [blame] | 225 | find_methods.append(test_finder_base.Finder(finder_instance, |
nelsonli | 34997d5 | 2018-08-17 09:43:28 +0800 | [diff] [blame] | 226 | find_method, |
| 227 | finder_info)) |
Kevin Cheng | 8b2c94c | 2017-12-18 14:43:26 -0800 | [diff] [blame] | 228 | return find_methods |
| 229 | |
| 230 | |
| 231 | def 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 |