124 lines
5.3 KiB
Python
124 lines
5.3 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import logging
|
|
from ast import literal_eval
|
|
from os.path import join as opj
|
|
|
|
from odoo.modules import get_modules
|
|
from odoo.modules.module import _DEFAULT_MANIFEST, module_manifest, get_module_path
|
|
from odoo.tests import BaseCase
|
|
from odoo.tools.misc import file_open, file_path
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
MANIFEST_KEYS = {
|
|
'name', 'icon', 'addons_path', 'license', # mandatory keys
|
|
*_DEFAULT_MANIFEST, # optional keys
|
|
'contributors', 'maintainer', 'url', # unused "informative" keys
|
|
}
|
|
|
|
|
|
class ManifestLinter(BaseCase):
|
|
|
|
def _load_manifest(self, module):
|
|
"""Do not rely on odoo/modules/module -> load_manifest
|
|
as we want to check manifests content, independently of the
|
|
values from _DEFAULT_MANIFEST added automatically by load_manifest
|
|
"""
|
|
mod_path = get_module_path(module, downloaded=True)
|
|
manifest_file = module_manifest(mod_path)
|
|
|
|
manifest_data = {}
|
|
with file_open(manifest_file, mode='r') as f:
|
|
manifest_data.update(literal_eval(f.read()))
|
|
|
|
return manifest_data
|
|
|
|
def test_manifests(self):
|
|
for module in get_modules():
|
|
with self.subTest(module=module):
|
|
manifest_data = self._load_manifest(module)
|
|
self._test_manifest_keys(module, manifest_data)
|
|
self._test_manifest_values(module, manifest_data)
|
|
|
|
def _test_manifest_keys(self, module, manifest_data):
|
|
manifest_keys = manifest_data.keys()
|
|
unknown_keys = manifest_keys - MANIFEST_KEYS
|
|
self.assertEqual(unknown_keys, set(), f"Unknown manifest keys in module {module!r}. Either there are typos or they must be white listed.")
|
|
|
|
def _test_manifest_values(self, module, manifest_data):
|
|
verified_keys = [
|
|
'application', 'auto_install',
|
|
'summary', 'description', 'author',
|
|
'demo', 'data', 'test',
|
|
# todo installable ?
|
|
]
|
|
|
|
if len(manifest_data.get('countries', [])) == 1 and 'l10n' not in module:
|
|
_logger.warning(
|
|
"Module %r specific to one single country %r should contain `l10n` in their name.",
|
|
module, manifest_data['countries'][0])
|
|
|
|
for key in manifest_data:
|
|
value = manifest_data[key]
|
|
if key in _DEFAULT_MANIFEST:
|
|
if key in verified_keys:
|
|
self.assertNotEqual(
|
|
value,
|
|
_DEFAULT_MANIFEST[key],
|
|
f"Setting manifest key {key} to the default manifest value for module {module!r}. "
|
|
"You can remove this key from the dict to reduce noise/inconsistencies between manifests specifications"
|
|
" and ease understanding of manifest content."
|
|
)
|
|
|
|
expected_type = type(_DEFAULT_MANIFEST[key])
|
|
if not isinstance(value, expected_type):
|
|
if key != 'auto_install':
|
|
_logger.warning(
|
|
"Wrong type for manifest value %s in module %s, expected %s",
|
|
key, module, expected_type)
|
|
elif not isinstance(value, list):
|
|
_logger.warning(
|
|
"Wrong type for manifest value %s in module %s, expected bool or list",
|
|
key, module)
|
|
else:
|
|
if key == 'countries':
|
|
self._test_manifest_countries_value(module, value)
|
|
elif key == 'icon':
|
|
self._test_manifest_icon_value(module, value)
|
|
|
|
def _test_manifest_icon_value(self, module, value):
|
|
self.assertTrue(
|
|
isinstance(value, str),
|
|
f"Wrong type for manifest value icon in module {module!r}, expected string",
|
|
)
|
|
self.assertNotEqual(
|
|
value,
|
|
f"/{module}/static/description/icon.png",
|
|
f"Setting manifest key icon to the default manifest value for module {module!r}. "
|
|
"You can remove this key from the dict to reduce noise/inconsistencies between manifests specifications"
|
|
" and ease understanding of manifest content."
|
|
)
|
|
if not value:
|
|
_logger.warning(
|
|
"Empty value specified as icon in manifest of module %r."
|
|
" Please specify a correct value or remove this key from the manifest.",
|
|
module)
|
|
else:
|
|
path_parts = value.split('/')
|
|
try:
|
|
file_path(opj(*path_parts[1:]))
|
|
except FileNotFoundError:
|
|
_logger.warning(
|
|
"Icon value specified in manifest of module %s wasn't found in given path."
|
|
" Please specify a correct value or remove this key from the manifest.",
|
|
module)
|
|
|
|
def _test_manifest_countries_value(self, module, values):
|
|
for value in values:
|
|
if value and len(value) != 2:
|
|
_logger.warning(
|
|
"Country value %s specified for the icon in manifest of module %s doesn't look like a country code"
|
|
"Please specify a correct value or remove this key from the manifest.",
|
|
value, module)
|