[Checkins] SVN: z3c.etree/trunk/src/z3c/etree/ Add new method
`assertXMLEqualIgnoreOrdering' and doctest option
Michael Kerrin
michael.kerrin at openapp.ie
Thu Feb 7 15:34:23 EST 2008
Log message for revision 83653:
Add new method `assertXMLEqualIgnoreOrdering' and doctest option
`XMLDATA_IGNOREORDER' to test XML output while ignoring the ordering of the
subelements under certain restrictions.
Changed:
U z3c.etree/trunk/src/z3c/etree/doctests.txt
U z3c.etree/trunk/src/z3c/etree/doctestssuccess.txt
U z3c.etree/trunk/src/z3c/etree/doctesttests.txt
U z3c.etree/trunk/src/z3c/etree/testing.py
U z3c.etree/trunk/src/z3c/etree/tests.py
-=-
Modified: z3c.etree/trunk/src/z3c/etree/doctests.txt
===================================================================
--- z3c.etree/trunk/src/z3c/etree/doctests.txt 2008-02-07 20:27:50 UTC (rev 83652)
+++ z3c.etree/trunk/src/z3c/etree/doctests.txt 2008-02-07 20:34:23 UTC (rev 83653)
@@ -41,7 +41,7 @@
'<test><subtest attr="d">XXX</subtest></test>'
ElementTree objects
-===================
+-------------------
Got to see what happens when 'got' argument is an ElementTree object.
@@ -53,3 +53,36 @@
<subtest>
</subtest>
</test>
+
+Ordering matters with XMLDATA
+-----------------------------
+
+ >>> data = '''<D:multistatus xmlns:D='DAV:'>
+ ... <D:response>
+ ... <D:prop>
+ ... <test1/>
+ ... <test2/>
+ ... </D:prop>
+ ... </D:response></D:multistatus>'''
+
+ >>> print data #doctest:+XMLDATA
+ <D:multistatus xmlns:D='DAV:'>
+ <D:response>
+ <D:prop>
+ <test2/>
+ <test1/>
+ </D:prop>
+ </D:response>
+ </D:multistatus>
+
+But not with XMLDATA_IGNOREORDER
+
+ >>> print data #doctest:+XMLDATA_IGNOREORDER
+ <D:multistatus xmlns:D='DAV:'>
+ <D:response>
+ <D:prop>
+ <test2/>
+ <test1/>
+ </D:prop>
+ </D:response>
+ </D:multistatus>
Modified: z3c.etree/trunk/src/z3c/etree/doctestssuccess.txt
===================================================================
--- z3c.etree/trunk/src/z3c/etree/doctestssuccess.txt 2008-02-07 20:27:50 UTC (rev 83652)
+++ z3c.etree/trunk/src/z3c/etree/doctestssuccess.txt 2008-02-07 20:34:23 UTC (rev 83653)
@@ -9,3 +9,24 @@
>>> 'Hello, world!'
'Hello, world!'
+
+Order doesn't matter with XMLDATA_IGNOREORDER
+---------------------------------------------
+
+ >>> data = '''<D:multistatus xmlns:D='DAV:'>
+ ... <D:response>
+ ... <D:prop>
+ ... <test1/>
+ ... <test2/>
+ ... </D:prop>
+ ... </D:response></D:multistatus>'''
+
+ >>> print data #doctest:+XMLDATA_IGNOREORDER
+ <D:multistatus xmlns:D='DAV:'>
+ <D:response>
+ <D:prop>
+ <test2/>
+ <test1/>
+ </D:prop>
+ </D:response>
+ </D:multistatus>
Modified: z3c.etree/trunk/src/z3c/etree/doctesttests.txt
===================================================================
--- z3c.etree/trunk/src/z3c/etree/doctesttests.txt 2008-02-07 20:27:50 UTC (rev 83652)
+++ z3c.etree/trunk/src/z3c/etree/doctesttests.txt 2008-02-07 20:34:23 UTC (rev 83653)
@@ -81,6 +81,29 @@
'<test><subtest attr="d">XXX</subtest></test>'
XML differences:
'subtest' expected to find the 'a' attribute.
+ ----------------------------------------------------------------------
+ File doctests.txt
+ Failed example:
+ doctest:+XMLDATA
+ Expected:
+ <D:multistatus xmlns:D='DAV:'>
+ <D:response>
+ <D:prop>
+ <test2/>
+ <test1/>
+ </D:prop>
+ </D:response>
+ </D:multistatus>
+ Got:
+ <D:multistatus xmlns:D='DAV:'>
+ <D:response>
+ <D:prop>
+ <test1/>
+ <test2/>
+ </D:prop>
+ </D:response></D:multistatus>
+ XML differences:
+ 'test2' != 'test1' different tag name.
<BLANKLINE>
Ran N tests with N failures and N errors in N.NNN seconds.
True
Modified: z3c.etree/trunk/src/z3c/etree/testing.py
===================================================================
--- z3c.etree/trunk/src/z3c/etree/testing.py 2008-02-07 20:27:50 UTC (rev 83652)
+++ z3c.etree/trunk/src/z3c/etree/testing.py 2008-02-07 20:34:23 UTC (rev 83653)
@@ -159,6 +159,47 @@
return False, "'%r != %r' have different element content." %(want, got)
+def _assertSubXMLElements(want, got, optionflags):
+ for index in range(0, len(want)):
+ result, msg = _assertXMLElementEqual(
+ want[index], got[index], optionflags)
+ if not result:
+ return result, msg
+
+ return True, None
+
+
+def _assertSubXMLElementsUnordered(want, got, optionflags):
+ # First we check all the elements in order and if one of the elements
+ # tags doesn't match what we expected then we start to call the `findall'
+ # method to try and find the element we expected. The reason for this is
+ # that if we do have multiple elements in an XML fragment with the same
+ # tag it is very hard to report on which element didn't match which. But
+ # in the case of WebDAV this works a treat since the ordering of
+ # properties is depended on the Zope component architecture but we never
+ # have multiple properties in these XML documents.
+ for index in range(0, len(want)):
+ wantchild = want[index]
+ gotchild = got[index]
+ if wantchild.tag != gotchild.tag:
+ gotchild = got.findall(str(wantchild.tag))
+ if len(gotchild) == 0:
+ return False, \
+ "Failed to find the element %r in the fragment %r." %(
+ str(wantchild.tag), str(got.tag))
+ if len(gotchild) > 1:
+ return False, "Too many %r sub-elements where found in %r. " \
+ "Ordering algorithim broke down." %(
+ str(wantchild.tag), str(got.tag))
+ gotchild = gotchild[0]
+ result, msg = _assertXMLElementEqual(
+ wantchild, gotchild, optionflags)
+ if not result:
+ return result, msg
+
+ return True, None
+
+
def _assertXMLElementEqual(want, got, optionflags):
# See assertXMLEqual for tests - it is easier to the tests with strings that
# get converted to element tree objects in assertXMLEqual.
@@ -187,15 +228,31 @@
False, "%r attribute has different value for the %r tag." %(
attrib, want.tag)
- for index in range(0, len(want)):
- result, msg = _assertXMLElementEqual(
- want[index], got[index], optionflags)
- if not result:
- return result, msg
+ if optionflags & XMLDATA_IGNOREORDER:
+ return _assertSubXMLElementsUnordered(want, got, optionflags)
+ else:
+ return _assertSubXMLElements(want, got, optionflags)
- return True, None
+def _cleanTree(want, got):
+ etree = z3c.etree.getEngine()
+ if isinstance(want, (str, unicode)):
+ want = etree.fromstring(want)
+ if isinstance(got, (str, unicode)):
+ got = etree.fromstring(got)
+
+ if getattr(want, "getroot", None) is not None:
+ # Most then likely a ElementTree object.
+ want = want.getroot()
+
+ if getattr(got, "getroot", None) is not None:
+ # Most then likely a ElementTree object.
+ got = got.getroot()
+
+ return want, got
+
+
def assertXMLEqual(want, got):
"""
@@ -324,22 +381,144 @@
>>> assertXMLEqual(elroot, '<test />')
"""
- etree = z3c.etree.getEngine()
+ want, got = _cleanTree(want, got)
- if isinstance(want, (str, unicode)):
- want = etree.fromstring(want)
- if isinstance(got, (str, unicode)):
- got = etree.fromstring(got)
+ result, msg = _assertXMLElementEqual(want, got, XMLDATA)
+ assert result, msg
- if getattr(want, "getroot", None) is not None:
- # Most then likely a ElementTree object.
- want = want.getroot()
- if getattr(got, "getroot", None) is not None:
- # Most then likely a ElementTree object.
- got = got.getroot()
-
- result, msg = _assertXMLElementEqual(want, got, XMLDATA)
+def assertXMLEqualIgnoreOrdering(want, got):
+ """
+
+ >>> assertXMLEqualIgnoreOrdering('<test>xml</test>', '<test>xml</test>')
+
+ Re-ording sub-elements of an XML fragment still passes with this method.
+
+ >>> assertXMLEqualIgnoreOrdering(
+ ... '<test><a>Content</a><b>Content</b></test>',
+ ... '<test><a>Content</a><b>Content</b></test>')
+
+ Now we re-order the `a' and `b' elements.
+
+ >>> assertXMLEqualIgnoreOrdering(
+ ... '<test><a>Content</a><b>Content</b></test>',
+ ... '<test><b>Content</b><a>Content</a></test>')
+
+ But with the previous `assertXMLEqual' it still fails.
+
+ >>> assertXMLEqual(
+ ... '<test><a>Content</a><b>Content</b></test>',
+ ... '<test><b>Content</b><a>Content</a></test>')
+ Traceback (most recent call last):
+ ...
+ AssertionError: 'a' != 'b' different tag name.
+
+ Ording still picks up on different attributes.
+
+ >>> assertXMLEqualIgnoreOrdering(
+ ... '<test><a attr="1">Content</a><b>Content</b></test>',
+ ... '<test><b>Content</b><a>Content</a></test>')
+ Traceback (most recent call last):
+ ...
+ AssertionError: 1 != 0 different number of attributes on 'a'.
+
+ And on different content.
+
+ >>> assertXMLEqualIgnoreOrdering(
+ ... '<test><a>Different content</a><b>Content</b></test>',
+ ... '<test><b>Content</b><a>Content</a></test>')
+ Traceback (most recent call last):
+ ...
+ AssertionError: ''Different content' != 'Content'' have different element content.
+
+ Missing element.
+
+ >>> assertXMLEqualIgnoreOrdering(
+ ... '<test><b>Content</b></test>',
+ ... '<test><b>Content</b><a>Content</a></test>')
+ Traceback (most recent call last):
+ ...
+ AssertionError: 1 != 2 different number of subchildren on 'test'.
+
+ >>> assertXMLEqualIgnoreOrdering(
+ ... '<test><c>Content</c><b>Content</b></test>',
+ ... '<test><b>Content</b><a>Content</a></test>')
+ Traceback (most recent call last):
+ ...
+ AssertionError: Failed to find the element 'c' in the fragment 'test'.
+
+ Un-ordering matching does not work when we have more then one sub-element
+ with the same tag. This is problematic as their is no way for me to find
+ the correct expected element reliably.
+
+ >>> assertXMLEqualIgnoreOrdering(
+ ... '<test><a>Content</a><b>Content</b><a>Test</a></test>',
+ ... '<test><b>Content</b><a>Content</a><a>Test</a></test>')
+ Traceback (most recent call last):
+ ...
+ AssertionError: Too many 'a' sub-elements where found in 'test'. Ordering algorithim broke down.
+
+ But if the ordering breaks down within a level of the XML fragment that
+ doesn't contain duplicate elements then everything works. Even though we
+ have duplicate `r' elements it is only on the third level that the sub
+ ordering breaks down and so the documents match.
+
+ >>> assertXMLEqualIgnoreOrdering(
+ ... '<test><r><a>Content</a><b>Content</b></r><r><a>Test</a></r></test>',
+ ... '<test><r><b>Content</b><a>Content</a></r><r><a>Test</a></r></test>')
+
+ >>> etree = z3c.etree.getEngine()
+
+ Pass `assertXMLEqualIgnoreOrdering' an actual elementtree element object
+ whose tag was setup via the etree.QName method. Make sure that we don't
+ have any errors in processing this element.
+
+ >>> el = etree.Element('test')
+ >>> el.append(etree.Element(etree.QName('testns:', 'a')))
+ >>> el[-1].text = 'Content'
+ >>> el.append(etree.Element('{testns:}b'))
+ >>> el[-1].text = 'Content'
+
+ >>> assertXMLEqualIgnoreOrdering(el,
+ ... '<test><b xmlns="testns:">Content</b><a xmlns="testns:">Content</a></test>')
+
+ >>> assertXMLEqualIgnoreOrdering(
+ ... '<test><b xmlns="testns:">Content</b><a xmlns="testns:">Content</a></test>',
+ ... el)
+
+ Also make sure that the errors messages returned by
+ `assertXMLEqualIgnoreOrdering' when we have a QName tag are readable.
+
+ >>> assertXMLEqualIgnoreOrdering(
+ ... '<test><b>Content</b><a>Content</a></test>',
+ ... el)
+ Traceback (most recent call last):
+ ...
+ AssertionError: Failed to find the element 'b' in the fragment 'test'.
+
+ >>> assertXMLEqualIgnoreOrdering(el,
+ ... '<test><b>Content</b><a>Content</a></test>')
+ Traceback (most recent call last):
+ ...
+ AssertionError: Failed to find the element '{testns:}a' in the fragment 'test'.
+
+ Finally - test passing elementtree objects through the first arguement.
+
+ >>> elroot = etree.Element('test')
+ >>> eltree = etree.ElementTree(elroot)
+
+ >>> assertXMLEqual(eltree, '<test />')
+
+ >>> assertXMLEqual(eltree, etree.ElementTree(etree.Element('test')))
+
+ >>> assertXMLEqual(eltree, etree.Element('test'))
+
+ >>> assertXMLEqual(elroot, '<test />')
+
+ """
+ want, got = _cleanTree(want, got)
+
+ result, msg = _assertXMLElementEqual(want, got, XMLDATA_IGNOREORDER)
assert result, msg
#
@@ -365,10 +544,13 @@
XMLDATA = doctest.register_optionflag("XMLDATA")
+XMLDATA_IGNOREORDER = doctest.register_optionflag("XMLDATA_IGNOREORDER")
+
+
class XMLOutputChecker(doctest.OutputChecker):
def check_output(self, want, got, optionflags):
- if optionflags & XMLDATA:
+ if optionflags & XMLDATA or optionflags & XMLDATA_IGNOREORDER:
if want and got: # it only makes sense to compare actual data.
result, msg = _assertXMLElement(want, got, optionflags)
return result
@@ -376,7 +558,7 @@
self, want, got, optionflags)
def output_difference(self, example, got, optionflags):
- if optionflags & XMLDATA:
+ if optionflags & XMLDATA or optionflags & XMLDATA_IGNOREORDER:
want = example.want
if want and got: # it only makes sense to compare actual data
error = 'Expected:\n%sGot:\n%s' %(doctest._indent(want),
@@ -391,10 +573,12 @@
error += "No known XML difference."
return error
+ # XXX - not tested
return doctest.OutputChecker.output_difference(
self, example, got, optionflags)
xmlOutputChecker = XMLOutputChecker()
-__all__ = ("etreeSetup", "etreeTearDown", "assertXMLEqual",
- "xmlOutputChecker", "XMLDATA")
+__all__ = ("etreeSetup", "etreeTearDown",
+ "xmlOutputChecker", "assertXMLEqual", "XMLDATA",
+ "assertXMLEqualIgnoreOrdering", "XMLDATA_IGNOREORDER")
Modified: z3c.etree/trunk/src/z3c/etree/tests.py
===================================================================
--- z3c.etree/trunk/src/z3c/etree/tests.py 2008-02-07 20:27:50 UTC (rev 83652)
+++ z3c.etree/trunk/src/z3c/etree/tests.py 2008-02-07 20:34:23 UTC (rev 83653)
@@ -201,7 +201,7 @@
(re.compile('import pdb; pdb'), 'Pdb()'), # Py 2.3
# Omit the number of tests ran
- (re.compile(r'Ran \d tests with \d failures and \d errors'),
+ (re.compile(r'Ran \d+ tests with \d+ failures and \d+ errors'),
r'Ran N tests with N failures and N errors'),
])
More information about the Checkins
mailing list