# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import random
import re
from unittest.mock import patch
import textwrap
from datetime import datetime, timedelta
from lxml import etree
from odoo.tests.common import BaseCase, tagged
from odoo.tools import topological_sort
from odoo.addons.web.controllers.main import HomeStaticTemplateHelpers
def sample(population):
return random.sample(
population,
random.randint(0, min(len(population), 5)))
class TestModulesLoading(BaseCase):
def setUp(self):
self.mods = [str(i) for i in range(1000)]
def test_topological_sort(self):
random.shuffle(self.mods)
modules = [
(k, sample(self.mods[:i]))
for i, k in enumerate(self.mods)]
random.shuffle(modules)
ms = dict(modules)
seen = set()
sorted_modules = topological_sort(ms)
for module in sorted_modules:
deps = ms[module]
self.assertGreaterEqual(
seen, set(deps),
'Module %s (index %d), ' \
'missing dependencies %s from loaded modules %s' % (
module, sorted_modules.index(module), deps, seen
))
seen.add(module)
@tagged('static_templates')
class TestStaticInheritance(BaseCase):
def setUp(self):
super(TestStaticInheritance, self).setUp()
# output is "manifest_glob" return
self.modules = [
('module_1_file_1', None, 'module_1'),
('module_2_file_1', None, 'module_2'),
]
self.template_files = {
'module_1_file_1': b"""
"""
with self.assertRaises(ValueError) as ve:
HomeStaticTemplateHelpers.get_qweb_templates(addons=self._get_module_names(), debug=True)
self.assertEqual(
str(ve.exception),
'No template found to inherit from. Module module_2 and template name template_2_2'
)
def _sick_script(self, nMod, nFilePerMod, nTemplatePerFile, stepInheritInModule=2, stepInheritPreviousModule=3):
"""
Make a sick amount of templates to test perf
nMod modules
each module: has nFilesPerModule files, each of which contains nTemplatePerFile templates
"""
self.modules = []
self.template_files = {}
number_templates = 0
for m in range(nMod):
for f in range(nFilePerMod):
mname = 'mod_%s' % m
fname = 'mod_%s_file_%s' % (m, f)
self.modules.append((fname, None, mname))
_file = ''
for t in range(nTemplatePerFile):
_template = ''
if t % stepInheritInModule or t % stepInheritPreviousModule or t == 0:
_template += """
Parent
"""
elif not t % stepInheritInModule and t >= 1:
_template += """
Sick XPath
"""
elif not t % stepInheritPreviousModule and m >= 1:
_template += """
Mental XPath
"""
if _template:
number_templates += 1
_template_number = 1000 * f + t
_file += _template % {
't_number': _template_number,
'm_number': m,
't_inherit': _template_number - 1,
't_module_inherit': _template_number,
'm_module_inherit': m - 1,
}
_file += ''
self.template_files[fname] = _file.encode()
self.assertEqual(number_templates, nMod * nFilePerMod * nTemplatePerFile)
def test_performance_2500(self):
nMod, nFilePerMod, nTemplatePerFile = 50, 5, 10
self._sick_script(nMod, nFilePerMod, nTemplatePerFile)
before = datetime.now()
contents = HomeStaticTemplateHelpers.get_qweb_templates(addons=self._get_module_names(), debug=True)
after = datetime.now()
self.assertLessEqual(after - before, timedelta(milliseconds=130))
whole_tree = etree.fromstring(contents)
self.assertEqual(len(whole_tree), nMod * nFilePerMod * nTemplatePerFile)
def test_performance_25000(self):
nMod, nFilePerMod, nTemplatePerFile = 50, 5, 100
self._sick_script(nMod, nFilePerMod, nTemplatePerFile)
before = datetime.now()
HomeStaticTemplateHelpers.get_qweb_templates(addons=self._get_module_names(), debug=True)
after = datetime.now()
self.assertLessEqual(after - before, timedelta(milliseconds=1000))
def test_replace_in_debug_mode(self):
"""
Replacing a template's meta definition in place doesn't keep the original attrs of the template
"""
self.modules = [
('module_1_file_1', None, 'module_1'),
]
self.template_files = {
'module_1_file_1': b"""
""",
}
contents = HomeStaticTemplateHelpers.get_qweb_templates(addons=self._get_module_names(), debug=True)
expected = b"""
"""
self.assertXMLEqual(contents, expected)
def test_replace_in_debug_mode3(self):
"""Text outside of a div which will replace a whole template
becomes outside of the template
This doesn't mean anything in terms of the business of template inheritance
But it is in the XPATH specs"""
self.modules = [
('module_1_file_1', None, 'module_1'),
]
self.template_files = {
'module_1_file_1': b"""
And I grew strong
And I learned how to get along
And so you're back
""",
}
contents = HomeStaticTemplateHelpers.get_qweb_templates(addons=self._get_module_names(), debug=True)
expected = b"""
And so you're back
"""
self.assertXMLEqual(contents, expected)
def test_replace_root_node_tag(self):
"""
Root node is not targeted by //NODE_TAG in xpath
"""
self.modules = [
('module_1_file_1', None, 'module_1'),
]
self.template_files = {
'module_1_file_1': b"""
Form replacer
""",
}
contents = HomeStaticTemplateHelpers.get_qweb_templates(addons=self._get_module_names(), debug=True)
expected = b"""
"""
self.assertXMLEqual(contents, expected)
def test_replace_root_node_tag_in_primary(self):
"""
Root node is not targeted by //NODE_TAG in xpath
"""
self.maxDiff = None
self.modules = [
('module_1_file_1', None, 'module_1'),
]
self.template_files = {
'module_1_file_1': b"""
Form replacer
""",
}
contents = HomeStaticTemplateHelpers.get_qweb_templates(addons=self._get_module_names(), debug=True)
expected = b"""
"""
self.assertXMLEqual(contents, expected)
def test_inherit_primary_replace_debug(self):
"""
The inheriting template has got both its own defining attrs
and new ones if one is to replace its defining root node
"""
self.modules = [
('module_1_file_1', None, 'module_1'),
]
self.template_files = {
'module_1_file_1': b"""