summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-09-24 09:44:26 +0000
committerGerrit Code Review <review@openstack.org>2014-09-24 09:44:26 +0000
commitede83ef8d07546bf065b78ff7465f3e59735c834 (patch)
tree06d93e6ed87aa6bbc5004c90fdafe80378feb437
parent878f12e16017cf328bc91fe75b5db680e37df583 (diff)
parent18d48431aa65f64136d5308808de4f278606c07f (diff)
downloadkeystone-ede83ef8d07546bf065b78ff7465f3e59735c834.tar.gz
Merge "Fixes an issue with the XMLEquals matcher" into stable/icehouse
-rw-r--r--keystone/tests/matchers.py63
-rw-r--r--keystone/tests/test_matchers.py63
2 files changed, 80 insertions, 46 deletions
diff --git a/keystone/tests/matchers.py b/keystone/tests/matchers.py
index f904d339c..6076f348e 100644
--- a/keystone/tests/matchers.py
+++ b/keystone/tests/matchers.py
@@ -12,8 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import six
-
from lxml import etree
from testtools import matchers
@@ -30,24 +28,50 @@ class XMLEquals(object):
return "%s(%r)" % (self.__class__.__name__, self.expected)
def match(self, other):
- parser = etree.XMLParser(remove_blank_text=True)
+ def xml_element_equals(expected_doc, observed_doc):
+ """Tests whether two XML documents are equivalent.
+
+ This is a recursive algorithm that operates on each element in
+ the hierarchy. Siblings are sorted before being checked to
+ account for two semantically equivalent documents where siblings
+ appear in different document order.
+
+ The sorting algorithm is a little weak in that it could fail for
+ documents where siblings at a given level are the same, but have
+ different children.
+
+ """
+
+ if expected_doc.tag != observed_doc.tag:
+ return False
- def canonical_xml(s):
- s = s.strip()
+ if expected_doc.attrib != observed_doc.attrib:
+ return False
- fp = six.StringIO()
- dom = etree.fromstring(s, parser)
- dom.getroottree().write_c14n(fp)
- s = fp.getvalue()
+ def _sorted_children(doc):
+ return sorted(doc.getchildren(), key=lambda el: el.tag)
- dom = etree.fromstring(s, parser)
- return etree.tostring(dom, pretty_print=True)
+ expected_children = _sorted_children(expected_doc)
+ observed_children = _sorted_children(observed_doc)
- expected = canonical_xml(self.expected)
- other = canonical_xml(other)
- if expected == other:
+ if len(expected_children) != len(observed_children):
+ return False
+
+ for expected_el, observed_el in zip(expected_children,
+ observed_children):
+ if not xml_element_equals(expected_el, observed_el):
+ return False
+
+ return True
+
+ parser = etree.XMLParser(remove_blank_text=True)
+ expected_doc = etree.fromstring(self.expected.strip(), parser)
+ observed_doc = etree.fromstring(other.strip(), parser)
+
+ if xml_element_equals(expected_doc, observed_doc):
return
- return XMLMismatch(expected, other)
+
+ return XMLMismatch(self.expected, other)
class XMLMismatch(matchers.Mismatch):
@@ -57,4 +81,11 @@ class XMLMismatch(matchers.Mismatch):
self.other = other
def describe(self):
- return 'expected = %s\nactual = %s' % (self.expected, self.other)
+ def pretty_xml(xml):
+ parser = etree.XMLParser(remove_blank_text=True)
+ doc = etree.fromstring(xml.strip(), parser)
+ return (etree.tostring(doc, encoding='utf-8', pretty_print=True)
+ .decode('utf-8'))
+
+ return 'expected =\n%s\nactual =\n%s' % (
+ pretty_xml(self.expected), pretty_xml(self.other))
diff --git a/keystone/tests/test_matchers.py b/keystone/tests/test_matchers.py
index b1c324a2d..e9413a1f6 100644
--- a/keystone/tests/test_matchers.py
+++ b/keystone/tests/test_matchers.py
@@ -12,42 +12,45 @@
# License for the specific language governing permissions and limitations
# under the License.
-import textwrap
-
-import testtools
from testtools.tests.matchers import helpers
+from keystone import tests
from keystone.tests import matchers
-class TestXMLEquals(testtools.TestCase, helpers.TestMatchersInterface):
- matches_xml = """
- <?xml version="1.0" encoding="UTF-8"?>
- <test xmlns="http://docs.openstack.org/identity/api/v2.0">
- <success a="a" b="b"/>
- </test>
- """
- equivalent_xml = """
- <?xml version="1.0" encoding="UTF-8"?>
- <test xmlns="http://docs.openstack.org/identity/api/v2.0">
- <success b="b" a="a"></success>
- </test>
- """
- mismatches_xml = """
- <?xml version="1.0" encoding="UTF-8"?>
- <test xmlns="http://docs.openstack.org/identity/api/v2.0">
- <nope_it_fails/>
- </test>
- """
- mismatches_description = textwrap.dedent("""\
- expected = <test xmlns="http://docs.openstack.org/identity/api/v2.0">
- <success a="a" b="b"/>
- </test>
+class TestXMLEquals(tests.BaseTestCase, helpers.TestMatchersInterface):
+ matches_xml = b"""\
+<?xml version="1.0" encoding="UTF-8"?>
+<test xmlns="http://docs.openstack.org/identity/api/v2.0">
+ <first z="0" y="1" x="2"/>
+ <second a="a" b="b"></second>
+</test>
+"""
+ equivalent_xml = b"""\
+<?xml version="1.0" encoding="UTF-8"?>
+<test xmlns="http://docs.openstack.org/identity/api/v2.0">
+ <second a="a" b="b"/>
+ <first z="0" y="1" x="2"></first>
+</test>
+"""
+ mismatches_xml = b"""\
+<?xml version="1.0" encoding="UTF-8"?>
+<test xmlns="http://docs.openstack.org/identity/api/v2.0">
+ <nope_it_fails/>
+</test>
+"""
+ mismatches_description = """\
+expected =
+<test xmlns="http://docs.openstack.org/identity/api/v2.0">
+ <first z="0" y="1" x="2"/>
+ <second a="a" b="b"/>
+</test>
- actual = <test xmlns="http://docs.openstack.org/identity/api/v2.0">
- <nope_it_fails/>
- </test>
- """).lstrip()
+actual =
+<test xmlns="http://docs.openstack.org/identity/api/v2.0">
+ <nope_it_fails/>
+</test>
+"""
matches_matcher = matchers.XMLEquals(matches_xml)
matches_matches = [matches_xml, equivalent_xml]