test_module_operations.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. #!/usr/bin/env python3
  2. import argparse
  3. import logging.config
  4. import os
  5. import sys
  6. import time
  7. sys.path.append(os.path.abspath(os.path.join(__file__,'../../../')))
  8. import odoo
  9. from odoo.tools import config, topological_sort, unique
  10. from odoo.netsvc import init_logger
  11. from odoo.tests import standalone_tests
  12. import odoo.tests.loader
  13. _logger = logging.getLogger('odoo.tests.test_module_operations')
  14. BLACKLIST = {
  15. 'auth_ldap', 'document_ftp', 'website_instantclick', 'pad',
  16. 'pad_project', 'note_pad', 'pos_cache', 'pos_blackbox_be',
  17. }
  18. IGNORE = ('hw_', 'theme_', 'l10n_', 'test_')
  19. INSTALL_BLACKLIST = {
  20. 'payment_alipay', 'payment_ogone', 'payment_payulatam', 'payment_payumoney',
  21. } # deprecated modules (cannot be installed manually through button_install anymore)
  22. def install(db_name, module_id, module_name):
  23. with odoo.registry(db_name).cursor() as cr:
  24. env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
  25. module = env['ir.module.module'].browse(module_id)
  26. module.button_immediate_install()
  27. _logger.info('%s installed', module_name)
  28. def uninstall(db_name, module_id, module_name):
  29. with odoo.registry(db_name).cursor() as cr:
  30. env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
  31. module = env['ir.module.module'].browse(module_id)
  32. module.button_immediate_uninstall()
  33. _logger.info('%s uninstalled', module_name)
  34. def cycle(db_name, module_id, module_name):
  35. install(db_name, module_id, module_name)
  36. uninstall(db_name, module_id, module_name)
  37. install(db_name, module_id, module_name)
  38. class CheckAddons(argparse.Action):
  39. def __call__(self, parser, namespace, values, option_string=None):
  40. self.values = namespace
  41. config._check_addons_path(self, option_string, values, self)
  42. def parse_args():
  43. parser = argparse.ArgumentParser(
  44. description="Script for testing the install / uninstall / reinstall"
  45. " cycle of Odoo modules. Prefer the 'cycle' subcommand to"
  46. " running this without anything specified (this is the"
  47. " default behaviour).")
  48. parser.set_defaults(
  49. func=test_cycle,
  50. reinstall=True,
  51. )
  52. fake_commands = parser.add_mutually_exclusive_group()
  53. parser.add_argument("--database", "-d", type=str, required=True,
  54. help="The database to test (/ run the command on)")
  55. parser.add_argument("--data-dir", "-D", dest="data_dir", type=str,
  56. help="Directory where to store Odoo data"
  57. )
  58. parser.add_argument("--skip", "-s", type=str,
  59. help="Comma-separated list of modules to skip (they will only be installed)")
  60. parser.add_argument("--resume-at", "-r", type=str,
  61. help="Skip modules (only install) up to the specified one in topological order")
  62. parser.add_argument("--addons-path", "-p", type=str, action=CheckAddons,
  63. help="Comma-separated list of paths to directories containing extra Odoo modules")
  64. cmds = parser.add_subparsers(title="subcommands", metavar='')
  65. cycle = cmds.add_parser(
  66. 'cycle', help="Full install/uninstall/reinstall cycle.",
  67. description="Installs, uninstalls, and reinstalls all modules which are"
  68. " not skipped or blacklisted, the database should have"
  69. " 'base' installed (only).")
  70. cycle.set_defaults(func=test_cycle)
  71. fake_commands.add_argument(
  72. "--uninstall", "-U", action=UninstallAction,
  73. help="Comma-separated list of modules to uninstall/reinstall. Prefer the 'uninstall' subcommand."
  74. )
  75. uninstall = cmds.add_parser(
  76. 'uninstall', help="Uninstallation",
  77. description="Uninstalls then (by default) reinstalls every specified "
  78. "module. Modules which are not installed before running "
  79. "are ignored.")
  80. uninstall.set_defaults(func=test_uninstall)
  81. uninstall.add_argument('uninstall', help="comma-separated list of modules to uninstall/reinstall")
  82. uninstall.add_argument(
  83. '-n', '--no-reinstall', dest='reinstall', action='store_false',
  84. help="Skips reinstalling the module(s) after uninstalling."
  85. )
  86. fake_commands.add_argument("--standalone", action=StandaloneAction,
  87. help="Launch standalone scripts tagged with @standalone. Accepts a list of "
  88. "module names or tags separated by commas. 'all' will run all available scripts. Prefer the 'standalone' subcommand."
  89. )
  90. standalone = cmds.add_parser('standalone', help="Run scripts tagged with @standalone")
  91. standalone.set_defaults(func=test_standalone)
  92. standalone.add_argument('standalone', help="List of module names or tags separated by commas, 'all' will run all available scripts.")
  93. return parser.parse_args()
  94. class UninstallAction(argparse.Action):
  95. def __call__(self, parser, namespace, values, option_string=None):
  96. namespace.func = test_uninstall
  97. setattr(namespace, self.dest, values)
  98. class StandaloneAction(argparse.Action):
  99. def __call__(self, parser, namespace, values, option_string=None):
  100. namespace.func = test_standalone
  101. setattr(namespace, self.dest, values)
  102. def test_cycle(args):
  103. """ Test full install/uninstall/reinstall cycle for all modules """
  104. with odoo.registry(args.database).cursor() as cr:
  105. env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
  106. def valid(module):
  107. return not (
  108. module.name in BLACKLIST
  109. or module.name in INSTALL_BLACKLIST
  110. or module.name.startswith(IGNORE)
  111. or module.state in ('installed', 'uninstallable')
  112. )
  113. modules = env['ir.module.module'].search([]).filtered(valid)
  114. # order modules in topological order
  115. modules = modules.browse(topological_sort({
  116. module.id: module.dependencies_id.depend_id.ids
  117. for module in modules
  118. }))
  119. modules_todo = [(module.id, module.name) for module in modules]
  120. resume = args.resume_at
  121. skip = set(args.skip.split(',')) if args.skip else set()
  122. for module_id, module_name in modules_todo:
  123. if module_name == resume:
  124. resume = None
  125. if resume or module_name in skip:
  126. install(args.database, module_id, module_name)
  127. else:
  128. cycle(args.database, module_id, module_name)
  129. def test_uninstall(args):
  130. """ Tries to uninstall/reinstall one ore more modules"""
  131. for module_name in args.uninstall.split(','):
  132. with odoo.registry(args.database).cursor() as cr:
  133. env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
  134. module = env['ir.module.module'].search([('name', '=', module_name)])
  135. module_id, module_state = module.id, module.state
  136. if module_state == 'installed':
  137. uninstall(args.database, module_id, module_name)
  138. if args.reinstall and module_name not in INSTALL_BLACKLIST:
  139. install(args.database, module_id, module_name)
  140. elif module_state:
  141. _logger.warning("Module %r is not installed", module_name)
  142. else:
  143. _logger.warning("Module %r does not exist", module_name)
  144. def test_standalone(args):
  145. """ Tries to launch standalone scripts tagged with @post_testing """
  146. # load the registry once for script discovery
  147. registry = odoo.registry(args.database)
  148. for module_name in registry._init_modules:
  149. # import tests for loaded modules
  150. odoo.tests.loader.get_test_modules(module_name)
  151. # fetch and filter scripts to test
  152. funcs = list(unique(
  153. func
  154. for tag in args.standalone.split(',')
  155. for func in standalone_tests[tag]
  156. ))
  157. start_time = time.time()
  158. for index, func in enumerate(funcs, start=1):
  159. with odoo.registry(args.database).cursor() as cr:
  160. env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
  161. _logger.info("Executing standalone script: %s (%d / %d)",
  162. func.__name__, index, len(funcs))
  163. try:
  164. func(env)
  165. except Exception:
  166. _logger.error("Standalone script %s failed", func.__name__, exc_info=True)
  167. _logger.info("%d standalone scripts executed in %.2fs" % (len(funcs), time.time() - start_time))
  168. if __name__ == '__main__':
  169. args = parse_args()
  170. # handle paths option
  171. if args.addons_path:
  172. odoo.tools.config['addons_path'] = ','.join([args.addons_path, odoo.tools.config['addons_path']])
  173. if args.data_dir:
  174. odoo.tools.config['data_dir'] = args.data_dir
  175. odoo.modules.module.initialize_sys_path()
  176. init_logger()
  177. logging.config.dictConfig({
  178. 'version': 1,
  179. 'incremental': True,
  180. 'disable_existing_loggers': False,
  181. 'loggers': {
  182. 'odoo.modules.loading': {'level': 'CRITICAL'},
  183. 'odoo.sql_db': {'level': 'CRITICAL'},
  184. 'odoo.models.unlink': {'level': 'WARNING'},
  185. 'odoo.addons.base.models.ir_model': {'level': "WARNING"},
  186. }
  187. })
  188. try:
  189. args.func(args)
  190. except Exception:
  191. _logger.error("%s tests failed", args.func.__name__[5:])
  192. raise