| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Unit tests for model.py.""" | 6 """Unit tests for model.py.""" |
| 7 | 7 |
| 8 import logging |
| 8 import os | 9 import os |
| 9 import sys | 10 import sys |
| 10 import unittest | 11 import unittest |
| 11 | 12 |
| 12 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) | 13 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) |
| 13 sys.path.insert(0, os.path.join(ROOT_DIR, '..')) | 14 sys.path.insert(0, os.path.join(ROOT_DIR, '..')) |
| 14 from model import MODULE_FLAG, PersistentMixIn, TYPE_FLAG | 15 from model import PersistentMixIn, TYPE_FLAG |
| 15 | 16 |
| 16 | 17 |
| 17 class Invalid(PersistentMixIn): | 18 # Used a marker to determine that the check must ignore the value. |
| 19 IGNORE = object() |
| 20 |
| 21 |
| 22 def _members(instance): |
| 23 return sorted(i for i in dir(instance) if not i.startswith('_')) |
| 24 |
| 25 |
| 26 class Empty(PersistentMixIn): |
| 18 pass | 27 pass |
| 19 | 28 |
| 20 | 29 |
| 21 class MemberNotFound(PersistentMixIn): | |
| 22 persistent = ('a',) | |
| 23 | |
| 24 @staticmethod | |
| 25 def test_me(x): | |
| 26 return x + 1 | |
| 27 | |
| 28 | |
| 29 class Basic(PersistentMixIn): | 30 class Basic(PersistentMixIn): |
| 30 persistent = ('a', 'b') | 31 a = int |
| 31 a = None | 32 b = float |
| 32 b = None | 33 |
| 34 def test_me(self): |
| 35 return self.a + 1 |
| 33 | 36 |
| 34 | 37 |
| 35 class Inner(PersistentMixIn): | 38 class Inner(PersistentMixIn): |
| 36 persistent = ('c', 'd') | 39 c = Basic |
| 37 | 40 d = str |
| 41 |
| 42 |
| 43 class Subclass(Inner): |
| 44 e = list |
| 45 |
| 46 |
| 47 class MultiValue(PersistentMixIn): |
| 48 f = (None, int) |
| 49 g = (str, float) |
| 50 |
| 51 |
| 52 class WithInit(PersistentMixIn): |
| 53 h = str |
| 38 def __init__(self): | 54 def __init__(self): |
| 39 super(Inner, self).__init__() | 55 # The values are overriden when loaded. |
| 40 self.c = Basic() | 56 super(WithInit, self).__init__(h='baz') |
| 41 self.c.a = 'hello' | 57 # i is not serialized. |
| 42 self.d = 'foo' | 58 self.i = 3 |
| 43 self.extra = 'extra' | 59 |
| 44 | 60 |
| 45 | 61 class NotType(PersistentMixIn): |
| 46 class Serialize(unittest.TestCase): | 62 j = set |
| 47 def testInvalid(self): | 63 # k is not a type so it's not serialized. |
| 48 a = Invalid() | 64 k = 23 |
| 49 self.assertRaises(AssertionError, a.as_dict) | 65 |
| 50 | 66 |
| 51 def testMemberNotFound(self): | 67 class TypeOrDict(PersistentMixIn): |
| 52 a = MemberNotFound() | 68 # Accepts a Basic or a dict. |
| 53 self.assertRaises(AttributeError, a.as_dict) | 69 l = (Basic, dict) |
| 70 |
| 71 |
| 72 class Base(unittest.TestCase): |
| 73 def _check(self, actual, expected_type, **kwargs): |
| 74 kwargs['as_dict'] = IGNORE |
| 75 kwargs['from_dict'] = IGNORE |
| 76 self.assertEqual(expected_type, type(actual)) |
| 77 self.assertEqual(sorted(kwargs), _members(actual)) |
| 78 for member in sorted(kwargs): |
| 79 expected = kwargs[member] |
| 80 if expected == IGNORE: |
| 81 continue |
| 82 self.assertEqual(expected, getattr(actual, member)) |
| 83 |
| 84 |
| 85 class Serialize(Base): |
| 86 def testEmpty(self): |
| 87 expected = { |
| 88 TYPE_FLAG: 'Empty', |
| 89 } |
| 90 self.assertEqual(expected, Empty().as_dict()) |
| 54 | 91 |
| 55 def testBasic(self): | 92 def testBasic(self): |
| 56 a = Basic() | 93 data = Basic(b=23.2) |
| 57 a.b = 23.2 | 94 expected = { |
| 58 expected = { | 95 'a': 0, |
| 59 'a': None, | |
| 60 'b': 23.2, | 96 'b': 23.2, |
| 61 TYPE_FLAG: 'Basic', | 97 TYPE_FLAG: 'Basic', |
| 62 MODULE_FLAG: '__main__', | 98 } |
| 63 } | 99 self.assertEqual(expected, data.as_dict()) |
| 64 self.assertEquals(expected, a.as_dict()) | |
| 65 | 100 |
| 66 def testInner(self): | 101 def testInner(self): |
| 67 a = Inner() | 102 data = Inner(c=Basic(a=21, b=23.2), d='foo') |
| 68 expected = { | 103 expected = { |
| 69 'c': { | 104 'c': { |
| 70 'a': 'hello', | 105 'a': 21, |
| 71 'b': None, | 106 'b': 23.2, |
| 72 TYPE_FLAG: 'Basic', | 107 TYPE_FLAG: 'Basic', |
| 73 MODULE_FLAG: '__main__', | |
| 74 }, | 108 }, |
| 75 TYPE_FLAG: 'Inner', | 109 TYPE_FLAG: 'Inner', |
| 76 MODULE_FLAG: '__main__', | |
| 77 'd': 'foo', | 110 'd': 'foo', |
| 78 } | 111 } |
| 79 self.assertEquals(expected, a.as_dict()) | 112 self.assertEqual(expected, data.as_dict()) |
| 80 | 113 |
| 81 def testInnerList(self): | 114 def testSubclass(self): |
| 82 """Test serialization of: | 115 data = Subclass(c=Basic(a=23), e=[Basic()]) |
| 83 - Embedded objects | 116 expected = { |
| 84 - list | 117 'c': { |
| 85 - dict | 118 'a': 23, |
| 86 - string | 119 'b': 0., |
| 87 - int | 120 TYPE_FLAG: 'Basic', |
| 88 """ | 121 }, |
| 89 a = Basic() | 122 'e': [ |
| 90 a.b = [Basic(), 23] | |
| 91 a.a = {'x': 'y'} | |
| 92 expected = { | |
| 93 'a': {'x': 'y'}, | |
| 94 'b': [ | |
| 95 { | 123 { |
| 96 'a': None, | 124 'a': 0, |
| 97 'b': None, | 125 'b': 0., |
| 98 TYPE_FLAG: 'Basic', | 126 TYPE_FLAG: 'Basic', |
| 99 MODULE_FLAG: '__main__', | |
| 100 }, | 127 }, |
| 101 23 | |
| 102 ], | 128 ], |
| 103 TYPE_FLAG: 'Basic', | 129 'd': '', |
| 104 MODULE_FLAG: '__main__', | 130 TYPE_FLAG: 'Subclass', |
| 105 } | 131 } |
| 106 self.assertEquals(expected, a.as_dict()) | 132 self.assertEqual(expected, data.as_dict()) |
| 107 | 133 |
| 108 | 134 def testMultiValue_default(self): |
| 109 class Unas_dict(unittest.TestCase): | 135 data = MultiValue() |
| 110 def testInvalid(self): | 136 expected = { |
| 111 data = { TYPE_FLAG: 'Invalid' } | 137 'f': None, |
| 112 self.assertRaises(TypeError, PersistentMixIn.from_dict, data) | 138 'g': '', |
| 113 | 139 TYPE_FLAG: 'MultiValue', |
| 140 } |
| 141 self.assertEqual(expected, data.as_dict()) |
| 142 |
| 143 def testMultiValue_first(self): |
| 144 data = MultiValue(f=None, g='foo') |
| 145 expected = { |
| 146 'f': None, |
| 147 'g': 'foo', |
| 148 TYPE_FLAG: 'MultiValue', |
| 149 } |
| 150 self.assertEqual(expected, data.as_dict()) |
| 151 |
| 152 def testMultiValue_second(self): |
| 153 data = MultiValue(f=2, g=3.1) |
| 154 expected = { |
| 155 'f': 2, |
| 156 'g': 3.1, |
| 157 TYPE_FLAG: 'MultiValue', |
| 158 } |
| 159 self.assertEqual(expected, data.as_dict()) |
| 160 |
| 161 def testWithInit(self): |
| 162 data = WithInit() |
| 163 self._check(data, WithInit, h='baz', i=3) |
| 164 expected = { |
| 165 'h': 'baz', |
| 166 TYPE_FLAG: 'WithInit', |
| 167 } |
| 168 self.assertEqual(expected, data.as_dict()) |
| 169 |
| 170 def testNotType(self): |
| 171 data = NotType() |
| 172 self._check(data, NotType, j=set(), k=23) |
| 173 expected = { |
| 174 'j': [], |
| 175 TYPE_FLAG: 'NotType', |
| 176 } |
| 177 self.assertEqual(expected, data.as_dict()) |
| 178 |
| 179 def testTypeOrDict_Basic(self): |
| 180 data = TypeOrDict() |
| 181 self._check(data, TypeOrDict, l=IGNORE) |
| 182 self._check(data.l, Basic, a=0, b=0., test_me=IGNORE) |
| 183 expected = { |
| 184 'l': { |
| 185 'a': 0, |
| 186 'b': 0.0, |
| 187 TYPE_FLAG: 'Basic', |
| 188 }, |
| 189 TYPE_FLAG: 'TypeOrDict', |
| 190 } |
| 191 self.assertEqual(expected, data.as_dict()) |
| 192 |
| 193 def testTypeOrDict_dict(self): |
| 194 data = TypeOrDict(l={'foo': 'bar'}) |
| 195 self._check(data, TypeOrDict, l={'foo': 'bar'}) |
| 196 expected = { |
| 197 'l': { |
| 198 'foo': 'bar', |
| 199 }, |
| 200 TYPE_FLAG: 'TypeOrDict', |
| 201 } |
| 202 self.assertEqual(expected, data.as_dict()) |
| 203 |
| 204 |
| 205 |
| 206 class Deserialize(Base): |
| 114 def testNotFound(self): | 207 def testNotFound(self): |
| 115 data = { TYPE_FLAG: 'DoesNotExists' } | 208 data = { TYPE_FLAG: 'DoesNotExists' } |
| 116 self.assertRaises(KeyError, PersistentMixIn.from_dict, data) | 209 self.assertRaises(KeyError, PersistentMixIn.from_dict, data) |
| 117 | 210 |
| 118 def testEmpty(self): | 211 def testEmpty(self): |
| 119 data = { } | 212 data = { } |
| 120 self.assertRaises(KeyError, PersistentMixIn.from_dict, data) | 213 self.assertRaises(KeyError, PersistentMixIn.from_dict, data) |
| 121 | 214 |
| 122 def testBasic(self): | 215 def testBasic(self): |
| 123 data = { | 216 data = { |
| 124 'a': None, | 217 'a': 22, |
| 125 'b': 23.2, | 218 'b': 23.2, |
| 126 TYPE_FLAG: 'Basic', | 219 TYPE_FLAG: 'Basic', |
| 127 MODULE_FLAG: '__main__', | |
| 128 } | 220 } |
| 129 a = PersistentMixIn.from_dict(data) | 221 actual = PersistentMixIn.from_dict(data) |
| 130 self.assertEquals(Basic, type(a)) | 222 self._check(actual, Basic, a=22, b=23.2, test_me=IGNORE) |
| 131 self.assertEquals(None, a.a) | 223 |
| 132 self.assertEquals(23.2, a.b) | 224 def testBasic_WrongType(self): |
| 225 data = { |
| 226 'a': None, |
| 227 TYPE_FLAG: 'Basic', |
| 228 } |
| 229 self.assertRaises(TypeError, PersistentMixIn.from_dict, data) |
| 133 | 230 |
| 134 def testInner(self): | 231 def testInner(self): |
| 135 data = { | 232 data = { |
| 136 'c': { | 233 'c': { |
| 137 'a': 42, | 234 'a': 42, |
| 138 'b': [1, 2], | 235 'b': .1, |
| 139 TYPE_FLAG: 'Basic', | 236 TYPE_FLAG: 'Basic', |
| 140 MODULE_FLAG: '__main__', | |
| 141 }, | 237 }, |
| 142 TYPE_FLAG: 'Inner', | 238 TYPE_FLAG: 'Inner', |
| 143 MODULE_FLAG: '__main__', | |
| 144 'd': 'foo2', | 239 'd': 'foo2', |
| 145 } | 240 } |
| 146 a = PersistentMixIn.from_dict(data) | 241 actual = PersistentMixIn.from_dict(data) |
| 147 self.assertEquals(Inner, type(a)) | 242 self._check(actual, Inner, c=IGNORE, d='foo2') |
| 148 self.assertEquals(Basic, type(a.c)) | 243 self._check(actual.c, Basic, a=42, b=.1, test_me=IGNORE) |
| 149 self.assertEquals(42, a.c.a) | |
| 150 self.assertEquals([1, 2], a.c.b) | |
| 151 self.assertEquals('foo2', a.d) | |
| 152 # Make sure __init__ is not called. | |
| 153 self.assertFalse(hasattr(a, 'extra')) | |
| 154 | 244 |
| 155 def testInnerList(self): | 245 def testSubclass(self): |
| 156 """Test unserialization of: | |
| 157 - Embedded objects | |
| 158 - list | |
| 159 - dict | |
| 160 - string | |
| 161 - int | |
| 162 """ | |
| 163 data = { | 246 data = { |
| 164 'a': None, | 247 'd': 'bar', |
| 165 'b': [ | 248 'e': [ |
| 166 { | 249 { |
| 167 'a': {'x': 'y'}, | 250 'a': 1, |
| 168 'b': None, | 251 'b': 2., |
| 169 TYPE_FLAG: 'Basic', | 252 TYPE_FLAG: 'Basic', |
| 170 MODULE_FLAG: '__main__', | |
| 171 }, | 253 }, |
| 172 23 | |
| 173 ], | 254 ], |
| 174 TYPE_FLAG: 'Basic', | 255 TYPE_FLAG: 'Subclass', |
| 175 MODULE_FLAG: '__main__', | |
| 176 } | 256 } |
| 177 a = PersistentMixIn.from_dict(data) | 257 actual = PersistentMixIn.from_dict(data) |
| 178 self.assertEquals(Basic, type(a)) | 258 self._check(actual, Subclass, c=IGNORE, d='bar', e=IGNORE) |
| 179 self.assertEquals(None, a.a) | 259 self._check(actual.c, Basic, a=0, b=0., test_me=IGNORE) |
| 180 self.assertEquals(2, len(a.b)) | 260 self.assertEqual(list, type(actual.e)) |
| 181 self.assertEquals(Basic, type(a.b[0])) | 261 self.assertEqual(1, len(actual.e)) |
| 182 self.assertEquals({'x': 'y'}, a.b[0].a) | 262 self._check(actual.e[0], Basic, a=1, b=2., test_me=IGNORE) |
| 183 self.assertEquals(None, a.b[0].b) | |
| 184 self.assertEquals(23, a.b[1]) | |
| 185 | 263 |
| 186 def testMemberFunction(self): | 264 def testMemberFunction(self): |
| 265 # Make sure the member functions are accessible. |
| 187 data = { | 266 data = { |
| 188 TYPE_FLAG: 'MemberNotFound', | 267 TYPE_FLAG: 'Basic', |
| 268 'ignored': 'really', |
| 189 } | 269 } |
| 190 a = PersistentMixIn.from_dict(data) | 270 actual = PersistentMixIn.from_dict(data) |
| 191 self.assertEquals(MemberNotFound, type(a)) | 271 self._check(actual, Basic, a=0, b=0., test_me=IGNORE) |
| 192 # Make sure the member functions are accessible. | 272 self.assertEqual(1, actual.test_me()) |
| 193 self.assertEquals(3, a.test_me(2)) | 273 |
| 274 def testMultiValue_default(self): |
| 275 data = { |
| 276 TYPE_FLAG: 'MultiValue', |
| 277 } |
| 278 actual = PersistentMixIn.from_dict(data) |
| 279 self._check(actual, MultiValue, f=None, g='') |
| 280 |
| 281 def testMultiValue_first(self): |
| 282 data = { |
| 283 'f': None, |
| 284 'g': 'foo', |
| 285 TYPE_FLAG: 'MultiValue', |
| 286 } |
| 287 actual = PersistentMixIn.from_dict(data) |
| 288 self._check(actual, MultiValue, f=None, g='foo') |
| 289 |
| 290 def testMultiValue_second(self): |
| 291 data = { |
| 292 'f': 2, |
| 293 'g': 3.1, |
| 294 TYPE_FLAG: 'MultiValue', |
| 295 } |
| 296 actual = PersistentMixIn.from_dict(data) |
| 297 self._check(actual, MultiValue, f=2, g=3.1) |
| 298 |
| 299 def testWithInit_default(self): |
| 300 data = { |
| 301 TYPE_FLAG: 'WithInit', |
| 302 } |
| 303 actual = PersistentMixIn.from_dict(data) |
| 304 self._check(actual, WithInit, h='', i=3) |
| 305 |
| 306 def testWithInit_values(self): |
| 307 data = { |
| 308 'h': 'foo', |
| 309 'i': 4, |
| 310 TYPE_FLAG: 'WithInit', |
| 311 } |
| 312 actual = PersistentMixIn.from_dict(data) |
| 313 self._check(actual, WithInit, h='foo', i=3) |
| 314 |
| 315 def testNotType(self): |
| 316 data = { |
| 317 'j': ['a', 2], |
| 318 TYPE_FLAG: 'NotType', |
| 319 } |
| 320 actual = PersistentMixIn.from_dict(data) |
| 321 self._check(actual, NotType, j=set(['a', 2]), k=23) |
| 322 |
| 323 def testTypeOrDict_Basic(self): |
| 324 data = { |
| 325 'l': { |
| 326 'a': 3, |
| 327 'b': 4.0, |
| 328 TYPE_FLAG: 'Basic', |
| 329 }, |
| 330 TYPE_FLAG: 'TypeOrDict', |
| 331 } |
| 332 actual = PersistentMixIn.from_dict(data) |
| 333 self._check(actual, TypeOrDict, l=IGNORE) |
| 334 self._check(actual.l, Basic, a=3, b=4., test_me=IGNORE) |
| 335 |
| 336 def testTypeOrDict_dict(self): |
| 337 data = { |
| 338 'l': { |
| 339 'foo': 'bar', |
| 340 }, |
| 341 TYPE_FLAG: 'TypeOrDict', |
| 342 } |
| 343 actual = PersistentMixIn.from_dict(data) |
| 344 self._check(actual, TypeOrDict, l={'foo': 'bar'}) |
| 194 | 345 |
| 195 | 346 |
| 196 if __name__ == '__main__': | 347 if __name__ == '__main__': |
| 348 logging.basicConfig( |
| 349 level=logging.DEBUG if '-v' in sys.argv else logging.WARNING, |
| 350 format='%(levelname)5s %(module)15s(%(lineno)3d): %(message)s') |
| 197 unittest.main() | 351 unittest.main() |
| OLD | NEW |