| Index: tests/model_test.py
|
| diff --git a/tests/model_test.py b/tests/model_test.py
|
| index 3b9b711bf9d33e2f8dad98a00d92fd77f616d12b..b8bad0003c2231dbad369f61ae57986ea6f29dd4 100755
|
| --- a/tests/model_test.py
|
| +++ b/tests/model_test.py
|
| @@ -5,112 +5,205 @@
|
|
|
| """Unit tests for model.py."""
|
|
|
| +import logging
|
| import os
|
| import sys
|
| import unittest
|
|
|
| ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
| sys.path.insert(0, os.path.join(ROOT_DIR, '..'))
|
| -from model import MODULE_FLAG, PersistentMixIn, TYPE_FLAG
|
| +from model import PersistentMixIn, TYPE_FLAG
|
|
|
|
|
| -class Invalid(PersistentMixIn):
|
| - pass
|
| +# Used a marker to determine that the check must ignore the value.
|
| +IGNORE = object()
|
| +
|
|
|
| +def _members(instance):
|
| + return sorted(i for i in dir(instance) if not i.startswith('_'))
|
|
|
| -class MemberNotFound(PersistentMixIn):
|
| - persistent = ('a',)
|
|
|
| - @staticmethod
|
| - def test_me(x):
|
| - return x + 1
|
| +class Empty(PersistentMixIn):
|
| + pass
|
|
|
|
|
| class Basic(PersistentMixIn):
|
| - persistent = ('a', 'b')
|
| - a = None
|
| - b = None
|
| + a = int
|
| + b = float
|
| +
|
| + def test_me(self):
|
| + return self.a + 1
|
|
|
|
|
| class Inner(PersistentMixIn):
|
| - persistent = ('c', 'd')
|
| + c = Basic
|
| + d = str
|
| +
|
| +
|
| +class Subclass(Inner):
|
| + e = list
|
|
|
| +
|
| +class MultiValue(PersistentMixIn):
|
| + f = (None, int)
|
| + g = (str, float)
|
| +
|
| +
|
| +class WithInit(PersistentMixIn):
|
| + h = str
|
| def __init__(self):
|
| - super(Inner, self).__init__()
|
| - self.c = Basic()
|
| - self.c.a = 'hello'
|
| - self.d = 'foo'
|
| - self.extra = 'extra'
|
| + # The values are overriden when loaded.
|
| + super(WithInit, self).__init__(h='baz')
|
| + # i is not serialized.
|
| + self.i = 3
|
| +
|
| +
|
| +class NotType(PersistentMixIn):
|
| + j = set
|
| + # k is not a type so it's not serialized.
|
| + k = 23
|
|
|
|
|
| -class Serialize(unittest.TestCase):
|
| - def testInvalid(self):
|
| - a = Invalid()
|
| - self.assertRaises(AssertionError, a.as_dict)
|
| +class TypeOrDict(PersistentMixIn):
|
| + # Accepts a Basic or a dict.
|
| + l = (Basic, dict)
|
|
|
| - def testMemberNotFound(self):
|
| - a = MemberNotFound()
|
| - self.assertRaises(AttributeError, a.as_dict)
|
| +
|
| +class Base(unittest.TestCase):
|
| + def _check(self, actual, expected_type, **kwargs):
|
| + kwargs['as_dict'] = IGNORE
|
| + kwargs['from_dict'] = IGNORE
|
| + self.assertEqual(expected_type, type(actual))
|
| + self.assertEqual(sorted(kwargs), _members(actual))
|
| + for member in sorted(kwargs):
|
| + expected = kwargs[member]
|
| + if expected == IGNORE:
|
| + continue
|
| + self.assertEqual(expected, getattr(actual, member))
|
| +
|
| +
|
| +class Serialize(Base):
|
| + def testEmpty(self):
|
| + expected = {
|
| + TYPE_FLAG: 'Empty',
|
| + }
|
| + self.assertEqual(expected, Empty().as_dict())
|
|
|
| def testBasic(self):
|
| - a = Basic()
|
| - a.b = 23.2
|
| + data = Basic(b=23.2)
|
| expected = {
|
| - 'a': None,
|
| + 'a': 0,
|
| 'b': 23.2,
|
| TYPE_FLAG: 'Basic',
|
| - MODULE_FLAG: '__main__',
|
| }
|
| - self.assertEquals(expected, a.as_dict())
|
| + self.assertEqual(expected, data.as_dict())
|
|
|
| def testInner(self):
|
| - a = Inner()
|
| + data = Inner(c=Basic(a=21, b=23.2), d='foo')
|
| expected = {
|
| 'c': {
|
| - 'a': 'hello',
|
| - 'b': None,
|
| + 'a': 21,
|
| + 'b': 23.2,
|
| TYPE_FLAG: 'Basic',
|
| - MODULE_FLAG: '__main__',
|
| },
|
| TYPE_FLAG: 'Inner',
|
| - MODULE_FLAG: '__main__',
|
| 'd': 'foo',
|
| }
|
| - self.assertEquals(expected, a.as_dict())
|
| -
|
| - def testInnerList(self):
|
| - """Test serialization of:
|
| - - Embedded objects
|
| - - list
|
| - - dict
|
| - - string
|
| - - int
|
| - """
|
| - a = Basic()
|
| - a.b = [Basic(), 23]
|
| - a.a = {'x': 'y'}
|
| + self.assertEqual(expected, data.as_dict())
|
| +
|
| + def testSubclass(self):
|
| + data = Subclass(c=Basic(a=23), e=[Basic()])
|
| expected = {
|
| - 'a': {'x': 'y'},
|
| - 'b': [
|
| + 'c': {
|
| + 'a': 23,
|
| + 'b': 0.,
|
| + TYPE_FLAG: 'Basic',
|
| + },
|
| + 'e': [
|
| {
|
| - 'a': None,
|
| - 'b': None,
|
| + 'a': 0,
|
| + 'b': 0.,
|
| TYPE_FLAG: 'Basic',
|
| - MODULE_FLAG: '__main__',
|
| },
|
| - 23
|
| ],
|
| - TYPE_FLAG: 'Basic',
|
| - MODULE_FLAG: '__main__',
|
| + 'd': '',
|
| + TYPE_FLAG: 'Subclass',
|
| + }
|
| + self.assertEqual(expected, data.as_dict())
|
| +
|
| + def testMultiValue_default(self):
|
| + data = MultiValue()
|
| + expected = {
|
| + 'f': None,
|
| + 'g': '',
|
| + TYPE_FLAG: 'MultiValue',
|
| }
|
| - self.assertEquals(expected, a.as_dict())
|
| + self.assertEqual(expected, data.as_dict())
|
|
|
| + def testMultiValue_first(self):
|
| + data = MultiValue(f=None, g='foo')
|
| + expected = {
|
| + 'f': None,
|
| + 'g': 'foo',
|
| + TYPE_FLAG: 'MultiValue',
|
| + }
|
| + self.assertEqual(expected, data.as_dict())
|
|
|
| -class Unas_dict(unittest.TestCase):
|
| - def testInvalid(self):
|
| - data = { TYPE_FLAG: 'Invalid' }
|
| - self.assertRaises(TypeError, PersistentMixIn.from_dict, data)
|
| + def testMultiValue_second(self):
|
| + data = MultiValue(f=2, g=3.1)
|
| + expected = {
|
| + 'f': 2,
|
| + 'g': 3.1,
|
| + TYPE_FLAG: 'MultiValue',
|
| + }
|
| + self.assertEqual(expected, data.as_dict())
|
| +
|
| + def testWithInit(self):
|
| + data = WithInit()
|
| + self._check(data, WithInit, h='baz', i=3)
|
| + expected = {
|
| + 'h': 'baz',
|
| + TYPE_FLAG: 'WithInit',
|
| + }
|
| + self.assertEqual(expected, data.as_dict())
|
| +
|
| + def testNotType(self):
|
| + data = NotType()
|
| + self._check(data, NotType, j=set(), k=23)
|
| + expected = {
|
| + 'j': [],
|
| + TYPE_FLAG: 'NotType',
|
| + }
|
| + self.assertEqual(expected, data.as_dict())
|
|
|
| + def testTypeOrDict_Basic(self):
|
| + data = TypeOrDict()
|
| + self._check(data, TypeOrDict, l=IGNORE)
|
| + self._check(data.l, Basic, a=0, b=0., test_me=IGNORE)
|
| + expected = {
|
| + 'l': {
|
| + 'a': 0,
|
| + 'b': 0.0,
|
| + TYPE_FLAG: 'Basic',
|
| + },
|
| + TYPE_FLAG: 'TypeOrDict',
|
| + }
|
| + self.assertEqual(expected, data.as_dict())
|
| +
|
| + def testTypeOrDict_dict(self):
|
| + data = TypeOrDict(l={'foo': 'bar'})
|
| + self._check(data, TypeOrDict, l={'foo': 'bar'})
|
| + expected = {
|
| + 'l': {
|
| + 'foo': 'bar',
|
| + },
|
| + TYPE_FLAG: 'TypeOrDict',
|
| + }
|
| + self.assertEqual(expected, data.as_dict())
|
| +
|
| +
|
| +
|
| +class Deserialize(Base):
|
| def testNotFound(self):
|
| data = { TYPE_FLAG: 'DoesNotExists' }
|
| self.assertRaises(KeyError, PersistentMixIn.from_dict, data)
|
| @@ -121,77 +214,138 @@ class Unas_dict(unittest.TestCase):
|
|
|
| def testBasic(self):
|
| data = {
|
| - 'a': None,
|
| + 'a': 22,
|
| 'b': 23.2,
|
| TYPE_FLAG: 'Basic',
|
| - MODULE_FLAG: '__main__',
|
| }
|
| - a = PersistentMixIn.from_dict(data)
|
| - self.assertEquals(Basic, type(a))
|
| - self.assertEquals(None, a.a)
|
| - self.assertEquals(23.2, a.b)
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, Basic, a=22, b=23.2, test_me=IGNORE)
|
| +
|
| + def testBasic_WrongType(self):
|
| + data = {
|
| + 'a': None,
|
| + TYPE_FLAG: 'Basic',
|
| + }
|
| + self.assertRaises(TypeError, PersistentMixIn.from_dict, data)
|
|
|
| def testInner(self):
|
| data = {
|
| 'c': {
|
| 'a': 42,
|
| - 'b': [1, 2],
|
| + 'b': .1,
|
| TYPE_FLAG: 'Basic',
|
| - MODULE_FLAG: '__main__',
|
| },
|
| TYPE_FLAG: 'Inner',
|
| - MODULE_FLAG: '__main__',
|
| 'd': 'foo2',
|
| }
|
| - a = PersistentMixIn.from_dict(data)
|
| - self.assertEquals(Inner, type(a))
|
| - self.assertEquals(Basic, type(a.c))
|
| - self.assertEquals(42, a.c.a)
|
| - self.assertEquals([1, 2], a.c.b)
|
| - self.assertEquals('foo2', a.d)
|
| - # Make sure __init__ is not called.
|
| - self.assertFalse(hasattr(a, 'extra'))
|
| -
|
| - def testInnerList(self):
|
| - """Test unserialization of:
|
| - - Embedded objects
|
| - - list
|
| - - dict
|
| - - string
|
| - - int
|
| - """
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, Inner, c=IGNORE, d='foo2')
|
| + self._check(actual.c, Basic, a=42, b=.1, test_me=IGNORE)
|
| +
|
| + def testSubclass(self):
|
| data = {
|
| - 'a': None,
|
| - 'b': [
|
| + 'd': 'bar',
|
| + 'e': [
|
| {
|
| - 'a': {'x': 'y'},
|
| - 'b': None,
|
| + 'a': 1,
|
| + 'b': 2.,
|
| TYPE_FLAG: 'Basic',
|
| - MODULE_FLAG: '__main__',
|
| },
|
| - 23
|
| ],
|
| - TYPE_FLAG: 'Basic',
|
| - MODULE_FLAG: '__main__',
|
| + TYPE_FLAG: 'Subclass',
|
| }
|
| - a = PersistentMixIn.from_dict(data)
|
| - self.assertEquals(Basic, type(a))
|
| - self.assertEquals(None, a.a)
|
| - self.assertEquals(2, len(a.b))
|
| - self.assertEquals(Basic, type(a.b[0]))
|
| - self.assertEquals({'x': 'y'}, a.b[0].a)
|
| - self.assertEquals(None, a.b[0].b)
|
| - self.assertEquals(23, a.b[1])
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, Subclass, c=IGNORE, d='bar', e=IGNORE)
|
| + self._check(actual.c, Basic, a=0, b=0., test_me=IGNORE)
|
| + self.assertEqual(list, type(actual.e))
|
| + self.assertEqual(1, len(actual.e))
|
| + self._check(actual.e[0], Basic, a=1, b=2., test_me=IGNORE)
|
|
|
| def testMemberFunction(self):
|
| + # Make sure the member functions are accessible.
|
| data = {
|
| - TYPE_FLAG: 'MemberNotFound',
|
| + TYPE_FLAG: 'Basic',
|
| + 'ignored': 'really',
|
| }
|
| - a = PersistentMixIn.from_dict(data)
|
| - self.assertEquals(MemberNotFound, type(a))
|
| - # Make sure the member functions are accessible.
|
| - self.assertEquals(3, a.test_me(2))
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, Basic, a=0, b=0., test_me=IGNORE)
|
| + self.assertEqual(1, actual.test_me())
|
| +
|
| + def testMultiValue_default(self):
|
| + data = {
|
| + TYPE_FLAG: 'MultiValue',
|
| + }
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, MultiValue, f=None, g='')
|
| +
|
| + def testMultiValue_first(self):
|
| + data = {
|
| + 'f': None,
|
| + 'g': 'foo',
|
| + TYPE_FLAG: 'MultiValue',
|
| + }
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, MultiValue, f=None, g='foo')
|
| +
|
| + def testMultiValue_second(self):
|
| + data = {
|
| + 'f': 2,
|
| + 'g': 3.1,
|
| + TYPE_FLAG: 'MultiValue',
|
| + }
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, MultiValue, f=2, g=3.1)
|
| +
|
| + def testWithInit_default(self):
|
| + data = {
|
| + TYPE_FLAG: 'WithInit',
|
| + }
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, WithInit, h='', i=3)
|
| +
|
| + def testWithInit_values(self):
|
| + data = {
|
| + 'h': 'foo',
|
| + 'i': 4,
|
| + TYPE_FLAG: 'WithInit',
|
| + }
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, WithInit, h='foo', i=3)
|
| +
|
| + def testNotType(self):
|
| + data = {
|
| + 'j': ['a', 2],
|
| + TYPE_FLAG: 'NotType',
|
| + }
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, NotType, j=set(['a', 2]), k=23)
|
| +
|
| + def testTypeOrDict_Basic(self):
|
| + data = {
|
| + 'l': {
|
| + 'a': 3,
|
| + 'b': 4.0,
|
| + TYPE_FLAG: 'Basic',
|
| + },
|
| + TYPE_FLAG: 'TypeOrDict',
|
| + }
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, TypeOrDict, l=IGNORE)
|
| + self._check(actual.l, Basic, a=3, b=4., test_me=IGNORE)
|
| +
|
| + def testTypeOrDict_dict(self):
|
| + data = {
|
| + 'l': {
|
| + 'foo': 'bar',
|
| + },
|
| + TYPE_FLAG: 'TypeOrDict',
|
| + }
|
| + actual = PersistentMixIn.from_dict(data)
|
| + self._check(actual, TypeOrDict, l={'foo': 'bar'})
|
|
|
|
|
| if __name__ == '__main__':
|
| + logging.basicConfig(
|
| + level=logging.DEBUG if '-v' in sys.argv else logging.WARNING,
|
| + format='%(levelname)5s %(module)15s(%(lineno)3d): %(message)s')
|
| unittest.main()
|
|
|