loader.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import importlib
  2. import importlib.util
  3. import inspect
  4. import itertools
  5. import sys
  6. import threading
  7. import unittest
  8. from pathlib import Path
  9. from .. import tools
  10. from .tag_selector import TagsSelector
  11. from .suite import OdooSuite
  12. from .result import OdooTestResult
  13. def get_test_modules(module):
  14. """ Return a list of module for the addons potentially containing tests to
  15. feed unittest.TestLoader.loadTestsFromModule() """
  16. results = _get_tests_modules(importlib.util.find_spec(f'odoo.addons.{module}'))
  17. results += list(_get_upgrade_test_modules(module))
  18. return results
  19. def _get_tests_modules(mod):
  20. spec = importlib.util.find_spec('.tests', mod.name)
  21. if not spec:
  22. return []
  23. tests_mod = importlib.import_module(spec.name)
  24. return [
  25. mod_obj
  26. for name, mod_obj in inspect.getmembers(tests_mod, inspect.ismodule)
  27. if name.startswith('test_')
  28. ]
  29. def _get_upgrade_test_modules(module):
  30. upgrade_modules = (
  31. f"odoo.upgrade.{module}",
  32. f"odoo.addons.{module}.migrations",
  33. f"odoo.addons.{module}.upgrades",
  34. )
  35. for module_name in upgrade_modules:
  36. if not importlib.util.find_spec(module_name):
  37. continue
  38. upg = importlib.import_module(module_name)
  39. for path in map(Path, upg.__path__):
  40. for test in path.glob("tests/test_*.py"):
  41. spec = importlib.util.spec_from_file_location(f"{upg.__name__}.tests.{test.stem}", test)
  42. if not spec:
  43. continue
  44. pymod = importlib.util.module_from_spec(spec)
  45. sys.modules[spec.name] = pymod
  46. spec.loader.exec_module(pymod)
  47. yield pymod
  48. def make_suite(module_names, position='at_install'):
  49. """ Creates a test suite for all the tests in the specified modules,
  50. filtered by the provided ``position`` and the current test tags
  51. :param list[str] module_names: modules to load tests from
  52. :param str position: "at_install" or "post_install"
  53. """
  54. config_tags = TagsSelector(tools.config['test_tags'])
  55. position_tag = TagsSelector(position)
  56. tests = (
  57. t
  58. for module_name in module_names
  59. for m in get_test_modules(module_name)
  60. for t in unwrap_suite(unittest.TestLoader().loadTestsFromModule(m))
  61. if position_tag.check(t) and config_tags.check(t)
  62. )
  63. return OdooSuite(sorted(tests, key=lambda t: t.test_sequence))
  64. def run_suite(suite, module_name=None):
  65. # avoid dependency hell
  66. from ..modules import module
  67. module.current_test = module_name
  68. threading.current_thread().testing = True
  69. results = OdooTestResult()
  70. suite(results)
  71. threading.current_thread().testing = False
  72. module.current_test = None
  73. return results
  74. def unwrap_suite(test):
  75. """
  76. Attempts to unpack testsuites (holding suites or cases) in order to
  77. generate a single stream of terminals (either test cases or customized
  78. test suites). These can then be checked for run/skip attributes
  79. individually.
  80. An alternative would be to use a variant of @unittest.skipIf with a state
  81. flag of some sort e.g. @unittest.skipIf(common.runstate != 'at_install'),
  82. but then things become weird with post_install as tests should *not* run
  83. by default there
  84. """
  85. if isinstance(test, unittest.TestCase):
  86. yield test
  87. return
  88. subtests = list(test)
  89. ## custom test suite (no test cases)
  90. #if not len(subtests):
  91. # yield test
  92. # return
  93. for item in itertools.chain.from_iterable(unwrap_suite(t) for t in subtests):
  94. yield item