• 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
26from enum import unique, Enum
27
28from atest import constants
29
30from atest.test_finders import cache_finder
31from atest.test_finders import test_finder_base
32from atest.test_finders import test_finder_utils
33from atest.test_finders import suite_plan_finder
34from atest.test_finders import tf_integration_finder
35from atest.test_finders import module_finder
36
37# List of default test finder classes.
38_TEST_FINDERS = {
39    suite_plan_finder.SuitePlanFinder,
40    tf_integration_finder.TFIntegrationFinder,
41    module_finder.ModuleFinder,
42    cache_finder.CacheFinder,
43}
44
45@unique
46class FinderMethod(Enum):
47    """An enum object for test finders.
48
49    Explanation of FinderMethod:
50    0. MODULE: LOCAL_MODULE or LOCAL_PACKAGE_NAME value in Android.mk/Android.bp.
51    1. MAINLINE_MODULE: module[mod1.apk+mod2.apex] pattern in TEST_MAPPING files.
52    2. CLASS: Names which the same with a ClassName.java/kt file.
53    3. QUALIFIED_CLASS: String like "a.b.c.ClassName".
54    4. MODULE_CLASS: Combo of MODULE and CLASS as "module:class".
55    5. PACKAGE: Package in java file. Same as file path to java file.
56    6. MODULE_PACKAGE: Combo of MODULE and PACKAGE as "module:package".
57    7. MODULE_FILE_PATH: File path to dir of tests or test itself.
58    8. INTEGRATION_FILE_PATH: File path to config xml in one of the 4 integration
59                              config directories.
60    9. INTEGRATION: xml file name in one of the 4 integration config directories.
61    10. SUITE: Value of the "run-suite-tag" in xml config file in 4 config dirs.
62               Same as value of "test-suite-tag" in AndroidTest.xml files.
63    11. CC_CLASS: Test case in cc file.
64    12. SUITE_PLAN: Suite name such as cts.
65    13. SUITE_PLAN_FILE_PATH: File path to config xml in the suite config
66                              directories.
67    14. CACHE: A pseudo type that runs cache_finder without finding test in real.
68    15: CONFIG: Find tests by the given AndroidTest.xml file path.
69    """
70    MODULE = ('MODULE',
71              module_finder.ModuleFinder.find_test_by_module_name)
72    MAINLINE_MODULE = (
73        'MAINLINE_MODULE',
74        module_finder.MainlineModuleFinder.find_test_by_module_name)
75    CLASS = ('CLASS', module_finder.ModuleFinder.find_test_by_class_name)
76    MODULE_CLASS = (
77        'MODULE_CLASS',
78        module_finder.ModuleFinder.find_test_by_module_and_class)
79    QUALIFIED_CLASS = (
80        'QUALIFIED_CLASS', module_finder.ModuleFinder.find_test_by_class_name)
81    PACKAGE = ('PACKAGE', module_finder.ModuleFinder.find_test_by_package_name)
82    MODULE_PACKAGE = (
83        'MODULE_PACKAGE',
84        module_finder.ModuleFinder.find_test_by_module_and_package)
85    MODULE_FILE_PATH = (
86        'MODULE_FILE_PATH', module_finder.ModuleFinder.find_test_by_path)
87    INTEGRATION_FILE_PATH = (
88        'INTEGRATION_FILE_PATH',
89        tf_integration_finder.TFIntegrationFinder.find_int_test_by_path)
90    INTEGRATION = (
91        'INTEGRATION',
92        tf_integration_finder.TFIntegrationFinder.find_test_by_integration_name)
93    CC_CLASS = ('CC_CLASS',
94                module_finder.ModuleFinder.find_test_by_cc_class_name)
95    SUITE_PLAN = ('SUITE_PLAN',
96                  suite_plan_finder.SuitePlanFinder.find_test_by_suite_name)
97    SUITE_PLAN_FILE_PATH = (
98        'SUITE_PLAN_FILE_PATH',
99        suite_plan_finder.SuitePlanFinder.find_test_by_suite_path)
100    CACHE = ('CACHE', cache_finder.CacheFinder.find_test_by_cache)
101    CONFIG = ('CONFIG', module_finder.ModuleFinder.find_test_by_config_name)
102
103    def __init__(self, name, method):
104        self._name = name
105        self._method = method
106
107    def get_name(self):
108        """Return finder's name."""
109        return self._name
110
111    def get_method(self):
112        """Return finder's method."""
113        return self._method
114
115def _get_finder_instance_dict(module_info):
116    """Return dict of finder instances.
117
118    Args:
119        module_info: ModuleInfo for finder classes to use.
120
121    Returns:
122        Dict of finder instances keyed by their name.
123    """
124    instance_dict = {}
125    for finder in _get_test_finders():
126        instance_dict[finder.NAME] = finder(module_info=module_info)
127    return instance_dict
128
129
130def _get_test_finders():
131    """Returns the test finders.
132
133    If external test types are defined outside atest, they can be try-except
134    imported into here.
135
136    Returns:
137        Set of test finder classes.
138    """
139    test_finders_list = _TEST_FINDERS
140    # Example import of external test finder:
141    try:
142        from test_finders import example_finder
143        test_finders_list.add(example_finder.ExampleFinder)
144    except ImportError:
145        pass
146    return test_finders_list
147
148# pylint: disable=too-many-branches
149# pylint: disable=too-many-return-statements
150def _get_test_reference_types(ref):
151    """Determine type of test reference based on the content of string.
152
153    Examples:
154        The string 'SequentialRWTest' could be a reference to
155        a Module or a Class name.
156
157        The string 'cts/tests/filesystem' could be a Path, Integration
158        or Suite reference.
159
160    Args:
161        ref: A string referencing a test.
162
163    Returns:
164        A list of possible REFERENCE_TYPEs (ints) for reference string.
165    """
166    if ref.startswith('.') or '..' in ref:
167        return [FinderMethod.CACHE,
168                FinderMethod.MODULE_FILE_PATH,
169                FinderMethod.INTEGRATION_FILE_PATH,
170                FinderMethod.SUITE_PLAN_FILE_PATH]
171    if '/' in ref:
172        if ref.startswith('/'):
173            return [FinderMethod.CACHE,
174                    FinderMethod.MODULE_FILE_PATH,
175                    FinderMethod.INTEGRATION_FILE_PATH,
176                    FinderMethod.SUITE_PLAN_FILE_PATH]
177        if ':' in ref:
178            return [FinderMethod.CACHE,
179                    FinderMethod.MODULE_FILE_PATH,
180                    FinderMethod.INTEGRATION_FILE_PATH,
181                    FinderMethod.INTEGRATION,
182                    FinderMethod.SUITE_PLAN_FILE_PATH,
183                    FinderMethod.MODULE_CLASS]
184        return [FinderMethod.CACHE,
185                FinderMethod.MODULE_FILE_PATH,
186                FinderMethod.INTEGRATION_FILE_PATH,
187                FinderMethod.INTEGRATION,
188                FinderMethod.SUITE_PLAN_FILE_PATH,
189                FinderMethod.CC_CLASS,
190                # TODO: Uncomment in SUITE when it's supported
191                # FinderMethod.SUITE
192                ]
193    if constants.TEST_WITH_MAINLINE_MODULES_RE.match(ref):
194        return [FinderMethod.CACHE, FinderMethod.MAINLINE_MODULE]
195    if '.' in ref:
196        ref_end = ref.rsplit('.', 1)[-1]
197        ref_end_is_upper = ref_end[0].isupper()
198    # parse_test_reference() will return none empty dictionary if input test
199    # reference match $module:$package_class.
200    if test_finder_utils.parse_test_reference(ref):
201        if '.' in ref:
202            if ref_end_is_upper:
203                # Module:fully.qualified.Class or Integration:fully.q.Class
204                return [FinderMethod.CACHE,
205                        FinderMethod.MODULE_CLASS,
206                        FinderMethod.INTEGRATION]
207            # Module:some.package
208            return [FinderMethod.CACHE, FinderMethod.MODULE_PACKAGE,
209                    FinderMethod.MODULE_CLASS]
210        # Module:Class or IntegrationName:Class
211        return [FinderMethod.CACHE,
212                FinderMethod.MODULE_CLASS,
213                FinderMethod.INTEGRATION]
214    if '.' in ref:
215        # The string of ref_end possibly includes specific mathods, e.g.
216        # foo.java#method, so let ref_end be the first part of splitting '#'.
217        if "#" in ref_end:
218            ref_end = ref_end.split('#')[0]
219        if ref_end in ('java', 'kt', 'bp', 'mk', 'cc', 'cpp'):
220            return [FinderMethod.CACHE, FinderMethod.MODULE_FILE_PATH]
221        if ref_end == 'xml':
222            return [FinderMethod.CACHE,
223                    FinderMethod.INTEGRATION_FILE_PATH,
224                    FinderMethod.SUITE_PLAN_FILE_PATH]
225        # (b/207327349) ref_end_is_upper does not guarantee a classname anymore.
226        return [FinderMethod.CACHE,
227                FinderMethod.MODULE,
228                FinderMethod.QUALIFIED_CLASS,
229                FinderMethod.PACKAGE]
230    # Note: We assume that if you're referencing a file in your cwd,
231    # that file must have a '.' in its name, i.e. foo.java, foo.xml.
232    # If this ever becomes not the case, then we need to include path below.
233    return [FinderMethod.CACHE,
234            FinderMethod.MODULE,
235            FinderMethod.INTEGRATION,
236            # TODO: Uncomment in SUITE when it's supported
237            # FinderMethod.SUITE,
238            FinderMethod.CONFIG,
239            FinderMethod.SUITE_PLAN,
240            FinderMethod.CLASS,
241            FinderMethod.CC_CLASS]
242
243
244def _get_registered_find_methods(module_info):
245    """Return list of registered find methods.
246
247    This is used to return find methods that were not listed in the
248    default find methods but just registered in the finder classes. These
249    find methods will run before the default find methods.
250
251    Args:
252        module_info: ModuleInfo for finder classes to instantiate with.
253
254    Returns:
255        List of registered find methods.
256    """
257    find_methods = []
258    finder_instance_dict = _get_finder_instance_dict(module_info)
259    for finder in _get_test_finders():
260        finder_instance = finder_instance_dict[finder.NAME]
261        for find_method_info in finder_instance.get_all_find_methods():
262            find_methods.append(test_finder_base.Finder(
263                finder_instance, find_method_info.find_method, finder.NAME))
264    return find_methods
265
266
267def _get_default_find_methods(module_info, test):
268    """Default find methods to be used based on the given test name.
269
270    Args:
271        module_info: ModuleInfo for finder instances to use.
272        test: String of test name to help determine which find methods
273              to utilize.
274
275    Returns:
276        List of find methods to use.
277    """
278    find_methods = []
279    finder_instance_dict = _get_finder_instance_dict(module_info)
280    test_ref_types = _get_test_reference_types(test)
281    logging.debug('Resolved input to possible references: %s', ', '.join([
282        t.get_name() for t in test_ref_types]))
283    for test_ref_type in test_ref_types:
284        find_method = test_ref_type.get_method()
285        finder_instance = finder_instance_dict[inspect._findclass(find_method).NAME]
286        finder_info = test_ref_type.get_name()
287        find_methods.append(test_finder_base.Finder(finder_instance,
288                                                    find_method,
289                                                    finder_info))
290    return find_methods
291
292
293def get_find_methods_for_test(module_info, test):
294    """Return a list of ordered find methods.
295
296    Args:
297      test: String of test name to get find methods for.
298
299    Returns:
300        List of ordered find methods.
301    """
302    registered_find_methods = _get_registered_find_methods(module_info)
303    default_find_methods = _get_default_find_methods(module_info, test)
304    return registered_find_methods + default_find_methods
305