[IMP] module_auto_update: black, isort, prettier

pull/1832/head
Eric Antones 2020-05-18 13:12:59 +02:00
parent e93659fb03
commit 8592e0e285
10 changed files with 184 additions and 199 deletions

View File

@ -3,24 +3,20 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
{ {
'name': 'Module Auto Update', "name": "Module Auto Update",
'summary': 'Automatically update Odoo modules', "summary": "Automatically update Odoo modules",
'version': '12.0.2.0.5', "version": "12.0.2.0.5",
'category': 'Extra Tools', "category": "Extra Tools",
'website': 'https://github.com/OCA/server-tools', "website": "https://github.com/OCA/server-tools",
'author': 'LasLabs, ' "author": "LasLabs, "
'Juan José Scarafía, ' "Juan José Scarafía, "
'Tecnativa, ' "Tecnativa, "
'ACSONE SA/NV, ' "ACSONE SA/NV, "
'Odoo Community Association (OCA)', "Odoo Community Association (OCA)",
'license': 'LGPL-3', "license": "LGPL-3",
'installable': True, "installable": True,
'uninstall_hook': 'uninstall_hook', "uninstall_hook": "uninstall_hook",
'depends': [ "depends": ["base"],
'base', "data": ["views/ir_module_module.xml"],
], "development_status": "Production/Stable",
'data': [
'views/ir_module_module.xml',
],
'development_status': 'Production/Stable',
} }

View File

@ -1,9 +1,9 @@
# Copyright 2018 ACSONE SA/NV. # Copyright 2018 ACSONE SA/NV.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
from fnmatch import fnmatch
import hashlib import hashlib
import os import os
from fnmatch import fnmatch
def _fnmatch(filename, patterns): def _fnmatch(filename, patterns):
@ -14,20 +14,20 @@ def _fnmatch(filename, patterns):
def _walk(top, exclude_patterns, keep_langs): def _walk(top, exclude_patterns, keep_langs):
keep_langs = {l.split('_')[0] for l in keep_langs} keep_langs = {l.split("_")[0] for l in keep_langs}
for dirpath, dirnames, filenames in os.walk(top): for dirpath, dirnames, filenames in os.walk(top):
dirnames.sort() dirnames.sort()
reldir = os.path.relpath(dirpath, top) reldir = os.path.relpath(dirpath, top)
if reldir == '.': if reldir == ".":
reldir = '' reldir = ""
for filename in sorted(filenames): for filename in sorted(filenames):
filepath = os.path.join(reldir, filename) filepath = os.path.join(reldir, filename)
if _fnmatch(filepath, exclude_patterns): if _fnmatch(filepath, exclude_patterns):
continue continue
if keep_langs and reldir in {'i18n', 'i18n_extra'}: if keep_langs and reldir in {"i18n", "i18n_extra"}:
basename, ext = os.path.splitext(filename) basename, ext = os.path.splitext(filename)
if ext == '.po': if ext == ".po":
if basename.split('_')[0] not in keep_langs: if basename.split("_")[0] not in keep_langs:
continue continue
yield filepath yield filepath
@ -37,8 +37,8 @@ def addon_hash(top, exclude_patterns, keep_langs):
m = hashlib.sha1() m = hashlib.sha1()
for filepath in _walk(top, exclude_patterns, keep_langs): for filepath in _walk(top, exclude_patterns, keep_langs):
# hash filename so empty files influence the hash # hash filename so empty files influence the hash
m.update(filepath.encode('utf-8')) m.update(filepath.encode("utf-8"))
# hash file content # hash file content
with open(os.path.join(top, filepath), 'rb') as f: with open(os.path.join(top, filepath), "rb") as f:
m.update(f.read()) m.update(f.read())
return m.hexdigest() return m.hexdigest()

View File

@ -6,7 +6,5 @@ from openupgradelib import openupgrade
@openupgrade.migrate() @openupgrade.migrate()
def migrate(env, version): def migrate(env, version):
openupgrade.delete_records_safely_by_xml_id( openupgrade.delete_records_safely_by_xml_id(
env, [ env, ["module_auto_update.module_check_upgrades_cron"],
'module_auto_update.module_check_upgrades_cron',
],
) )

View File

@ -11,12 +11,9 @@ from odoo.modules.module import get_module_path
from ..addon_hash import addon_hash from ..addon_hash import addon_hash
PARAM_INSTALLED_CHECKSUMS = \ PARAM_INSTALLED_CHECKSUMS = "module_auto_update.installed_checksums"
'module_auto_update.installed_checksums' PARAM_EXCLUDE_PATTERNS = "module_auto_update.exclude_patterns"
PARAM_EXCLUDE_PATTERNS = \ DEFAULT_EXCLUDE_PATTERNS = "*.pyc,*.pyo,i18n/*.pot,i18n_extra/*.pot,static/*"
'module_auto_update.exclude_patterns'
DEFAULT_EXCLUDE_PATTERNS = \
'*.pyc,*.pyo,i18n/*.pot,i18n_extra/*.pot,static/*'
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -34,40 +31,33 @@ def ensure_module_state(env, modules, state):
if not modules: if not modules:
return return
env.cr.execute( env.cr.execute(
"SELECT name FROM ir_module_module " "SELECT name FROM ir_module_module " "WHERE id IN %s AND state != %s",
"WHERE id IN %s AND state != %s",
(tuple(modules.ids), state), (tuple(modules.ids), state),
) )
names = [r[0] for r in env.cr.fetchall()] names = [r[0] for r in env.cr.fetchall()]
if names: if names:
raise FailedUpgradeError( raise FailedUpgradeError(
"The following modules should be in state '%s' " "The following modules should be in state '%s' "
"at this stage: %s. Bailing out for safety." % "at this stage: %s. Bailing out for safety." % (state, ",".join(names),),
(state, ','.join(names), ),
) )
class Module(models.Model): class Module(models.Model):
_inherit = 'ir.module.module' _inherit = "ir.module.module"
@api.multi @api.multi
def _get_checksum_dir(self): def _get_checksum_dir(self):
self.ensure_one() self.ensure_one()
exclude_patterns = self.env["ir.config_parameter"].get_param( exclude_patterns = self.env["ir.config_parameter"].get_param(
PARAM_EXCLUDE_PATTERNS, PARAM_EXCLUDE_PATTERNS, DEFAULT_EXCLUDE_PATTERNS,
DEFAULT_EXCLUDE_PATTERNS,
) )
exclude_patterns = [p.strip() for p in exclude_patterns.split(',')] exclude_patterns = [p.strip() for p in exclude_patterns.split(",")]
keep_langs = self.env['res.lang'].search([]).mapped('code') keep_langs = self.env["res.lang"].search([]).mapped("code")
module_path = get_module_path(self.name) module_path = get_module_path(self.name)
if module_path and os.path.isdir(module_path): if module_path and os.path.isdir(module_path):
checksum_dir = addon_hash( checksum_dir = addon_hash(module_path, exclude_patterns, keep_langs,)
module_path,
exclude_patterns,
keep_langs,
)
else: else:
checksum_dir = False checksum_dir = False
@ -75,32 +65,30 @@ class Module(models.Model):
@api.model @api.model
def _get_saved_checksums(self): def _get_saved_checksums(self):
Icp = self.env['ir.config_parameter'] Icp = self.env["ir.config_parameter"]
return json.loads(Icp.get_param(PARAM_INSTALLED_CHECKSUMS, '{}')) return json.loads(Icp.get_param(PARAM_INSTALLED_CHECKSUMS, "{}"))
@api.model @api.model
def _save_checksums(self, checksums): def _save_checksums(self, checksums):
Icp = self.env['ir.config_parameter'] Icp = self.env["ir.config_parameter"]
Icp.set_param(PARAM_INSTALLED_CHECKSUMS, json.dumps(checksums)) Icp.set_param(PARAM_INSTALLED_CHECKSUMS, json.dumps(checksums))
@api.model @api.model
def _save_installed_checksums(self): def _save_installed_checksums(self):
checksums = {} checksums = {}
installed_modules = self.search([('state', '=', 'installed')]) installed_modules = self.search([("state", "=", "installed")])
for module in installed_modules: for module in installed_modules:
checksums[module.name] = module._get_checksum_dir() checksums[module.name] = module._get_checksum_dir()
self._save_checksums(checksums) self._save_checksums(checksums)
@api.model @api.model
def _get_modules_partially_installed(self): def _get_modules_partially_installed(self):
return self.search([ return self.search([("state", "in", ("to install", "to remove", "to upgrade"))])
('state', 'in', ('to install', 'to remove', 'to upgrade')),
])
@api.model @api.model
def _get_modules_with_changed_checksum(self): def _get_modules_with_changed_checksum(self):
saved_checksums = self._get_saved_checksums() saved_checksums = self._get_saved_checksums()
installed_modules = self.search([('state', '=', 'installed')]) installed_modules = self.search([("state", "=", "installed")])
return installed_modules.filtered( return installed_modules.filtered(
lambda r: r._get_checksum_dir() != saved_checksums.get(r.name), lambda r: r._get_checksum_dir() != saved_checksums.get(r.name),
) )
@ -131,32 +119,37 @@ class Module(models.Model):
""" """
_logger.info( _logger.info(
"Checksum upgrade starting (i18n-overwrite=%s)...", "Checksum upgrade starting (i18n-overwrite=%s)...",
overwrite_existing_translations overwrite_existing_translations,
) )
tools.config['overwrite_existing_translations'] = \ tools.config[
overwrite_existing_translations "overwrite_existing_translations"
] = overwrite_existing_translations
_logger.info("Updating modules list...") _logger.info("Updating modules list...")
self.update_list() self.update_list()
changed_modules = self._get_modules_with_changed_checksum() changed_modules = self._get_modules_with_changed_checksum()
if not changed_modules and not self._get_modules_partially_installed(): if not changed_modules and not self._get_modules_partially_installed():
_logger.info("No checksum change detected in installed modules " _logger.info(
"and all modules installed, nothing to do.") "No checksum change detected in installed modules "
"and all modules installed, nothing to do."
)
return return
_logger.info("Marking the following modules to upgrade, " _logger.info(
"for their checksums changed: %s...", "Marking the following modules to upgrade, "
','.join(changed_modules.mapped('name'))) "for their checksums changed: %s...",
",".join(changed_modules.mapped("name")),
)
changed_modules.button_upgrade() changed_modules.button_upgrade()
self.env.cr.commit() # pylint: disable=invalid-commit self.env.cr.commit() # pylint: disable=invalid-commit
# in rare situations, button_upgrade may fail without # in rare situations, button_upgrade may fail without
# exception, this would lead to corruption because # exception, this would lead to corruption because
# no upgrade would be performed and save_installed_checksums # no upgrade would be performed and save_installed_checksums
# would update cheksums for modules that have not been upgraded # would update cheksums for modules that have not been upgraded
ensure_module_state(self.env, changed_modules, 'to upgrade') ensure_module_state(self.env, changed_modules, "to upgrade")
_logger.info("Upgrading...") _logger.info("Upgrading...")
self.env['base.module.upgrade'].upgrade_module() self.env["base.module.upgrade"].upgrade_module()
self.env.cr.commit() # pylint: disable=invalid-commit self.env.cr.commit() # pylint: disable=invalid-commit
_logger.info("Upgrade successful, updating checksums...") _logger.info("Upgrade successful, updating checksums...")
@ -167,8 +160,8 @@ class Module(models.Model):
if partial_modules: if partial_modules:
raise IncompleteUpgradeError( raise IncompleteUpgradeError(
"Checksum upgrade successful " "Checksum upgrade successful "
"but incomplete for the following modules: %s" % "but incomplete for the following modules: %s"
','.join(partial_modules.mapped('name')) % ",".join(partial_modules.mapped("name"))
) )
_logger.info("Checksum upgrade complete.") _logger.info("Checksum upgrade complete.")

View File

@ -1 +1 @@
<odoo/> <odoo />

View File

@ -1 +1 @@
<odoo/> <odoo />

View File

@ -1 +1 @@
1+1 _ = 1 + 1

View File

@ -9,59 +9,63 @@ from ..models.module import DEFAULT_EXCLUDE_PATTERNS
class TestAddonHash(unittest.TestCase): class TestAddonHash(unittest.TestCase):
def setUp(self): def setUp(self):
super(TestAddonHash, self).setUp() super(TestAddonHash, self).setUp()
self.sample_dir = os.path.join( self.sample_dir = os.path.join(os.path.dirname(__file__), "sample_module",)
os.path.dirname(__file__),
'sample_module',
)
def test_basic(self): def test_basic(self):
files = list(addon_hash._walk( files = list(
self.sample_dir, addon_hash._walk(
exclude_patterns=["*/__pycache__/*"], self.sample_dir, exclude_patterns=["*/__pycache__/*"], keep_langs=[],
keep_langs=[], )
)) )
self.assertEqual(files, [ self.assertEqual(
'README.rst', files,
'data/f1.xml', [
'data/f2.xml', "README.rst",
'i18n/en.po', "data/f1.xml",
'i18n/en_US.po', "data/f2.xml",
'i18n/fr.po', "i18n/en.po",
'i18n/fr_BE.po', "i18n/en_US.po",
'i18n/test.pot', "i18n/fr.po",
'i18n_extra/en.po', "i18n/fr_BE.po",
'i18n_extra/fr.po', "i18n/test.pot",
'i18n_extra/nl_NL.po', "i18n_extra/en.po",
'models/stuff.py', "i18n_extra/fr.po",
'models/stuff.pyc', "i18n_extra/nl_NL.po",
'models/stuff.pyo', "models/stuff.py",
'static/src/some.js', "models/stuff.pyc",
]) "models/stuff.pyo",
"static/src/some.js",
],
)
def test_exclude(self): def test_exclude(self):
files = list(addon_hash._walk( files = list(
self.sample_dir, addon_hash._walk(
exclude_patterns=DEFAULT_EXCLUDE_PATTERNS.split(','), self.sample_dir,
keep_langs=['fr_FR', 'nl'], exclude_patterns=DEFAULT_EXCLUDE_PATTERNS.split(","),
)) keep_langs=["fr_FR", "nl"],
self.assertEqual(files, [ )
'README.rst', )
'data/f1.xml', self.assertEqual(
'data/f2.xml', files,
'i18n/fr.po', [
'i18n/fr_BE.po', "README.rst",
'i18n_extra/fr.po', "data/f1.xml",
'i18n_extra/nl_NL.po', "data/f2.xml",
'models/stuff.py', "i18n/fr.po",
]) "i18n/fr_BE.po",
"i18n_extra/fr.po",
"i18n_extra/nl_NL.po",
"models/stuff.py",
],
)
def test2(self): def test2(self):
checksum = addon_hash.addon_hash( checksum = addon_hash.addon_hash(
self.sample_dir, self.sample_dir,
exclude_patterns=['*.pyc', '*.pyo', '*.pot', 'static/*'], exclude_patterns=["*.pyc", "*.pyo", "*.pot", "static/*"],
keep_langs=['fr_FR', 'nl'], keep_langs=["fr_FR", "nl"],
) )
self.assertEqual(checksum, 'fecb89486c8a29d1f760cbd01c1950f6e8421b14') self.assertEqual(checksum, "fecb89486c8a29d1f760cbd01c1950f6e8421b14")

View File

@ -12,23 +12,22 @@ from odoo.modules import get_module_path
from odoo.tests import TransactionCase from odoo.tests import TransactionCase
from ..addon_hash import addon_hash from ..addon_hash import addon_hash
from ..models.module import IncompleteUpgradeError, DEFAULT_EXCLUDE_PATTERNS from ..models.module import DEFAULT_EXCLUDE_PATTERNS, IncompleteUpgradeError
MODULE_NAME = 'module_auto_update' MODULE_NAME = "module_auto_update"
class TestModule(TransactionCase): class TestModule(TransactionCase):
def setUp(self): def setUp(self):
super(TestModule, self).setUp() super(TestModule, self).setUp()
self.own_module = self.env['ir.module.module'].search([ self.own_module = self.env["ir.module.module"].search(
('name', '=', MODULE_NAME), [("name", "=", MODULE_NAME)]
]) )
self.own_dir_path = get_module_path(MODULE_NAME) self.own_dir_path = get_module_path(MODULE_NAME)
keep_langs = self.env['res.lang'].search([]).mapped('code') keep_langs = self.env["res.lang"].search([]).mapped("code")
self.own_checksum = addon_hash( self.own_checksum = addon_hash(
self.own_dir_path, self.own_dir_path,
exclude_patterns=DEFAULT_EXCLUDE_PATTERNS.split(','), exclude_patterns=DEFAULT_EXCLUDE_PATTERNS.split(","),
keep_langs=keep_langs, keep_langs=keep_langs,
) )
self.own_writeable = os.access(self.own_dir_path, os.W_OK) self.own_writeable = os.access(self.own_dir_path, os.W_OK)
@ -36,8 +35,9 @@ class TestModule(TransactionCase):
def test_compute_checksum_dir(self): def test_compute_checksum_dir(self):
"""It should compute the directory's SHA-1 hash""" """It should compute the directory's SHA-1 hash"""
self.assertEqual( self.assertEqual(
self.own_module._get_checksum_dir(), self.own_checksum, self.own_module._get_checksum_dir(),
'Module directory checksum not computed properly', self.own_checksum,
"Module directory checksum not computed properly",
) )
def test_compute_checksum_dir_ignore_excluded(self): def test_compute_checksum_dir_ignore_excluded(self):
@ -45,10 +45,11 @@ class TestModule(TransactionCase):
calculations""" calculations"""
if not self.own_writeable: if not self.own_writeable:
self.skipTest("Own directory not writeable") self.skipTest("Own directory not writeable")
with tempfile.NamedTemporaryFile(suffix='.pyc', dir=self.own_dir_path): with tempfile.NamedTemporaryFile(suffix=".pyc", dir=self.own_dir_path):
self.assertEqual( self.assertEqual(
self.own_module._get_checksum_dir(), self.own_checksum, self.own_module._get_checksum_dir(),
'SHA1 checksum does not ignore excluded extensions', self.own_checksum,
"SHA1 checksum does not ignore excluded extensions",
) )
def test_compute_checksum_dir_recomputes_when_file_added(self): def test_compute_checksum_dir_recomputes_when_file_added(self):
@ -56,62 +57,59 @@ class TestModule(TransactionCase):
added to the module directory""" added to the module directory"""
if not self.own_writeable: if not self.own_writeable:
self.skipTest("Own directory not writeable") self.skipTest("Own directory not writeable")
with tempfile.NamedTemporaryFile(suffix='.py', dir=self.own_dir_path): with tempfile.NamedTemporaryFile(suffix=".py", dir=self.own_dir_path):
self.assertNotEqual( self.assertNotEqual(
self.own_module._get_checksum_dir(), self.own_checksum, self.own_module._get_checksum_dir(),
'SHA1 checksum not recomputed', self.own_checksum,
"SHA1 checksum not recomputed",
) )
def test_saved_checksums(self): def test_saved_checksums(self):
Imm = self.env['ir.module.module'] Imm = self.env["ir.module.module"]
base_module = Imm.search([('name', '=', 'base')]) base_module = Imm.search([("name", "=", "base")])
self.assertEqual(base_module.state, 'installed') self.assertEqual(base_module.state, "installed")
self.assertFalse(Imm._get_saved_checksums()) self.assertFalse(Imm._get_saved_checksums())
Imm._save_installed_checksums() Imm._save_installed_checksums()
saved_checksums = Imm._get_saved_checksums() saved_checksums = Imm._get_saved_checksums()
self.assertTrue(saved_checksums) self.assertTrue(saved_checksums)
self.assertTrue(saved_checksums['base']) self.assertTrue(saved_checksums["base"])
def test_get_modules_with_changed_checksum(self): def test_get_modules_with_changed_checksum(self):
Imm = self.env['ir.module.module'] Imm = self.env["ir.module.module"]
self.assertTrue(Imm._get_modules_with_changed_checksum()) self.assertTrue(Imm._get_modules_with_changed_checksum())
Imm._save_installed_checksums() Imm._save_installed_checksums()
self.assertFalse(Imm._get_modules_with_changed_checksum()) self.assertFalse(Imm._get_modules_with_changed_checksum())
@odoo.tests.tagged('post_install', '-at_install') @odoo.tests.tagged("post_install", "-at_install")
class TestModuleAfterInstall(TransactionCase): class TestModuleAfterInstall(TransactionCase):
def setUp(self): def setUp(self):
super(TestModuleAfterInstall, self).setUp() super(TestModuleAfterInstall, self).setUp()
Imm = self.env['ir.module.module'] Imm = self.env["ir.module.module"]
self.own_module = Imm.search([('name', '=', MODULE_NAME)]) self.own_module = Imm.search([("name", "=", MODULE_NAME)])
self.base_module = Imm.search([('name', '=', 'base')]) self.base_module = Imm.search([("name", "=", "base")])
def test_get_modules_partially_installed(self): def test_get_modules_partially_installed(self):
Imm = self.env['ir.module.module'] Imm = self.env["ir.module.module"]
self.assertTrue( self.assertTrue(self.own_module not in Imm._get_modules_partially_installed())
self.own_module not in Imm._get_modules_partially_installed())
self.own_module.button_upgrade() self.own_module.button_upgrade()
self.assertTrue( self.assertTrue(self.own_module in Imm._get_modules_partially_installed())
self.own_module in Imm._get_modules_partially_installed())
self.own_module.button_upgrade_cancel() self.own_module.button_upgrade_cancel()
self.assertTrue( self.assertTrue(self.own_module not in Imm._get_modules_partially_installed())
self.own_module not in Imm._get_modules_partially_installed())
def test_upgrade_changed_checksum(self): def test_upgrade_changed_checksum(self):
Imm = self.env['ir.module.module'] Imm = self.env["ir.module.module"]
Bmu = self.env['base.module.upgrade'] Bmu = self.env["base.module.upgrade"]
# check modules are in installed state # check modules are in installed state
installed_modules = Imm.search([('state', '=', 'installed')]) installed_modules = Imm.search([("state", "=", "installed")])
self.assertTrue(self.own_module in installed_modules) self.assertTrue(self.own_module in installed_modules)
self.assertTrue(self.base_module in installed_modules) self.assertTrue(self.base_module in installed_modules)
self.assertTrue(len(installed_modules) > 2) self.assertTrue(len(installed_modules) > 2)
# change the checksum of 'base' # change the checksum of 'base'
Imm._save_installed_checksums() Imm._save_installed_checksums()
saved_checksums = Imm._get_saved_checksums() saved_checksums = Imm._get_saved_checksums()
saved_checksums['base'] = False saved_checksums["base"] = False
Imm._save_checksums(saved_checksums) Imm._save_checksums(saved_checksums)
changed_modules = Imm._get_modules_with_changed_checksum() changed_modules = Imm._get_modules_with_changed_checksum()
self.assertEqual(len(changed_modules), 1) self.assertEqual(len(changed_modules), 1)
@ -121,102 +119,100 @@ class TestModuleAfterInstall(TransactionCase):
upgrade_module_mock.call_count += 1 upgrade_module_mock.call_count += 1
# since we are upgrading base, all installed module # since we are upgrading base, all installed module
# must have been marked to upgrade at this stage # must have been marked to upgrade at this stage
self.assertEqual(self.base_module.state, 'to upgrade') self.assertEqual(self.base_module.state, "to upgrade")
self.assertEqual(self.own_module.state, 'to upgrade') self.assertEqual(self.own_module.state, "to upgrade")
installed_modules.write({'state': 'installed'}) installed_modules.write({"state": "installed"})
upgrade_module_mock.call_count = 0 upgrade_module_mock.call_count = 0
# upgrade_changed_checksum commits, so mock that # upgrade_changed_checksum commits, so mock that
with mock.patch.object(self.env.cr, 'commit'): with mock.patch.object(self.env.cr, "commit"):
# we simulate an install by setting module states # we simulate an install by setting module states
Bmu._patch_method('upgrade_module', upgrade_module_mock) Bmu._patch_method("upgrade_module", upgrade_module_mock)
try: try:
Imm.upgrade_changed_checksum() Imm.upgrade_changed_checksum()
self.assertEqual(upgrade_module_mock.call_count, 1) self.assertEqual(upgrade_module_mock.call_count, 1)
self.assertEqual(self.base_module.state, 'installed') self.assertEqual(self.base_module.state, "installed")
self.assertEqual(self.own_module.state, 'installed') self.assertEqual(self.own_module.state, "installed")
saved_checksums = Imm._get_saved_checksums() saved_checksums = Imm._get_saved_checksums()
self.assertTrue(saved_checksums['base']) self.assertTrue(saved_checksums["base"])
self.assertTrue(saved_checksums[MODULE_NAME]) self.assertTrue(saved_checksums[MODULE_NAME])
finally: finally:
Bmu._revert_method('upgrade_module') Bmu._revert_method("upgrade_module")
def test_incomplete_upgrade(self): def test_incomplete_upgrade(self):
Imm = self.env['ir.module.module'] Imm = self.env["ir.module.module"]
Bmu = self.env['base.module.upgrade'] Bmu = self.env["base.module.upgrade"]
installed_modules = Imm.search([('state', '=', 'installed')]) installed_modules = Imm.search([("state", "=", "installed")])
# change the checksum of 'base' # change the checksum of 'base'
Imm._save_installed_checksums() Imm._save_installed_checksums()
saved_checksums = Imm._get_saved_checksums() saved_checksums = Imm._get_saved_checksums()
saved_checksums['base'] = False saved_checksums["base"] = False
Imm._save_checksums(saved_checksums) Imm._save_checksums(saved_checksums)
def upgrade_module_mock(self_model): def upgrade_module_mock(self_model):
upgrade_module_mock.call_count += 1 upgrade_module_mock.call_count += 1
# since we are upgrading base, all installed module # since we are upgrading base, all installed module
# must have been marked to upgrade at this stage # must have been marked to upgrade at this stage
self.assertEqual(self.base_module.state, 'to upgrade') self.assertEqual(self.base_module.state, "to upgrade")
self.assertEqual(self.own_module.state, 'to upgrade') self.assertEqual(self.own_module.state, "to upgrade")
installed_modules.write({'state': 'installed'}) installed_modules.write({"state": "installed"})
# simulate partial upgrade # simulate partial upgrade
self.own_module.write({'state': 'to upgrade'}) self.own_module.write({"state": "to upgrade"})
upgrade_module_mock.call_count = 0 upgrade_module_mock.call_count = 0
# upgrade_changed_checksum commits, so mock that # upgrade_changed_checksum commits, so mock that
with mock.patch.object(self.env.cr, 'commit'): with mock.patch.object(self.env.cr, "commit"):
# we simulate an install by setting module states # we simulate an install by setting module states
Bmu._patch_method('upgrade_module', upgrade_module_mock) Bmu._patch_method("upgrade_module", upgrade_module_mock)
try: try:
with self.assertRaises(IncompleteUpgradeError): with self.assertRaises(IncompleteUpgradeError):
Imm.upgrade_changed_checksum() Imm.upgrade_changed_checksum()
self.assertEqual(upgrade_module_mock.call_count, 1) self.assertEqual(upgrade_module_mock.call_count, 1)
finally: finally:
Bmu._revert_method('upgrade_module') Bmu._revert_method("upgrade_module")
def test_incomplete_upgrade_no_checkusm(self): def test_incomplete_upgrade_no_checkusm(self):
Imm = self.env['ir.module.module'] Imm = self.env["ir.module.module"]
Bmu = self.env['base.module.upgrade'] Bmu = self.env["base.module.upgrade"]
installed_modules = Imm.search( installed_modules = Imm.search([("state", "=", "installed")])
[('state', '=', 'installed')])
# change the checksum of 'base' # change the checksum of 'base'
Imm._save_installed_checksums() Imm._save_installed_checksums()
saved_checksums = Imm._get_saved_checksums() saved_checksums = Imm._get_saved_checksums()
Imm._save_checksums(saved_checksums) Imm._save_checksums(saved_checksums)
self.base_module.write({'state': 'to upgrade'}) self.base_module.write({"state": "to upgrade"})
def upgrade_module_mock(self_model): def upgrade_module_mock(self_model):
upgrade_module_mock.call_count += 1 upgrade_module_mock.call_count += 1
# since we are upgrading base, all installed module # since we are upgrading base, all installed module
# must have been marked to upgrade at this stage # must have been marked to upgrade at this stage
self.assertEqual(self.base_module.state, 'to upgrade') self.assertEqual(self.base_module.state, "to upgrade")
self.assertEqual(self.own_module.state, 'installed') self.assertEqual(self.own_module.state, "installed")
installed_modules.write({'state': 'installed'}) installed_modules.write({"state": "installed"})
upgrade_module_mock.call_count = 0 upgrade_module_mock.call_count = 0
# upgrade_changed_checksum commits, so mock that # upgrade_changed_checksum commits, so mock that
with mock.patch.object(self.env.cr, 'commit'): with mock.patch.object(self.env.cr, "commit"):
# we simulate an install by setting module states # we simulate an install by setting module states
Bmu._patch_method('upgrade_module', Bmu._patch_method("upgrade_module", upgrade_module_mock)
upgrade_module_mock)
# got just other modules to_upgrade and no checksum ones # got just other modules to_upgrade and no checksum ones
try: try:
Imm.upgrade_changed_checksum() Imm.upgrade_changed_checksum()
self.assertEqual(upgrade_module_mock.call_count, 1) self.assertEqual(upgrade_module_mock.call_count, 1)
finally: finally:
Bmu._revert_method('upgrade_module') Bmu._revert_method("upgrade_module")
def test_nothing_to_upgrade(self): def test_nothing_to_upgrade(self):
Imm = self.env['ir.module.module'] Imm = self.env["ir.module.module"]
Bmu = self.env['base.module.upgrade'] Bmu = self.env["base.module.upgrade"]
Imm._save_installed_checksums() Imm._save_installed_checksums()
@ -226,12 +222,12 @@ class TestModuleAfterInstall(TransactionCase):
upgrade_module_mock.call_count = 0 upgrade_module_mock.call_count = 0
# upgrade_changed_checksum commits, so mock that # upgrade_changed_checksum commits, so mock that
with mock.patch.object(self.env.cr, 'commit'): with mock.patch.object(self.env.cr, "commit"):
# we simulate an install by setting module states # we simulate an install by setting module states
Bmu._patch_method('upgrade_module', upgrade_module_mock) Bmu._patch_method("upgrade_module", upgrade_module_mock)
try: try:
Imm.upgrade_changed_checksum() Imm.upgrade_changed_checksum()
self.assertEqual(upgrade_module_mock.call_count, 0) self.assertEqual(upgrade_module_mock.call_count, 0)
finally: finally:
Bmu._revert_method('upgrade_module') Bmu._revert_method("upgrade_module")

View File

@ -4,17 +4,15 @@
Copyright 2018 Brainbean Apps (https://brainbeanapps.com) Copyright 2018 Brainbean Apps (https://brainbeanapps.com)
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
--> -->
<record id="ir_module_module_upgrade_changed_checksum" model="ir.actions.server"> <record id="ir_module_module_upgrade_changed_checksum" model="ir.actions.server">
<field name="name">Auto-Upgrade Modules</field> <field name="name">Auto-Upgrade Modules</field>
<field name="type">ir.actions.server</field> <field name="type">ir.actions.server</field>
<field name="model_id" ref="base.model_ir_module_module"/> <field name="model_id" ref="base.model_ir_module_module" />
<field name="state">code</field> <field name="state">code</field>
<field name="code"> <field name="code">
action = model.upgrade_changed_checksum() action = model.upgrade_changed_checksum()
</field> </field>
</record> </record>
<menuitem <menuitem
name="Auto-Upgrade Modules" name="Auto-Upgrade Modules"
action="ir_module_module_upgrade_changed_checksum" action="ir_module_module_upgrade_changed_checksum"
@ -22,6 +20,6 @@
groups="base.group_no_one" groups="base.group_no_one"
parent="base.menu_management" parent="base.menu_management"
sequence="45" sequence="45"
icon="fa-exchange"/> icon="fa-exchange"
/>
</odoo> </odoo>