oslo-incubator/tests/unit/test_jsonutils.py
Russell Bryant 2d6f84742a Track to_primitive() depth after iteritems().
Change jsonutils.to_primitive() to increase the recursion depth counter
when calling to_primitive() on the result of iteritems() from the
current element.  Previously, the only time the counter was increased
was when converting the __dict__ from an object.  The iteritems() case
risks cycles, as well.

I hit a problem with this when trying to call to_primitive on an
instance of nova.db.sqlalchemy.models.Instance.  An Instance includes a
reference to InstanceInfoCache, which has a reference back to the
Instance.  Without this change, to_primitive() would raise an exception
for an Instance due to excessive recursion.

Related to nova blueprint no-db-messaging.

Change-Id: Ifb878368d97e92ab6c361a4dd5f5ab2e68fc16e2
2012-07-18 16:15:52 -04:00

142 lines
4.3 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import unittest
import xmlrpclib
import StringIO
from openstack.common import jsonutils
class JSONUtilsTestCase(unittest.TestCase):
def test_dumps(self):
self.assertEqual(jsonutils.dumps({'a': 'b'}), '{"a": "b"}')
def test_loads(self):
self.assertEqual(jsonutils.loads('{"a": "b"}'), {'a': 'b'})
def test_load(self):
x = StringIO.StringIO('{"a": "b"}')
self.assertEqual(jsonutils.load(x), {'a': 'b'})
class ToPrimitiveTestCase(unittest.TestCase):
def test_list(self):
self.assertEquals(jsonutils.to_primitive([1, 2, 3]), [1, 2, 3])
def test_empty_list(self):
self.assertEquals(jsonutils.to_primitive([]), [])
def test_tuple(self):
self.assertEquals(jsonutils.to_primitive((1, 2, 3)), [1, 2, 3])
def test_dict(self):
self.assertEquals(jsonutils.to_primitive(dict(a=1, b=2, c=3)),
dict(a=1, b=2, c=3))
def test_empty_dict(self):
self.assertEquals(jsonutils.to_primitive({}), {})
def test_datetime(self):
x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
self.assertEquals(jsonutils.to_primitive(x),
'1920-02-03T04:05:06.000007')
def test_DateTime(self):
x = xmlrpclib.DateTime()
x.decode("19710203T04:05:06")
self.assertEquals(jsonutils.to_primitive(x),
'1971-02-03T04:05:06.000000')
def test_iter(self):
class IterClass(object):
def __init__(self):
self.data = [1, 2, 3, 4, 5]
self.index = 0
def __iter__(self):
return self
def next(self):
if self.index == len(self.data):
raise StopIteration
self.index = self.index + 1
return self.data[self.index - 1]
x = IterClass()
self.assertEquals(jsonutils.to_primitive(x), [1, 2, 3, 4, 5])
def test_iteritems(self):
class IterItemsClass(object):
def __init__(self):
self.data = dict(a=1, b=2, c=3).items()
self.index = 0
def iteritems(self):
return self.data
x = IterItemsClass()
p = jsonutils.to_primitive(x)
self.assertEquals(p, {'a': 1, 'b': 2, 'c': 3})
def test_iteritems_with_cycle(self):
class IterItemsClass(object):
def __init__(self):
self.data = dict(a=1, b=2, c=3)
self.index = 0
def iteritems(self):
return self.data.items()
x = IterItemsClass()
x2 = IterItemsClass()
x.data['other'] = x2
x2.data['other'] = x
# If the cycle isn't caught, to_primitive() will eventually result in
# an exception due to excessive recursion depth.
p = jsonutils.to_primitive(x)
def test_instance(self):
class MysteryClass(object):
a = 10
def __init__(self):
self.b = 1
x = MysteryClass()
self.assertEquals(jsonutils.to_primitive(x, convert_instances=True),
dict(b=1))
self.assertEquals(jsonutils.to_primitive(x), x)
def test_typeerror(self):
x = bytearray # Class, not instance
self.assertEquals(jsonutils.to_primitive(x), u"<type 'bytearray'>")
def test_nasties(self):
def foo():
pass
x = [datetime, foo, dir]
ret = jsonutils.to_primitive(x)
self.assertEquals(len(ret), 3)
self.assertTrue(ret[0].startswith(u"<module 'datetime' from "))
self.assertTrue(ret[1].startswith('<function foo at 0x'))
self.assertEquals(ret[2], '<built-in function dir>')