1#!/usr/bin/env python3 2import argparse 3import logging 4import os 5import sys 6import time 7 8 9sys.path.append(os.path.abspath(os.path.join(__file__,'../../../'))) 10 11import odoo 12from odoo.tools import topological_sort, unique 13from odoo.netsvc import init_logger 14from odoo.tests import standalone_tests 15import odoo.tests.loader 16 17_logger = logging.getLogger('odoo.tests.test_module_operations') 18 19BLACKLIST = { 20 'auth_ldap', 'document_ftp', 'website_instantclick', 'pad', 21 'pad_project', 'note_pad', 'pos_cache', 'pos_blackbox_be', 'payment_test', 22} 23IGNORE = ('hw_', 'theme_', 'l10n_', 'test_', 'payment_') 24 25 26def install(db_name, module_id, module_name): 27 with odoo.api.Environment.manage(): 28 with odoo.registry(db_name).cursor() as cr: 29 env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) 30 module = env['ir.module.module'].browse(module_id) 31 module.button_immediate_install() 32 _logger.info('%s installed', module_name) 33 34 35def uninstall(db_name, module_id, module_name): 36 with odoo.api.Environment.manage(): 37 with odoo.registry(db_name).cursor() as cr: 38 env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) 39 module = env['ir.module.module'].browse(module_id) 40 module.button_immediate_uninstall() 41 _logger.info('%s uninstalled', module_name) 42 43 44def cycle(db_name, module_id, module_name): 45 install(db_name, module_id, module_name) 46 uninstall(db_name, module_id, module_name) 47 install(db_name, module_id, module_name) 48 49 50def parse_args(): 51 parser = argparse.ArgumentParser( 52 description="Script for testing the install / uninstall / reinstall cycle of Odoo modules") 53 parser.add_argument("--database", "-d", type=str, required=True, 54 help="The database to test (note: must have only 'base' installed)") 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, 63 help="Comma-separated list of paths to directories containing extra Odoo modules") 64 parser.add_argument("--uninstall", "-U", type=str, 65 help="Comma-separated list of modules to uninstall/reinstall") 66 parser.add_argument("--standalone", type=str, 67 help="Launch standalone scripts tagged with @standalone. Accepts a list of " 68 "module names or tags separated by commas. 'all' will run all available scripts." 69 ) 70 return parser.parse_args() 71 72 73def test_full(args): 74 """ Test full install/uninstall/reinstall cycle for all modules """ 75 with odoo.api.Environment.manage(): 76 with odoo.registry(args.database).cursor() as cr: 77 env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) 78 79 def valid(module): 80 return not ( 81 module.name in BLACKLIST 82 or module.name.startswith(IGNORE) 83 or module.state in ('installed', 'uninstallable') 84 ) 85 86 modules = env['ir.module.module'].search([]).filtered(valid) 87 88 # order modules in topological order 89 modules = modules.browse(topological_sort({ 90 module.id: module.dependencies_id.depend_id.ids 91 for module in modules 92 })) 93 modules_todo = [(module.id, module.name) for module in modules] 94 95 resume = args.resume_at 96 skip = set(args.skip.split(',')) if args.skip else set() 97 for module_id, module_name in modules_todo: 98 if module_name == resume: 99 resume = None 100 101 if resume or module_name in skip: 102 install(args.database, module_id, module_name) 103 else: 104 cycle(args.database, module_id, module_name) 105 106 107def test_uninstall(args): 108 """ Tries to uninstall/reinstall one ore more modules""" 109 domain = [('name', 'in', args.uninstall.split(',')), ('state', '=', 'installed')] 110 with odoo.api.Environment.manage(): 111 with odoo.registry(args.database).cursor() as cr: 112 env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) 113 modules = env['ir.module.module'].search(domain) 114 modules_todo = [(module.id, module.name) for module in modules] 115 116 for module_id, module_name in modules_todo: 117 uninstall(args.database, module_id, module_name) 118 install(args.database, module_id, module_name) 119 120 121def test_scripts(args): 122 """ Tries to launch standalone scripts tagged with @post_testing """ 123 # load the registry once for script discovery 124 registry = odoo.registry(args.database) 125 for module_name in registry._init_modules: 126 # import tests for loaded modules 127 odoo.tests.loader.get_test_modules(module_name) 128 129 # fetch and filter scripts to test 130 funcs = list(unique( 131 func 132 for tag in args.standalone.split(',') 133 for func in standalone_tests[tag] 134 )) 135 136 start_time = time.time() 137 for index, func in enumerate(funcs, start=1): 138 with odoo.api.Environment.manage(): 139 with odoo.registry(args.database).cursor() as cr: 140 env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) 141 _logger.info("Executing standalone script: %s (%d / %d)", 142 func.__name__, index, len(funcs)) 143 try: 144 func(env) 145 except Exception: 146 _logger.error("Standalone script %s failed", func.__name__, exc_info=True) 147 148 _logger.info("%d standalone scripts executed in %.2fs" % (len(funcs), time.time() - start_time)) 149 150 151if __name__ == '__main__': 152 args = parse_args() 153 154 # handle paths option 155 if args.addons_path: 156 odoo.tools.config['addons_path'] = ','.join([args.addons_path, odoo.tools.config['addons_path']]) 157 if args.data_dir: 158 odoo.tools.config['data_dir'] = args.data_dir 159 odoo.modules.module.initialize_sys_path() 160 161 init_logger() 162 logging.getLogger('odoo.modules.loading').setLevel(logging.CRITICAL) 163 logging.getLogger('odoo.sql_db').setLevel(logging.CRITICAL) 164 165 if args.uninstall: 166 test_uninstall(args) 167 elif args.standalone: 168 test_scripts(args) 169 else: 170 test_full(args) 171