From 5b80bfd7bffd4c20d80b7c70a7130529e9a755dd Mon Sep 17 00:00:00 2001 From: ToshikazuOhiwa Date: Mon, 30 Mar 2020 09:24:26 +0900 Subject: agl-basesystem --- external/poky/meta/lib/oeqa/core/loader.py | 366 +++++++++++++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 external/poky/meta/lib/oeqa/core/loader.py (limited to 'external/poky/meta/lib/oeqa/core/loader.py') diff --git a/external/poky/meta/lib/oeqa/core/loader.py b/external/poky/meta/lib/oeqa/core/loader.py new file mode 100644 index 00000000..e66de32c --- /dev/null +++ b/external/poky/meta/lib/oeqa/core/loader.py @@ -0,0 +1,366 @@ +# Copyright (C) 2016 Intel Corporation +# Released under the MIT license (see COPYING.MIT) + +import os +import re +import sys +import unittest +import inspect + +from oeqa.core.utils.path import findFile +from oeqa.core.utils.test import getSuiteModules, getCaseID + +from oeqa.core.exception import OEQATestNotFound +from oeqa.core.case import OETestCase +from oeqa.core.decorator import decoratorClasses, OETestDecorator, \ + OETestFilter, OETestDiscover + +# When loading tests, the unittest framework stores any exceptions and +# displays them only when the run method is called. +# +# For our purposes, it is better to raise the exceptions in the loading +# step rather than waiting to run the test suite. +# +# Generate the function definition because this differ across python versions +# Python >= 3.4.4 uses tree parameters instead four but for example Python 3.5.3 +# ueses four parameters so isn't incremental. +_failed_test_args = inspect.getfullargspec(unittest.loader._make_failed_test).args +exec("""def _make_failed_test(%s): raise exception""" % ', '.join(_failed_test_args)) +unittest.loader._make_failed_test = _make_failed_test + +def _find_duplicated_modules(suite, directory): + for module in getSuiteModules(suite): + path = findFile('%s.py' % module, directory) + if path: + raise ImportError("Duplicated %s module found in %s" % (module, path)) + +def _built_modules_dict(modules): + modules_dict = {} + + if modules == None: + return modules_dict + + for module in modules: + # Assumption: package and module names do not contain upper case + # characters, whereas class names do + m = re.match(r'^(\w+)(?:\.(\w[^.]*)(?:\.([^.]+))?)?$', module, flags=re.ASCII) + if not m: + continue + + module_name, class_name, test_name = m.groups() + + if module_name and module_name not in modules_dict: + modules_dict[module_name] = {} + if class_name and class_name not in modules_dict[module_name]: + modules_dict[module_name][class_name] = [] + if test_name and test_name not in modules_dict[module_name][class_name]: + modules_dict[module_name][class_name].append(test_name) + + return modules_dict + +class OETestLoader(unittest.TestLoader): + caseClass = OETestCase + + kwargs_names = ['testMethodPrefix', 'sortTestMethodUsing', 'suiteClass', + '_top_level_dir'] + + def __init__(self, tc, module_paths, modules, tests, modules_required, + filters, *args, **kwargs): + self.tc = tc + + self.modules = _built_modules_dict(modules) + + self.tests = tests + self.modules_required = modules_required + + self.filters = filters + self.decorator_filters = [d for d in decoratorClasses if \ + issubclass(d, OETestFilter)] + self._validateFilters(self.filters, self.decorator_filters) + self.used_filters = [d for d in self.decorator_filters + for f in self.filters + if f in d.attrs] + + if isinstance(module_paths, str): + module_paths = [module_paths] + elif not isinstance(module_paths, list): + raise TypeError('module_paths must be a str or a list of str') + self.module_paths = module_paths + + for kwname in self.kwargs_names: + if kwname in kwargs: + setattr(self, kwname, kwargs[kwname]) + + self._patchCaseClass(self.caseClass) + + super(OETestLoader, self).__init__() + + def _patchCaseClass(self, testCaseClass): + # Adds custom attributes to the OETestCase class + setattr(testCaseClass, 'tc', self.tc) + setattr(testCaseClass, 'td', self.tc.td) + setattr(testCaseClass, 'logger', self.tc.logger) + + def _validateFilters(self, filters, decorator_filters): + # Validate if filter isn't empty + for key,value in filters.items(): + if not value: + raise TypeError("Filter %s specified is empty" % key) + + # Validate unique attributes + attr_filters = [attr for clss in decorator_filters \ + for attr in clss.attrs] + dup_attr = [attr for attr in attr_filters + if attr_filters.count(attr) > 1] + if dup_attr: + raise TypeError('Detected duplicated attribute(s) %s in filter' + ' decorators' % ' ,'.join(dup_attr)) + + # Validate if filter is supported + for f in filters: + if f not in attr_filters: + classes = ', '.join([d.__name__ for d in decorator_filters]) + raise TypeError('Found "%s" filter but not declared in any of ' + '%s decorators' % (f, classes)) + + def _registerTestCase(self, case): + case_id = case.id() + self.tc._registry['cases'][case_id] = case + + def _handleTestCaseDecorators(self, case): + def _handle(obj): + if isinstance(obj, OETestDecorator): + if not obj.__class__ in decoratorClasses: + raise Exception("Decorator %s isn't registered" \ + " in decoratorClasses." % obj.__name__) + obj.bind(self.tc._registry, case) + + def _walk_closure(obj): + if hasattr(obj, '__closure__') and obj.__closure__: + for f in obj.__closure__: + obj = f.cell_contents + _handle(obj) + _walk_closure(obj) + method = getattr(case, case._testMethodName, None) + _walk_closure(method) + + def _filterTest(self, case): + """ + Returns True if test case must be filtered, False otherwise. + """ + # XXX; If the module has more than one namespace only use + # the first to support run the whole module specifying the + # .[test_class].[test_name] + module_name_small = case.__module__.split('.')[0] + module_name = case.__module__ + + class_name = case.__class__.__name__ + test_name = case._testMethodName + + # 'auto' is a reserved key word to run test cases automatically + # warn users if their test case belong to a module named 'auto' + if module_name_small == "auto": + bb.warn("'auto' is a reserved key word for TEST_SUITES. " + "But test case '%s' is detected to belong to auto module. " + "Please condier using a new name for your module." % str(case)) + + # check if case belongs to any specified module + # if 'auto' is specified, such check is skipped + if self.modules and not 'auto' in self.modules: + module = None + try: + module = self.modules[module_name_small] + except KeyError: + try: + module = self.modules[module_name] + except KeyError: + return True + + if module: + if not class_name in module: + return True + + if module[class_name]: + if test_name not in module[class_name]: + return True + + # Decorator filters + if self.filters and isinstance(case, OETestCase): + filters = self.filters.copy() + case_decorators = [cd for cd in case.decorators + if cd.__class__ in self.used_filters] + + # Iterate over case decorators to check if needs to be filtered. + for cd in case_decorators: + if cd.filtrate(filters): + return True + + # Case is missing one or more decorators for all the filters + # being used, so filter test case. + if filters: + return True + + return False + + def _getTestCase(self, testCaseClass, tcName): + if not hasattr(testCaseClass, '__oeqa_loader') and \ + issubclass(testCaseClass, OETestCase): + # In order to support data_vars validation + # monkey patch the default setUp/tearDown{Class} to use + # the ones provided by OETestCase + setattr(testCaseClass, 'setUpClassMethod', + getattr(testCaseClass, 'setUpClass')) + setattr(testCaseClass, 'tearDownClassMethod', + getattr(testCaseClass, 'tearDownClass')) + setattr(testCaseClass, 'setUpClass', + testCaseClass._oeSetUpClass) + setattr(testCaseClass, 'tearDownClass', + testCaseClass._oeTearDownClass) + + # In order to support decorators initialization + # monkey patch the default setUp/tearDown to use + # a setUpDecorators/tearDownDecorators that methods + # will call setUp/tearDown original methods. + setattr(testCaseClass, 'setUpMethod', + getattr(testCaseClass, 'setUp')) + setattr(testCaseClass, 'tearDownMethod', + getattr(testCaseClass, 'tearDown')) + setattr(testCaseClass, 'setUp', testCaseClass._oeSetUp) + setattr(testCaseClass, 'tearDown', testCaseClass._oeTearDown) + + setattr(testCaseClass, '__oeqa_loader', True) + + case = testCaseClass(tcName) + if isinstance(case, OETestCase): + setattr(case, 'decorators', []) + + return case + + def loadTestsFromTestCase(self, testCaseClass): + """ + Returns a suite of all tests cases contained in testCaseClass. + """ + if issubclass(testCaseClass, unittest.suite.TestSuite): + raise TypeError("Test cases should not be derived from TestSuite." \ + " Maybe you meant to derive %s from TestCase?" \ + % testCaseClass.__name__) + if not issubclass(testCaseClass, unittest.case.TestCase): + raise TypeError("Test %s is not derived from %s" % \ + (testCaseClass.__name__, unittest.case.TestCase.__name__)) + + testCaseNames = self.getTestCaseNames(testCaseClass) + if not testCaseNames and hasattr(testCaseClass, 'runTest'): + testCaseNames = ['runTest'] + + suite = [] + for tcName in testCaseNames: + case = self._getTestCase(testCaseClass, tcName) + # Filer by case id + if not (self.tests and not 'auto' in self.tests + and not getCaseID(case) in self.tests): + self._handleTestCaseDecorators(case) + + # Filter by decorators + if not self._filterTest(case): + self._registerTestCase(case) + suite.append(case) + + return self.suiteClass(suite) + + def _required_modules_validation(self): + """ + Search in Test context registry if a required + test is found, raise an exception when not found. + """ + + for module in self.modules_required: + found = False + + # The module name is splitted to only compare the + # first part of a test case id. + comp_len = len(module.split('.')) + for case in self.tc._registry['cases']: + case_comp = '.'.join(case.split('.')[0:comp_len]) + if module == case_comp: + found = True + break + + if not found: + raise OEQATestNotFound("Not found %s in loaded test cases" % \ + module) + + def discover(self): + big_suite = self.suiteClass() + for path in self.module_paths: + _find_duplicated_modules(big_suite, path) + suite = super(OETestLoader, self).discover(path, + pattern='*.py', top_level_dir=path) + big_suite.addTests(suite) + + cases = None + discover_classes = [clss for clss in decoratorClasses + if issubclass(clss, OETestDiscover)] + for clss in discover_classes: + cases = clss.discover(self.tc._registry) + + if self.modules_required: + self._required_modules_validation() + + return self.suiteClass(cases) if cases else big_suite + + def _filterModule(self, module): + if module.__name__ in sys.builtin_module_names: + msg = 'Tried to import %s test module but is a built-in' + raise ImportError(msg % module.__name__) + + # XXX; If the module has more than one namespace only use + # the first to support run the whole module specifying the + # .[test_class].[test_name] + module_name_small = module.__name__.split('.')[0] + module_name = module.__name__ + + # Normal test modules are loaded if no modules were specified, + # if module is in the specified module list or if 'auto' is in + # module list. + # Underscore modules are loaded only if specified in module list. + load_module = True if not module_name.startswith('_') \ + and (not self.modules \ + or module_name in self.modules \ + or module_name_small in self.modules \ + or 'auto' in self.modules) \ + else False + + load_underscore = True if module_name.startswith('_') \ + and (module_name in self.modules or \ + module_name_small in self.modules) \ + else False + + return (load_module, load_underscore) + + + # XXX After Python 3.5, remove backward compatibility hacks for + # use_load_tests deprecation via *args and **kws. See issue 16662. + if sys.version_info >= (3,5): + def loadTestsFromModule(self, module, *args, pattern=None, **kws): + """ + Returns a suite of all tests cases contained in module. + """ + load_module, load_underscore = self._filterModule(module) + + if load_module or load_underscore: + return super(OETestLoader, self).loadTestsFromModule( + module, *args, pattern=pattern, **kws) + else: + return self.suiteClass() + else: + def loadTestsFromModule(self, module, use_load_tests=True): + """ + Returns a suite of all tests cases contained in module. + """ + load_module, load_underscore = self._filterModule(module) + + if load_module or load_underscore: + return super(OETestLoader, self).loadTestsFromModule( + module, use_load_tests) + else: + return self.suiteClass() -- cgit 1.2.3-korg