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