966 lines
30 KiB
Python
966 lines
30 KiB
Python
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 = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1" a="1">
|
|
<div o-diff-key="2" a="2"/>
|
|
<div o-diff-key="3" a="3"/>
|
|
</div>
|
|
<div o-diff-key="4">
|
|
<div o-diff-key="5" a="5"/>
|
|
<div o-diff-key="6" a="6" />
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1" a="1">
|
|
<div o-diff-key="2" a="2"/>
|
|
</div>
|
|
<div o-diff-key="4">
|
|
<div o-diff-key="3" a="3"/>
|
|
<div o-diff-key="5" a="5"/>
|
|
<div o-diff-key="6" a="6" />
|
|
</div>
|
|
</div>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div[2]/div" position="before">
|
|
<xpath expr="/div/div/div[2]" position="move"/>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
""")
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1" a="1"><div o-diff-key="5" a="5"/><div o-diff-key="2" a="2"/><div o-diff-key="3" a="3"/></div>
|
|
<div o-diff-key="4"><div o-diff-key="6" a="6" /></div>
|
|
</div>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div/div" position="before">
|
|
<xpath expr="/div/div[2]/div" position="move"/>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
""")
|
|
|
|
def test_move_from_removed(self):
|
|
xml_old = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1" a="1">
|
|
<div o-diff-key="2" a="2"/>
|
|
<div o-diff-key="3" a="3"/>
|
|
</div>
|
|
<div o-diff-key="4">
|
|
<div o-diff-key="5" a="5"/>
|
|
<div o-diff-key="6" a="6" />
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="4">
|
|
<div o-diff-key="5" a="5"/>
|
|
<div o-diff-key="2" a="3"/>
|
|
<div o-diff-key="6" a="6" />
|
|
</div>
|
|
</div>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div[2]/div" position="after">
|
|
<xpath expr="/div/div/div" position="move"/>
|
|
</xpath>
|
|
</data>
|
|
<data>
|
|
<xpath expr="/div/div[2]/div[2]" position="attributes">
|
|
<attribute name="a">3</attribute>
|
|
</xpath>
|
|
</data>
|
|
<data>
|
|
<xpath expr="/div/div" position="replace"/>
|
|
</data>
|
|
</data>
|
|
""")
|
|
|
|
def test_move_from_move(self):
|
|
xml_old = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1" a="1">
|
|
<div o-diff-key="2" a="2"/>
|
|
<div o-diff-key="3" a="3"/>
|
|
</div>
|
|
<div o-diff-key="4">
|
|
<div o-diff-key="5" a="5"/>
|
|
<div o-diff-key="6" a="6" />
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="4">
|
|
<div o-diff-key="1" a="1">
|
|
<div o-diff-key="3" a="3"/>
|
|
</div>
|
|
<div o-diff-key="5" a="5"/>
|
|
<div o-diff-key="2" a="3"/>
|
|
<div o-diff-key="6" a="6" />
|
|
</div>
|
|
</div>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div[2]/div" position="before">
|
|
<xpath expr="/div/div" position="move"/>
|
|
</xpath>
|
|
<xpath expr="/div/div/div[2]" position="after">
|
|
<xpath expr="/div/div/div/div" position="move"/>
|
|
</xpath>
|
|
</data>
|
|
<data>
|
|
<xpath expr="/div/div/div[3]" position="attributes">
|
|
<attribute name="a">3</attribute>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
""")
|
|
|
|
def test_diff_inner_move(self):
|
|
xml_old = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1" a="1">
|
|
<div o-diff-key="2" a="2"/>
|
|
<div o-diff-key="3" a="3"/>
|
|
<div o-diff-key="4" name="4"/>
|
|
<div o-diff-key="5" a="5"/>
|
|
<div o-diff-key="6" a="6" />
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1" a="1">
|
|
<div o-diff-key="4" name="4"/>
|
|
<div o-diff-key="2" a="2"/>
|
|
<div o-diff-key="6" a="6"/>
|
|
<div o-diff-key="3" a="3"/>
|
|
<div o-diff-key="5" a="5"/>
|
|
</div>
|
|
</div>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div/div" position="before">
|
|
<xpath expr=".//div[@name='4']" position="move"/>
|
|
</xpath>
|
|
<xpath expr="/div/div/div[2]" position="after">
|
|
<xpath expr="/div/div/div[5]" position="move"/>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
""")
|
|
|
|
def test_text_replace(self):
|
|
xml_old = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1">
|
|
A
|
|
<div o-diff-key="2" a="2"/>
|
|
B
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1">
|
|
<div o-diff-key="2" a="2"/>
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div" position="inside">
|
|
<div o-diff-key="1"/>
|
|
</xpath>
|
|
<xpath expr="/div/div/div[2]" position="inside">
|
|
<xpath expr="/div/div/div" position="move"/>
|
|
</xpath>
|
|
<xpath expr="/div/div" position="replace">
|
|
<xpath expr="/div/div/div" position="move"/>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
""")
|
|
|
|
def test_text_replace2(self):
|
|
xml_old = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1">
|
|
A
|
|
<div o-diff-key="2" a="2"/>
|
|
B
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1">
|
|
AB
|
|
<div o-diff-key="2" a="2"/>
|
|
BC
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div" position="inside">
|
|
<div o-diff-key="1"/>
|
|
</xpath>
|
|
<xpath expr="/div/div/div[2]" position="inside">
|
|
AB
|
|
<xpath expr="/div/div/div" position="move"/>
|
|
BC
|
|
</xpath>
|
|
<xpath expr="/div/div" position="replace">
|
|
<xpath expr="/div/div/div" position="move"/>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
""")
|
|
|
|
def test_text_replace3(self):
|
|
xml_old = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1">
|
|
A
|
|
<div o-diff-key="2" a="2"/>
|
|
B
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1">
|
|
C
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div" position="replace">
|
|
<div o-diff-key="1">C</div>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
""")
|
|
|
|
def test_replace_all(self):
|
|
xml_old = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1" a="1">
|
|
A
|
|
<div o-diff-key="2" a="2">
|
|
<div o-diff-key="3" a="3" />
|
|
</div>
|
|
<div o-diff-key="4" a="4" />
|
|
B
|
|
</div>
|
|
<div o-diff-key="5" a="5"/>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1" a="changed">
|
|
<div o-diff-key="2" a="2"/>
|
|
C
|
|
<h1>
|
|
<div o-diff-key="3" a="change3" />
|
|
</h1>
|
|
<div o-diff-key="5" a="5"/>
|
|
B
|
|
</div>
|
|
</div>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div" position="inside">
|
|
<div o-diff-key="1" a="changed"/>
|
|
</xpath>
|
|
<xpath expr="/div/div/div[3]" position="inside">
|
|
<xpath expr="/div/div/div" position="move"/>
|
|
C
|
|
<h1/>
|
|
<xpath expr="/div/div[2]" position="move"/>
|
|
B
|
|
</xpath>
|
|
<xpath expr="/div/div" position="replace">
|
|
<xpath expr="/div/div/div[2]" position="move"/>
|
|
</xpath>
|
|
<xpath expr="/div/div/h1" position="inside">
|
|
<xpath expr="/div/div/div/div" position="move"/>
|
|
</xpath>
|
|
</data>
|
|
<data>
|
|
<xpath expr="/div/div/h1/div" position="attributes">
|
|
<attribute name="a">change3</attribute>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
""")
|
|
|
|
def test_text_keep(self):
|
|
xml_old = """
|
|
<div o-diff-key="0">
|
|
A
|
|
<div o-diff-key="2" a="2"/>
|
|
B
|
|
<div o-diff-key="3"/>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
A<h1/>D
|
|
<div o-diff-key="2" a="2"/>C<span/>
|
|
B
|
|
<div o-diff-key="3"/>
|
|
</div>
|
|
"""
|
|
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div" position="before"><h1/>D</xpath>
|
|
<xpath expr="/div/div" position="after">C<span/></xpath>
|
|
</data>
|
|
</data>
|
|
""")
|
|
|
|
def test_move_in_new(self):
|
|
xml_old = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="2" a="2"/>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
<span>
|
|
<h1>
|
|
<div o-diff-key="2" a="7"/>
|
|
</h1>
|
|
</span>
|
|
</div>
|
|
"""
|
|
reapplied_arch = """
|
|
<div o-diff-key="0">
|
|
<span is_new="true">
|
|
<h1 is_new="true">
|
|
<div o-diff-key="2" a="7"/>
|
|
</h1>
|
|
</span>
|
|
</div>
|
|
"""
|
|
|
|
differ_options = {
|
|
"on_new_node": lambda n: n.set("is_new", "true"),
|
|
}
|
|
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div" position="inside">
|
|
<span is_new="true">
|
|
<h1 is_new="true"/>
|
|
</span>
|
|
</xpath>
|
|
<xpath expr="/div/span/h1" position="inside">
|
|
<xpath expr="/div/div" position="move"/>
|
|
</xpath>
|
|
</data>
|
|
<data>
|
|
<xpath expr="/div/span/h1/div" position="attributes">
|
|
<attribute name="a">7</attribute>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
""", differ_options=differ_options, reapplied_arch=reapplied_arch)
|
|
|
|
def test_move_in_new_2(self):
|
|
xml_old = """
|
|
<div o-diff-key="0">
|
|
<div o-diff-key="1"/>
|
|
<div o-diff-key="2" a="2"/>
|
|
<div o-diff-key="3"/>
|
|
<div o-diff-key="4"/>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="0">
|
|
<span>
|
|
<h1>
|
|
A
|
|
<div o-diff-key="1"/>
|
|
<b>B</b>
|
|
C
|
|
<div o-diff-key="2" a="7"/>
|
|
D
|
|
<div o-diff-key="3"/>
|
|
E
|
|
<s>F</s>
|
|
<div o-diff-key="4"/>
|
|
G
|
|
</h1>
|
|
</span>
|
|
</div>
|
|
"""
|
|
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div" position="inside">
|
|
<span>
|
|
<h1>
|
|
A
|
|
<b>B</b>
|
|
E
|
|
<s>F</s>G
|
|
</h1>
|
|
</span>
|
|
</xpath>
|
|
<xpath expr="/div/span/h1/b" position="before">
|
|
<xpath expr="/div/div" position="move"/>
|
|
</xpath>
|
|
<xpath expr="/div/span/h1/b" position="after">
|
|
C
|
|
<xpath expr="/div/div" position="move"/>
|
|
D
|
|
<xpath expr="/div/div" position="move"/>
|
|
</xpath>
|
|
<xpath expr="/div/span/h1/s" position="after">
|
|
<xpath expr="/div/div" position="move"/>
|
|
</xpath>
|
|
</data>
|
|
<data>
|
|
<xpath expr="/div/span/h1/div[2]" position="attributes">
|
|
<attribute name="a">7</attribute>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
""")
|
|
|
|
def test_all_descendants_xpath(self):
|
|
xml_old = """
|
|
<form o-diff-key="0">
|
|
<field name="display_name" o-diff-key="1"/>
|
|
<field name="name" o-diff-key="2" />
|
|
<field name="some_ids" o-diff-key="3">
|
|
<form o-diff-key="4">
|
|
<div o-diff-key="5" ><field name="display_name" o-diff-key="6" /></div>
|
|
<notebook o-diff-key="7"/>
|
|
</form>
|
|
</field>
|
|
</form>
|
|
"""
|
|
|
|
xml_new = """
|
|
<form o-diff-key="0">
|
|
<field name="display_name" o-diff-key="1"/>
|
|
<field name="name" o-diff-key="2" />
|
|
<field name="some_ids" o-diff-key="3">
|
|
<form o-diff-key="4">
|
|
<div o-diff-key="5" ></div>
|
|
<notebook o-diff-key="7">
|
|
<field name="display_name" o-diff-key="6" />
|
|
</notebook>
|
|
</form>
|
|
</field>
|
|
</form>
|
|
"""
|
|
|
|
differ_options = {
|
|
"is_subtree": lambda n: n.tag in ("form", "field") and n.getparent() is not None,
|
|
}
|
|
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, """
|
|
<data>
|
|
<data>
|
|
<xpath expr=".//field[@name='some_ids']/form/notebook" position="inside">
|
|
<xpath expr=".//field[@name='some_ids']/form//field[@name='display_name']" position="move"/>
|
|
</xpath>
|
|
</data>
|
|
</data>""", differ_options=differ_options)
|
|
|
|
def test_with_meta(self):
|
|
xml_old = """
|
|
<div o-diff-key="1">
|
|
<div o-diff-key="2" class="some-class" />
|
|
<div o-diff-key="3" class="some-class2">
|
|
<div o-diff-key="4" a="3" class="some-class3" name="name"/>
|
|
<div o-diff-key="5"/>
|
|
<div o-diff-key="6" class="some-class4"/>
|
|
</div>
|
|
<div o-diff-key="7" class="some-class5"/>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="1">
|
|
<div o-diff-key="3">
|
|
<div o-diff-key="4" a="4"/>
|
|
<div o-diff-key="6"/>
|
|
<div o-diff-key="5"/>
|
|
</div>
|
|
<div o-diff-key="7">
|
|
<span>new</span>
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
differ_options = {
|
|
"xpath_with_meta": True
|
|
}
|
|
|
|
expected_arch = """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div" position="replace" meta-class="some-class"/>
|
|
</data>
|
|
<data>
|
|
<xpath expr=".//div[@name='name']" position="after" meta-a="3" meta-class="some-class3" meta-name="name">
|
|
<xpath expr="/div/div/div[3]" position="move" meta-class="some-class4"/>
|
|
</xpath>
|
|
<xpath expr="/div/div" position="attributes" meta-class="some-class2">
|
|
<attribute name="class"/>
|
|
</xpath>
|
|
</data>
|
|
<data>
|
|
<xpath expr=".//div[@name='name']" position="attributes" meta-a="3" meta-class="some-class3" meta-name="name">
|
|
<attribute name="a">4</attribute>
|
|
<attribute name="class"/>
|
|
<attribute name="name"/>
|
|
</xpath>
|
|
</data>
|
|
<data>
|
|
<xpath expr="/div/div/div[2]" position="attributes" meta-class="some-class4">
|
|
<attribute name="class"/>
|
|
</xpath>
|
|
</data>
|
|
<data>
|
|
<xpath expr="/div/div[2]" position="inside" meta-class="some-class5">
|
|
<span>new</span>
|
|
</xpath>
|
|
<xpath expr="/div/div[2]" position="attributes" meta-class="some-class5">
|
|
<attribute name="class"/>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch, differ_options=differ_options)
|
|
|
|
def test_replace_with(self):
|
|
xml_old = """
|
|
<div o-diff-key="1">
|
|
A
|
|
<div o-diff-key="2" class="some-class" />
|
|
B
|
|
<div o-diff-key="3" class="some-class2"/>
|
|
C
|
|
<div o-diff-key="7" class="some-class5"/>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="1">
|
|
A
|
|
<span/>
|
|
B
|
|
<h1/>
|
|
C
|
|
<div o-diff-key="7" class="some-class5"/>
|
|
</div>
|
|
"""
|
|
|
|
expected_arch = """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div[2]" position="replace"/>
|
|
<xpath expr="/div/div" position="replace">
|
|
<span/>
|
|
B
|
|
<h1/>
|
|
C
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)
|
|
|
|
def test_replace_with_2(self):
|
|
xml_old = """
|
|
<div o-diff-key="1">
|
|
A
|
|
<div o-diff-key="2" class="some-class" />
|
|
B
|
|
<div o-diff-key="3" class="some-class2"/>
|
|
C
|
|
<div o-diff-key="7" class="some-class5"/>
|
|
<div o-diff-key="8"/>
|
|
</div>
|
|
"""
|
|
|
|
xml_new = """
|
|
<div o-diff-key="1">
|
|
A
|
|
<span/>
|
|
<h1/>
|
|
C
|
|
<div o-diff-key="7" class="some-class5"/>
|
|
<div o-diff-key="2" class="some-class" />
|
|
B
|
|
<div o-diff-key="8"/>
|
|
</div>
|
|
"""
|
|
|
|
expected_arch = """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/div/div" position="before">
|
|
<span/>
|
|
<h1/>
|
|
C
|
|
<xpath expr="/div/div[3]" position="move"/>
|
|
</xpath>
|
|
<xpath expr="/div/div[3]" position="replace"/>
|
|
</data>
|
|
</data>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)
|
|
|
|
def test_candidate_replace(self):
|
|
xml_old = """
|
|
<form o-diff-key="1">
|
|
<field name="name" o-diff-key="2">
|
|
<form o-diff-key="3">
|
|
<field name="name2" o-diff-key="4"/>
|
|
</form>
|
|
</field>
|
|
</form>
|
|
"""
|
|
xml_new = """
|
|
<form o-diff-key="1">
|
|
<field name="name" o-diff-key="2">
|
|
<form o-diff-key="3">
|
|
<group>
|
|
<field name="name2" widget="some_widget"/>
|
|
</group>
|
|
</form>
|
|
</field>
|
|
</form>
|
|
"""
|
|
|
|
reapplied_arch = """
|
|
<form o-diff-key="1">
|
|
<field name="name" o-diff-key="2">
|
|
<form o-diff-key="3">
|
|
<group>
|
|
<field name="name2" o-diff-key="4" widget="some_widget"/>
|
|
</group>
|
|
</form>
|
|
</field>
|
|
</form>
|
|
"""
|
|
|
|
expected_arch = """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/form/field[@name='name']/form" position="inside">
|
|
<group/>
|
|
</xpath>
|
|
<xpath expr="/form/field[@name='name']/form/group" position="inside">
|
|
<xpath expr="/form/field[@name='name']/form//field[@name='name2']" position="move"/>
|
|
</xpath>
|
|
<xpath expr="/form/field[@name='name']/form//field[@name='name2']" position="attributes">
|
|
<attribute name="widget">some_widget</attribute>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
"""
|
|
|
|
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 = """
|
|
<form o-diff-key="1">
|
|
<field name="name" o-diff-key="2">
|
|
<form o-diff-key="3">
|
|
<field name="name2" o-diff-key="4"/>
|
|
<field name="name3" o-diff-key="5"/>
|
|
<field name="name4" o-diff-key="6"/>
|
|
</form>
|
|
</field>
|
|
</form>
|
|
"""
|
|
|
|
xml_new = """
|
|
<form o-diff-key="1">
|
|
<field name="name" o-diff-key="2">
|
|
<form o-diff-key="3">
|
|
<field name="name3" o-diff-key="5"/>
|
|
<field name="name4" o-diff-key="6"/>
|
|
<field name="name2" widget="some_widget"/>
|
|
</form>
|
|
</field>
|
|
</form>
|
|
"""
|
|
|
|
expected_arch = """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/form/field[@name='name']/form//field[@name='name4']" position="after">
|
|
<xpath expr="/form/field[@name='name']/form//field[@name='name2']" position="move"/>
|
|
</xpath>
|
|
<xpath expr="/form/field[@name='name']/form//field[@name='name2']" position="attributes">
|
|
<attribute name="widget">some_widget</attribute>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
"""
|
|
|
|
reapplied_arch = """
|
|
<form o-diff-key="1">
|
|
<field name="name" o-diff-key="2">
|
|
<form o-diff-key="3">
|
|
<field name="name3" o-diff-key="5"/>
|
|
<field name="name4" o-diff-key="6"/>
|
|
<field name="name2" o-diff-key="4" widget="some_widget"/>
|
|
</form>
|
|
</field>
|
|
</form>
|
|
"""
|
|
|
|
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 = """<form o-diff-key="1" a="1" b="2"/>"""
|
|
xml_new = """<form o-diff-key="1" a="3" />"""
|
|
|
|
expected_arch = """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/form" position="attributes">
|
|
<attribute name="a">3</attribute>
|
|
<attribute name="b"/>
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)
|
|
|
|
def test_comment(self):
|
|
xml_old = """
|
|
<form o-diff-key="1">
|
|
<div o-diff-key="2">
|
|
A
|
|
<!-- c1 -->
|
|
<div o-diff-key="3" />
|
|
<span o-diff-key="4" />
|
|
</div>
|
|
</form>
|
|
"""
|
|
xml_new = """
|
|
<form o-diff-key="1">
|
|
<div o-diff-key="2">
|
|
A
|
|
<!-- c1 -->
|
|
<!-- c2 -->
|
|
B
|
|
<div o-diff-key="3"/>
|
|
<span o-diff-key="4" />
|
|
</div>
|
|
</form>
|
|
"""
|
|
expected_arch = """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/form/div/div" position="before"><!-- c2 -->
|
|
B
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)
|
|
|
|
def test_comment_2(self):
|
|
xml_old = """
|
|
<form o-diff-key="1">
|
|
<div o-diff-key="2">
|
|
<div o-diff-key="3" />
|
|
A
|
|
<!-- c1 -->
|
|
<span o-diff-key="4" />
|
|
</div>
|
|
</form>
|
|
"""
|
|
xml_new = """
|
|
<form o-diff-key="1">
|
|
<div o-diff-key="2">
|
|
<!-- c2 -->
|
|
B
|
|
<div o-diff-key="3"/>
|
|
A
|
|
<!-- c1 -->
|
|
<span o-diff-key="4" />
|
|
</div>
|
|
</form>
|
|
"""
|
|
expected_arch = """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/form/div/div" position="before"><!-- c2 -->
|
|
B
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)
|
|
|
|
def test_comment_replace(self):
|
|
xml_old = """
|
|
<form o-diff-key="1">
|
|
<div o-diff-key="2">
|
|
<div o-diff-key="3" />
|
|
A
|
|
<!-- c1 -->
|
|
<div o-diff-key="4" />
|
|
<span o-diff-key="5" />
|
|
</div>
|
|
</form>
|
|
"""
|
|
xml_new = """
|
|
<form o-diff-key="1">
|
|
<div o-diff-key="2">
|
|
<div o-diff-key="3"/>
|
|
A
|
|
<!-- c1 -->
|
|
<!-- c2 -->
|
|
B
|
|
<span o-diff-key="5" />
|
|
</div>
|
|
</form>
|
|
"""
|
|
expected_arch = """
|
|
<data>
|
|
<data>
|
|
<xpath expr="/form/div/div[2]" position="replace"><!-- c2 -->
|
|
B
|
|
</xpath>
|
|
</data>
|
|
</data>
|
|
"""
|
|
self._assertXpathDiffAndReapply(xml_old, xml_new, expected_arch)
|