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