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 = """
A
B
""" xml_new = """
""" self._assertXpathDiffAndReapply(xml_old, xml_new, """
""") def test_text_replace2(self): xml_old = """
A
B
""" xml_new = """
AB
BC
""" self._assertXpathDiffAndReapply(xml_old, xml_new, """
AB BC """) def test_text_replace3(self): xml_old = """
A
B
""" xml_new = """
C
""" self._assertXpathDiffAndReapply(xml_old, xml_new, """
C
""") def test_replace_all(self): xml_old = """
A
B
""" xml_new = """
C

B
""" self._assertXpathDiffAndReapply(xml_old, xml_new, """
C

B change3 """) def test_text_keep(self): xml_old = """
A
B
""" xml_new = """
A

D
C B
""" 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 = """

A
B C
D
E F
G

""" 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 = """
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 = """
A
B
C
""" xml_new = """
A B

C
""" expected_arch = """ B

C """ self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch) def test_replace_with_2(self): xml_old = """
A
B
C
""" xml_new = """
A

C
B
""" 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 = """
A
""" xml_new = """
A B
""" expected_arch = """ B """ self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch) def test_comment_2(self): xml_old = """
A
""" xml_new = """
B
A
""" expected_arch = """ B """ self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch) def test_comment_replace(self): xml_old = """
A
""" xml_new = """
A B
""" expected_arch = """ B """ self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)