# 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