diff options
| author | Xavier Morel <xmo@odoo.com> | 2020-07-27 12:09:50 +0000 |
|---|---|---|
| committer | Xavier Morel <xmo@odoo.com> | 2020-08-19 07:28:44 +0000 |
| commit | ec8a64a85c7ce751bbd4fc3b5a2a8224a84c81d0 (patch) | |
| tree | 0bc5a7b4668688c2ea7d007137ee2705fce19d82 /odoo/tests/loader.py | |
| parent | e46a8bc7d95501569fd95c499b84a7027a3f2831 (diff) | |
[REF] core: move testing-related functions to odoo/tests submodules
Attempts to clean up odoo/module and odoo/service a tad, they still
invoke testing-related utilities but are more logical in what
they *contain*.
Diffstat (limited to 'odoo/tests/loader.py')
| -rw-r--r-- | odoo/tests/loader.py | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/odoo/tests/loader.py b/odoo/tests/loader.py new file mode 100644 index 00000000000..c2cc53452fa --- /dev/null +++ b/odoo/tests/loader.py @@ -0,0 +1,122 @@ +import importlib +import inspect +import itertools +import logging +import threading +import time +import unittest + +import odoo +from .. import tools +from .common import TagsSelector, OdooSuite +from .runner import OdooTestRunner + +# backwards compatibility +_logger = logging.getLogger('odoo.modules.module') + +def get_test_modules(module): + """ Return a list of module for the addons potentially containing tests to + feed unittest.TestLoader.loadTestsFromModule() """ + # Try to import the module + results = _get_tests_modules('odoo.addons', module) + + try: + importlib.import_module('odoo.upgrade.%s' % module) + except ImportError: + pass + else: + results += _get_tests_modules('odoo.upgrade', module) + + return results + + +def _get_tests_modules(path, module): + modpath = '%s.%s' % (path, module) + try: + mod = importlib.import_module('.tests', modpath) + except ImportError as e: # will also catch subclass ModuleNotFoundError of P3.6 + # Hide ImportErrors on `tests` sub-module, but display other exceptions + if e.name == modpath + '.tests' and e.msg.startswith('No module named'): + return [] + _logger.exception('Can not `import %s`.', module) + return [] + except Exception as e: + _logger.exception('Can not `import %s`.', module) + return [] + if hasattr(mod, 'fast_suite') or hasattr(mod, 'checks'): + _logger.warning( + "Found deprecated fast_suite or checks attribute in test module " + "%s. These have no effect in or after version 8.0.", + mod.__name__) + + result = [mod_obj for name, mod_obj in inspect.getmembers(mod, inspect.ismodule) + if name.startswith('test_')] + return result + + +def run_unit_tests(module_name, position='at_install'): + """ + :returns: ``True`` if all of ``module_name``'s tests succeeded, ``False`` + if any of them failed, ``None`` if no tests were ran. + :rtype: bool | None + """ + from ..modules import module + # avoid dependency hell + module.current_test = module_name + mods = get_test_modules(module_name) + threading.currentThread().testing = True + config_tags = TagsSelector(tools.config['test_tags']) + position_tag = TagsSelector(position) + ran_tests = failures = False + for m in mods: + tests = unwrap_suite(unittest.TestLoader().loadTestsFromModule(m)) + suite = OdooSuite(t for t in tests if position_tag.check(t) and config_tags.check(t)) + + if suite.countTestCases(): + t0 = time.time() + t0_sql = odoo.sql_db.sql_counter + _logger.info('%s running tests.', m.__name__) + result = OdooTestRunner().run(suite) + log_level = logging.INFO + if time.time() - t0 > 5: + log_level = logging.RUNBOT + _logger.log(log_level, "%s ran %s tests in %.2fs, %s queries", m.__name__, result.testsRun, time.time() - t0, odoo.sql_db.sql_counter - t0_sql) + ran_tests = True + if not result.wasSuccessful(): + failures = True + _logger.error("Module %s: %d failures, %d errors", module_name, len(result.failures), len(result.errors)) + + module.current_test = None + threading.currentThread().testing = False + + if failures: + return False + if ran_tests: + return True + return None + + +def unwrap_suite(test): + """ + Attempts to unpack testsuites (holding suites or cases) in order to + generate a single stream of terminals (either test cases or customized + test suites). These can then be checked for run/skip attributes + individually. + + An alternative would be to use a variant of @unittest.skipIf with a state + flag of some sort e.g. @unittest.skipIf(common.runstate != 'at_install'), + but then things become weird with post_install as tests should *not* run + by default there + """ + if isinstance(test, unittest.TestCase): + yield test + return + + subtests = list(test) + # custom test suite (no test cases) + if not len(subtests): + yield test + return + + for item in itertools.chain.from_iterable(unwrap_suite(t) for t in subtests): + yield item |
