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