summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--keystone/common/serializer.py53
-rw-r--r--keystone/test.py3
-rw-r--r--tests/test_serializer.py76
3 files changed, 73 insertions, 59 deletions
diff --git a/keystone/common/serializer.py b/keystone/common/serializer.py
index e5f21a73..9b9df418 100644
--- a/keystone/common/serializer.py
+++ b/keystone/common/serializer.py
@@ -71,35 +71,10 @@ class XmlDeserializer(object):
def __call__(self, xml_str):
"""Returns a dictionary populated by decoding the given xml string."""
dom = etree.fromstring(xml_str.strip(), PARSER)
- links_json = self._find_and_remove_links_from_root(dom, True)
- obj_json = self.walk_element(dom, True)
- if links_json:
- obj_json['links'] = links_json['links']
- return obj_json
+ return self.walk_element(dom, True)
- def _deserialize_links(self, links, links_json):
- for link in links:
- links_json['links'][link.attrib['rel']] = link.attrib['href']
-
- def _find_and_remove_links_from_root(self, dom, namespace):
- """Special-case links element
-
- If "links" is in the elements, convert it and remove it from root
- element. "links" will be placed back into the root of the converted
- JSON object.
-
- """
- for element in dom:
- decoded_tag = XmlDeserializer._tag_name(element.tag, namespace)
- if decoded_tag == 'links':
- links_json = {'links': {}}
- self._deserialize_links(element, links_json)
- dom.remove(element)
- # TODO(gyee): are 'next' and 'previous' mandatory? If so,
- # setting them to None if they don't exist?
- links_json['links'].setdefault('previous')
- links_json['links'].setdefault('next')
- return links_json
+ def _deserialize_links(self, links):
+ return dict((x.attrib['rel'], x.attrib['href']) for x in links)
@staticmethod
def _tag_name(tag, namespace):
@@ -161,23 +136,31 @@ class XmlDeserializer(object):
else:
list_item_tag = decoded_tag[:-1]
- # links is a special dict
if decoded_tag == 'links':
- links_json = {'links': {}}
- self._deserialize_links(element, links_json)
- return links_json
+ return {'links': self._deserialize_links(element)}
+ links = None
for child in [self.walk_element(x) for x in element
if not isinstance(x, ENTITY_TYPE)]:
if list_item_tag:
- # FIXME(gyee): special-case lists for now unti we
+ # FIXME(gyee): special-case lists for now until we
# figure out how to properly handle them.
# If any key ends with an 's', we are assuming it is a list.
- values.append(child[list_item_tag])
+ if list_item_tag in child:
+ values.append(child[list_item_tag])
+ else:
+ links = child['links']
else:
values = dict(values.items() + child.items())
- return {XmlDeserializer._tag_name(element.tag, namespace): values}
+ d = {XmlDeserializer._tag_name(element.tag, namespace): values}
+
+ if links:
+ d['links'] = links
+ d['links'].setdefault('next')
+ d['links'].setdefault('previous')
+
+ return d
class XmlSerializer(object):
diff --git a/keystone/test.py b/keystone/test.py
index 21b1924d..3a28b5ff 100644
--- a/keystone/test.py
+++ b/keystone/test.py
@@ -190,6 +190,9 @@ class TestCase(NoModule, unittest.TestCase):
self._overrides = []
self._group_overrides = {}
+ # show complete diffs on failure
+ self.maxDiff = None
+
def setUp(self):
super(TestCase, self).setUp()
self.config([etcdir('keystone.conf.sample'),
diff --git a/tests/test_serializer.py b/tests/test_serializer.py
index 8ea49015..b440c815 100644
--- a/tests/test_serializer.py
+++ b/tests/test_serializer.py
@@ -15,44 +15,52 @@
# under the License.
import copy
-import re
+import StringIO
+
+from lxml import etree
from keystone.common import serializer
from keystone import test
class XmlSerializerTestCase(test.TestCase):
- def assertEqualIgnoreWhitespace(self, a, b):
- """Splits two strings into lists and compares them.
+ def assertEqualXML(self, a, b):
+ """Parses two XML documents from strings and compares the results.
This provides easy-to-read failures from nose.
"""
- try:
- self.assertEqual(a, b)
- except AssertionError:
- a = re.sub('[ \n]+', ' ', a).strip().split()
- b = re.sub('[ \n]+', ' ', b).strip().split()
- self.assertEqual(a, b)
+ parser = etree.XMLParser(remove_blank_text=True)
+
+ def canonical_xml(s):
+ s = s.strip()
+
+ fp = StringIO.StringIO()
+ dom = etree.fromstring(s, parser)
+ dom.getroottree().write_c14n(fp)
+ s = fp.getvalue()
+
+ dom = etree.fromstring(s, parser)
+ return etree.tostring(dom, pretty_print=True)
+
+ a = canonical_xml(a)
+ b = canonical_xml(b)
+ self.assertEqual(a.split('\n'), b.split('\n'))
def assertSerializeDeserialize(self, d, xml, xmlns=None):
- self.assertEqualIgnoreWhitespace(serializer.to_xml(d, xmlns), xml)
+ self.assertEqualXML(
+ serializer.to_xml(copy.deepcopy(d), xmlns),
+ xml)
self.assertEqual(serializer.from_xml(xml), d)
- # operations should be invertable
+ # operations should be invertible
self.assertEqual(
- serializer.from_xml(serializer.to_xml(d, xmlns)),
+ serializer.from_xml(serializer.to_xml(copy.deepcopy(d), xmlns)),
d)
- self.assertEqualIgnoreWhitespace(
+ self.assertEqualXML(
serializer.to_xml(serializer.from_xml(xml), xmlns),
xml)
- def test_none(self):
- d = None
- xml = None
-
- self.assertSerializeDeserialize(d, xml)
-
def test_auth_request(self):
d = {
"auth": {
@@ -155,7 +163,7 @@ class XmlSerializerTestCase(test.TestCase):
<policy id="ab12cd"/>
</policies>
"""
- self.assertEqualIgnoreWhitespace(serializer.to_xml(d), xml)
+ self.assertEqualXML(serializer.to_xml(d), xml)
def test_values_list(self):
d = {
@@ -176,7 +184,7 @@ class XmlSerializerTestCase(test.TestCase):
</objects>
"""
- self.assertEqualIgnoreWhitespace(serializer.to_xml(d), xml)
+ self.assertEqualXML(serializer.to_xml(d), xml)
def test_collection_list(self):
d = {
@@ -222,6 +230,26 @@ class XmlSerializerTestCase(test.TestCase):
</links>
</objects>
"""
- self.assertEqualIgnoreWhitespace(
- serializer.to_xml(copy.deepcopy(d)), xml)
- self.assertDictEqual(serializer.from_xml(xml), d)
+ self.assertSerializeDeserialize(d, xml)
+
+ def test_collection_member(self):
+ d = {
+ "object": {
+ "attribute": "value",
+ "links": {
+ "self": "http://localhost:5000/v3/objects/abc123def",
+ "anotherobj": "http://localhost:5000/v3/anotherobjs/123"}}}
+
+ xml = """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <object xmlns="http://docs.openstack.org/identity/api/v2.0"
+ attribute="value">
+ <links>
+ <link rel="self"
+ href="http://localhost:5000/v3/objects/abc123def"/>
+ <link rel="anotherobj"
+ href="http://localhost:5000/v3/anotherobjs/123"/>
+ </links>
+ </object>
+ """
+ self.assertSerializeDeserialize(d, xml)