Browse Source

[REF][module_auto_update] Forward port v9 improvements

* Recompute dir hashes only when needed - By removing the recomputation from `update_list` we get faster CLI module upgrades and it only performs the autoupdate when using the autoupdate wizard or cron.
* Pass tests if addon is in readonly directory
* Set dependencies to upgrade

[FIX][module_auto_update] Pass tests if addon is in readonly directory

[FIX][module_auto_update] Set dependencies to upgrade
pull/1118/head
Jairo Llopis 7 years ago
parent
commit
10a6a1aa42
  1. 15
      module_auto_update/models/module.py
  2. 18
      module_auto_update/tests/test_module.py
  3. 16
      module_auto_update/tests/test_module_upgrade.py
  4. 16
      module_auto_update/wizards/module_upgrade.py

15
module_auto_update/models/module.py

@ -30,11 +30,16 @@ class Module(models.Model):
).split(",") ).split(",")
for r in self: for r in self:
try:
r.checksum_dir = dirhash( r.checksum_dir = dirhash(
get_module_path(r.name), get_module_path(r.name),
'sha1', 'sha1',
excluded_extensions=exclude, excluded_extensions=exclude,
) )
except TypeError:
_logger.debug(
"Cannot compute dir hash for %s, module not found",
r.display_name)
def _store_checksum_installed(self, vals): def _store_checksum_installed(self, vals):
if self.env.context.get('retain_checksum_installed'): if self.env.context.get('retain_checksum_installed'):
@ -66,16 +71,6 @@ class Module(models.Model):
res._store_checksum_installed(vals) res._store_checksum_installed(vals)
return res return res
@api.model
def update_list(self):
res = super(Module, self).update_list()
installed_modules = self.search([('state', '=', 'installed')])
upgradeable_modules = installed_modules.filtered(
lambda r: r.checksum_dir != r.checksum_installed,
)
upgradeable_modules.write({'state': "to upgrade"})
return res
@api.multi @api.multi
def write(self, vals): def write(self, vals):
res = super(Module, self).write(vals) res = super(Module, self).write(vals)

18
module_auto_update/tests/test_module.py

@ -3,6 +3,7 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import logging import logging
import os
import tempfile import tempfile
import mock import mock
@ -35,6 +36,7 @@ class TestModule(TransactionCase):
'sha1', 'sha1',
excluded_extensions=['pyc', 'pyo'], excluded_extensions=['pyc', 'pyo'],
) )
self.own_writeable = os.access(self.own_dir_path, os.W_OK)
@mock.patch('%s.get_module_path' % model) @mock.patch('%s.get_module_path' % model)
def create_test_module(self, vals, get_module_path_mock): def create_test_module(self, vals, get_module_path_mock):
@ -52,6 +54,8 @@ class TestModule(TransactionCase):
def test_compute_checksum_dir_ignore_excluded(self): def test_compute_checksum_dir_ignore_excluded(self):
"""It should exclude .pyc/.pyo extensions from checksum """It should exclude .pyc/.pyo extensions from checksum
calculations""" calculations"""
if not self.own_writeable:
self.skipTest("Own directory not writeable")
with tempfile.NamedTemporaryFile( with tempfile.NamedTemporaryFile(
suffix='.pyc', dir=self.own_dir_path): suffix='.pyc', dir=self.own_dir_path):
self.assertEqual( self.assertEqual(
@ -62,6 +66,8 @@ class TestModule(TransactionCase):
def test_compute_checksum_dir_recomputes_when_file_added(self): def test_compute_checksum_dir_recomputes_when_file_added(self):
"""It should return a different value when a non-.pyc/.pyo file is """It should return a different value when a non-.pyc/.pyo file is
added to the module directory""" added to the module directory"""
if not self.own_writeable:
self.skipTest("Own directory not writeable")
with tempfile.NamedTemporaryFile( with tempfile.NamedTemporaryFile(
suffix='.py', dir=self.own_dir_path): suffix='.py', dir=self.own_dir_path):
self.assertNotEqual( self.assertNotEqual(
@ -154,31 +160,33 @@ class TestModule(TransactionCase):
) )
@mock.patch('%s.get_module_path' % model) @mock.patch('%s.get_module_path' % model)
def test_update_list(self, get_module_path_mock):
def test_get_module_list(self, module_path_mock):
"""It should change the state of modules with different """It should change the state of modules with different
checksum_dir and checksum_installed to 'to upgrade'""" checksum_dir and checksum_installed to 'to upgrade'"""
get_module_path_mock.return_value = self.own_dir_path
module_path_mock.return_value = self.own_dir_path
vals = { vals = {
'name': 'module_auto_update_test_module', 'name': 'module_auto_update_test_module',
'state': 'installed', 'state': 'installed',
} }
test_module = self.create_test_module(vals) test_module = self.create_test_module(vals)
test_module.checksum_installed = 'test' test_module.checksum_installed = 'test'
self.env['ir.module.module'].update_list()
self.env['base.module.upgrade'].get_module_list()
self.assertEqual( self.assertEqual(
test_module.state, 'to upgrade', test_module.state, 'to upgrade',
'List update does not mark upgradeable modules "to upgrade"', 'List update does not mark upgradeable modules "to upgrade"',
) )
def test_update_list_only_changes_installed(self):
@mock.patch('%s.get_module_path' % model)
def test_get_module_list_only_changes_installed(self, module_path_mock):
"""It should not change the state of a module with a former state """It should not change the state of a module with a former state
other than 'installed' to 'to upgrade'""" other than 'installed' to 'to upgrade'"""
module_path_mock.return_value = self.own_dir_path
vals = { vals = {
'name': 'module_auto_update_test_module', 'name': 'module_auto_update_test_module',
'state': 'uninstalled', 'state': 'uninstalled',
} }
test_module = self.create_test_module(vals) test_module = self.create_test_module(vals)
self.env['ir.module.module'].update_list()
self.env['base.module.upgrade'].get_module_list()
self.assertNotEqual( self.assertNotEqual(
test_module.state, 'to upgrade', test_module.state, 'to upgrade',
'List update changed state of an uninstalled module', 'List update changed state of an uninstalled module',

16
module_auto_update/tests/test_module_upgrade.py

@ -31,12 +31,14 @@ class TestModuleUpgrade(TransactionCase):
@mock.patch.object(Registry, 'new') @mock.patch.object(Registry, 'new')
def test_upgrade_module(self, new_mock): def test_upgrade_module(self, new_mock):
"""It should call update_list method on ir.module.module"""
update_list_mock = mock.MagicMock()
self.env['ir.module.module']._patch_method(
'update_list',
update_list_mock,
"""Calls get_module_list when upgrading in api.model mode"""
get_module_list_mock = mock.MagicMock()
try:
self.env['base.module.upgrade']._patch_method(
'get_module_list',
get_module_list_mock,
) )
self.env['base.module.upgrade'].upgrade_module() self.env['base.module.upgrade'].upgrade_module()
update_list_mock.assert_called_once_with()
self.env['ir.module.module']._revert_method('update_list')
get_module_list_mock.assert_called_once_with()
finally:
self.env['base.module.upgrade']._revert_method('get_module_list')

16
module_auto_update/wizards/module_upgrade.py

@ -8,6 +8,16 @@ from odoo import api, models
class ModuleUpgrade(models.TransientModel): class ModuleUpgrade(models.TransientModel):
_inherit = 'base.module.upgrade' _inherit = 'base.module.upgrade'
@api.model
def get_module_list(self):
Module = self.env["ir.module.module"]
installed_modules = Module.search([('state', '=', 'installed')])
upgradeable_modules = installed_modules.filtered(
lambda r: r.checksum_dir != r.checksum_installed,
)
upgradeable_modules.button_upgrade()
return super(ModuleUpgrade, self).get_module_list()
@api.multi @api.multi
def upgrade_module_cancel(self): def upgrade_module_cancel(self):
return super( return super(
@ -17,5 +27,7 @@ class ModuleUpgrade(models.TransientModel):
@api.multi @api.multi
def upgrade_module(self): def upgrade_module(self):
self.env['ir.module.module'].update_list()
super(ModuleUpgrade, self).upgrade_module()
# Compute updates by checksum when called in @api.model fashion
if not self:
self.get_module_list()
return super(ModuleUpgrade, self).upgrade_module()
Loading…
Cancel
Save