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