from lxml import etree
from odoo.addons.web_studio.controllers.keyed_xml_differ import (
KeyedXmlDiffer,
dedent_tree,
indent_tree,
diff_dicts,
longest_increasing_subsequence,
)
from odoo.tests.common import BaseCase
from odoo.tools.template_inheritance import apply_inheritance_specs
class TestXmlDiffer(BaseCase):
def test_attribute_diff(self):
d1 = {"k": "v", "k2": "v2", "kignore": "ignore"}
d2 = {"k": "v1", "kignore": "changed", "k3": "v3"}
changes = diff_dicts(d1, d2, {"kignore"})
self.assertEqual(changes, {
"k": "v1",
"k3": "v3",
"k2": None,
})
def test_longest_increasing_sequence(self):
self.assertEqual(longest_increasing_subsequence([]), [])
self.assertEqual(longest_increasing_subsequence([8, 3, 4, 6, 5, 2, 0, 7, 9, 1]), [9, 7, 5, 4, 3])
self.assertEqual(longest_increasing_subsequence([1, 3, 4, 2]), [4, 3, 1])
self.assertEqual(longest_increasing_subsequence([4, 3, 2, 1]), [1])
self.assertEqual(longest_increasing_subsequence([1, 2, 3, 4]), [4, 3, 2, 1])
self.assertEqual(longest_increasing_subsequence([4, 2, 6, 3, 5]), [5, 3, 2])
# longest_increasing_subsequence compares items on their value
# in the case items are string: "11" < "2" == True
# transforming an item to its value if necessary should be done before hand
self.assertEqual(longest_increasing_subsequence(["11", "2", "3"]), ["3", "2", "11"])
d1 = {
"a": "3",
"b": "13",
"c": "1",
"d": "2",
"e": "14",
}
items = [(int(val), key) for key, val in d1.items()]
self.assertEqual([key for val, key in longest_increasing_subsequence(items)], ["e", "d", "c"])
def _standard_indent(self, str_arch):
arch = etree.fromstring(str_arch)
dedent_tree(arch)
indent_tree(arch)
return etree.tostring(arch)
def _assertXpathDiffAndReapply(self, old, new, expected_arch_diff, differ_options=None, reapplied_arch=None):
differ_options = {} if differ_options is None else differ_options
differ = KeyedXmlDiffer(**differ_options)
old = self._standard_indent(old)
new = self._standard_indent(new)
expected_arch_diff = self._standard_indent(expected_arch_diff)
if reapplied_arch:
reapplied_arch = self._standard_indent(reapplied_arch)
arch = differ.diff_xpath(old, new)
self.assertXMLEqual(arch, expected_arch_diff)
reapplied = apply_inheritance_specs(etree.fromstring(old), etree.fromstring(arch))
dedent_tree(reapplied)
indent_tree(reapplied)
self.assertXMLEqual(etree.tostring(reapplied), reapplied_arch or new)
def test_diff_move(self):
xml_old = """
"""
xml_new = """
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, """
""")
xml_new = """
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, """
""")
def test_move_from_removed(self):
xml_old = """
"""
xml_new = """
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, """
3
""")
def test_move_from_move(self):
xml_old = """
"""
xml_new = """
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, """
3
""")
def test_diff_inner_move(self):
xml_old = """
"""
xml_new = """
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, """
""")
def test_text_replace(self):
xml_old = """
"""
xml_new = """
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, """
""")
def test_text_replace2(self):
xml_old = """
"""
xml_new = """
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, """
AB
BC
""")
def test_text_replace3(self):
xml_old = """
"""
xml_new = """
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, """
C
""")
def test_replace_all(self):
xml_old = """
"""
xml_new = """
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, """
C
B
change3
""")
def test_text_keep(self):
xml_old = """
"""
xml_new = """
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, """
D
C
""")
def test_move_in_new(self):
xml_old = """
"""
xml_new = """
"""
reapplied_arch = """
"""
differ_options = {
"on_new_node": lambda n: n.set("is_new", "true"),
}
self._assertXpathDiffAndReapply(xml_old, xml_new, """
7
""", differ_options=differ_options, reapplied_arch=reapplied_arch)
def test_move_in_new_2(self):
xml_old = """
"""
xml_new = """
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, """
A
B
E
FG
C
D
7
""")
def test_all_descendants_xpath(self):
xml_old = """
"""
xml_new = """
"""
differ_options = {
"is_subtree": lambda n: n.tag in ("form", "field") and n.getparent() is not None,
}
self._assertXpathDiffAndReapply(xml_old, xml_new, """
""", differ_options=differ_options)
def test_with_meta(self):
xml_old = """
"""
xml_new = """
"""
differ_options = {
"xpath_with_meta": True
}
expected_arch = """
4
new
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch, differ_options=differ_options)
def test_replace_with(self):
xml_old = """
"""
xml_new = """
"""
expected_arch = """
B
C
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)
def test_replace_with_2(self):
xml_old = """
"""
xml_new = """
"""
expected_arch = """
C
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)
def test_candidate_replace(self):
xml_old = """
"""
xml_new = """
"""
reapplied_arch = """
"""
expected_arch = """
some_widget
"""
differ_options = {
"is_subtree": lambda n: n.tag == "form" or None,
"get_moving_candidate_key": lambda n: ("field", n.get("name")) if n.tag == "field" else None
}
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch, differ_options=differ_options, reapplied_arch=reapplied_arch)
def test_candidate_replace2(self):
xml_old = """
"""
xml_new = """
"""
expected_arch = """
some_widget
"""
reapplied_arch = """
"""
differ_options = {
"is_subtree": lambda n: n.tag == "form" or None,
"get_moving_candidate_key": lambda n: ("field", n.get("name")) if n.tag == "field" else None
}
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch, differ_options=differ_options, reapplied_arch=reapplied_arch)
def test_attributes(self):
xml_old = """"""
xml_new = """"""
expected_arch = """
3
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)
def test_comment(self):
xml_old = """
"""
xml_new = """
"""
expected_arch = """
B
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)
def test_comment_2(self):
xml_old = """
"""
xml_new = """
"""
expected_arch = """
B
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)
def test_comment_replace(self):
xml_old = """
"""
xml_new = """
"""
expected_arch = """
B
"""
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)