| Index: commit-queue/tests/model_test.py
 | 
| ===================================================================
 | 
| --- commit-queue/tests/model_test.py	(revision 249146)
 | 
| +++ commit-queue/tests/model_test.py	(working copy)
 | 
| @@ -1,502 +0,0 @@
 | 
| -#!/usr/bin/env python
 | 
| -# Copyright (c) 2012 The Chromium Authors. All rights reserved.
 | 
| -# Use of this source code is governed by a BSD-style license that can be
 | 
| -# found in the LICENSE file.
 | 
| -
 | 
| -"""Unit tests for model.py."""
 | 
| -
 | 
| -import json
 | 
| -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 PersistentMixIn, TYPE_FLAG, immutable
 | 
| -
 | 
| -
 | 
| -# 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 Empty(PersistentMixIn):
 | 
| -  pass
 | 
| -
 | 
| -
 | 
| -class Basic(PersistentMixIn):
 | 
| -  a = int
 | 
| -  b = float
 | 
| -
 | 
| -  def test_me(self):
 | 
| -    return self.a + 1
 | 
| -
 | 
| -
 | 
| -class Inner(PersistentMixIn):
 | 
| -  c = Basic
 | 
| -  d = unicode
 | 
| -
 | 
| -
 | 
| -class Subclass(Inner):
 | 
| -  e = list
 | 
| -
 | 
| -
 | 
| -class MultiValue(PersistentMixIn):
 | 
| -  f = (None, bool)
 | 
| -  g = (unicode, float)
 | 
| -
 | 
| -
 | 
| -class WithInit(PersistentMixIn):
 | 
| -  h = unicode
 | 
| -  def __init__(self, **kwargs):
 | 
| -    # The values are overriden when loaded.
 | 
| -    kwargs.setdefault('h', u'baz')
 | 
| -    super(WithInit, self).__init__(**kwargs)
 | 
| -    # 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 TypeOrDict(PersistentMixIn):
 | 
| -  # Accepts a Basic or a dict.
 | 
| -  l = (Basic, dict)
 | 
| -
 | 
| -
 | 
| -class StrDisallowed(PersistentMixIn):
 | 
| -  m = str
 | 
| -
 | 
| -
 | 
| -def marshall(data):
 | 
| -  """JSON encodes then decodes to make sure the data has passed through JSON
 | 
| -  type reduction.
 | 
| -  """
 | 
| -  return json.loads(json.dumps(data))
 | 
| -
 | 
| -
 | 
| -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):
 | 
| -    data = Basic(b=23.2)
 | 
| -    expected = {
 | 
| -        'a': 0,
 | 
| -        'b': 23.2,
 | 
| -        TYPE_FLAG: 'Basic',
 | 
| -    }
 | 
| -    self.assertEqual(expected, data.as_dict())
 | 
| -
 | 
| -  def testBasicFailConstruct(self):
 | 
| -    # TODO(maruel): should int be auto-upgraded to float when requested?
 | 
| -    self.assertRaises(TypeError, Basic, b=23)
 | 
| -
 | 
| -  def testBasicFailAsDict(self):
 | 
| -    # TODO(maruel): should int be auto-upgraded to float when requested?
 | 
| -    data = Basic()
 | 
| -    data.b = 23
 | 
| -    self.assertRaises(TypeError, data.as_dict)
 | 
| -
 | 
| -  def testInner(self):
 | 
| -    data = Inner(c=Basic(a=21, b=23.2), d=u'foo')
 | 
| -    expected = {
 | 
| -      'c': {
 | 
| -        'a': 21,
 | 
| -        'b': 23.2,
 | 
| -        TYPE_FLAG: 'Basic',
 | 
| -      },
 | 
| -      TYPE_FLAG: 'Inner',
 | 
| -      'd': 'foo',
 | 
| -    }
 | 
| -    self.assertEqual(expected, data.as_dict())
 | 
| -
 | 
| -  def testSubclass(self):
 | 
| -    data = Subclass(c=Basic(a=23), e=[Basic(), {'random': 'stuff', 'x': True}])
 | 
| -    expected = {
 | 
| -      'c': {
 | 
| -          'a': 23,
 | 
| -          'b': 0.,
 | 
| -          TYPE_FLAG: 'Basic',
 | 
| -      },
 | 
| -      'e': [
 | 
| -        {
 | 
| -          'a': 0,
 | 
| -          'b': 0.,
 | 
| -          TYPE_FLAG: 'Basic',
 | 
| -        },
 | 
| -        {
 | 
| -          'random': 'stuff',
 | 
| -          'x': True,
 | 
| -        },
 | 
| -      ],
 | 
| -      'd': '',
 | 
| -      TYPE_FLAG: 'Subclass',
 | 
| -    }
 | 
| -    self.assertEqual(expected, data.as_dict())
 | 
| -
 | 
| -  def testMultiValue_default(self):
 | 
| -    data = MultiValue()
 | 
| -    expected = {
 | 
| -      'f': None,
 | 
| -      'g': '',
 | 
| -      TYPE_FLAG: 'MultiValue',
 | 
| -    }
 | 
| -    self.assertEqual(expected, data.as_dict())
 | 
| -
 | 
| -  def testMultiValue_first(self):
 | 
| -    data = MultiValue(f=None, g=u'foo')
 | 
| -    expected = {
 | 
| -      'f': None,
 | 
| -      'g': 'foo',
 | 
| -      TYPE_FLAG: 'MultiValue',
 | 
| -    }
 | 
| -    self.assertEqual(expected, data.as_dict())
 | 
| -
 | 
| -  def testMultiValue_second(self):
 | 
| -    data = MultiValue(f=False, g=3.1)
 | 
| -    expected = {
 | 
| -      'f': False,
 | 
| -      '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': u'bar'})
 | 
| -    self._check(data, TypeOrDict, l={'foo': u'bar'})
 | 
| -    expected = {
 | 
| -      'l': {
 | 
| -        'foo': 'bar',
 | 
| -      },
 | 
| -      TYPE_FLAG: 'TypeOrDict',
 | 
| -    }
 | 
| -    self.assertEqual(expected, data.as_dict())
 | 
| -
 | 
| -  def testStrDisallowed(self):
 | 
| -    self.assertRaises(TypeError, StrDisallowed)
 | 
| -
 | 
| -
 | 
| -class Deserialize(Base):
 | 
| -  def testNotFound(self):
 | 
| -    data = { TYPE_FLAG: 'DoesNotExists' }
 | 
| -    self.assertRaises(KeyError, PersistentMixIn.from_dict, marshall(data))
 | 
| -
 | 
| -  def testEmpty(self):
 | 
| -    data = { }
 | 
| -    self.assertRaises(KeyError, PersistentMixIn.from_dict, marshall(data))
 | 
| -
 | 
| -  def testBasic(self):
 | 
| -    data = {
 | 
| -        'a': 22,
 | 
| -        'b': 23.2,
 | 
| -        TYPE_FLAG: 'Basic',
 | 
| -    }
 | 
| -    actual = PersistentMixIn.from_dict(marshall(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, marshall(data))
 | 
| -
 | 
| -  def testInner(self):
 | 
| -    data = {
 | 
| -      'c': {
 | 
| -        'a': 42,
 | 
| -        'b': .1,
 | 
| -        TYPE_FLAG: 'Basic',
 | 
| -      },
 | 
| -      TYPE_FLAG: 'Inner',
 | 
| -      'd': 'foo2',
 | 
| -    }
 | 
| -    actual = PersistentMixIn.from_dict(marshall(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 = {
 | 
| -      'd': 'bar',
 | 
| -      'e': [
 | 
| -        {
 | 
| -          'a': 1,
 | 
| -          'b': 2.,
 | 
| -          TYPE_FLAG: 'Basic',
 | 
| -        },
 | 
| -        {
 | 
| -          'random': 'stuff',
 | 
| -          'x': True,
 | 
| -        },
 | 
| -      ],
 | 
| -      TYPE_FLAG: 'Subclass',
 | 
| -    }
 | 
| -    actual = PersistentMixIn.from_dict(marshall(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(2, len(actual.e))
 | 
| -    self._check(actual.e[0], Basic, a=1, b=2., test_me=IGNORE)
 | 
| -    self.assertEqual({'random': 'stuff', 'x': True}, actual.e[1])
 | 
| -
 | 
| -  def testMemberFunction(self):
 | 
| -    # Make sure the member functions are accessible.
 | 
| -    data = {
 | 
| -      TYPE_FLAG: 'Basic',
 | 
| -      'ignored': 'really',
 | 
| -    }
 | 
| -    actual = PersistentMixIn.from_dict(marshall(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(marshall(data))
 | 
| -    self._check(actual, MultiValue, f=None, g='')
 | 
| -
 | 
| -  def testMultiValue_first(self):
 | 
| -    data  = {
 | 
| -      'f': None,
 | 
| -      'g': 'foo',
 | 
| -      TYPE_FLAG: 'MultiValue',
 | 
| -    }
 | 
| -    actual = PersistentMixIn.from_dict(marshall(data))
 | 
| -    self._check(actual, MultiValue, f=None, g='foo')
 | 
| -
 | 
| -  def testMultiValue_second(self):
 | 
| -    data  = {
 | 
| -      'f': False,
 | 
| -      'g': 3.1,
 | 
| -      TYPE_FLAG: 'MultiValue',
 | 
| -    }
 | 
| -    actual = PersistentMixIn.from_dict(marshall(data))
 | 
| -    self._check(actual, MultiValue, f=False, g=3.1)
 | 
| -
 | 
| -  def testWithInit_default(self):
 | 
| -    data  = {
 | 
| -      TYPE_FLAG: 'WithInit',
 | 
| -    }
 | 
| -    actual = PersistentMixIn.from_dict(marshall(data))
 | 
| -    self._check(actual, WithInit, h='', i=3)
 | 
| -
 | 
| -  def testWithInit_values(self):
 | 
| -    data  = {
 | 
| -      'h': 'foo',
 | 
| -      'i': 4,
 | 
| -      TYPE_FLAG: 'WithInit',
 | 
| -    }
 | 
| -    actual = PersistentMixIn.from_dict(marshall(data))
 | 
| -    self._check(actual, WithInit, h='foo', i=3)
 | 
| -
 | 
| -  def testNotType(self):
 | 
| -    data  = {
 | 
| -      'j': ['a', 2],
 | 
| -      TYPE_FLAG: 'NotType',
 | 
| -    }
 | 
| -    actual = PersistentMixIn.from_dict(marshall(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(marshall(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(marshall(data))
 | 
| -    self._check(actual, TypeOrDict, l={'foo': 'bar'})
 | 
| -
 | 
| -  def testStrDisallowed(self):
 | 
| -    data  = {
 | 
| -      TYPE_FLAG: 'StrDisallowed',
 | 
| -    }
 | 
| -    self.assertRaises(TypeError, PersistentMixIn.from_dict, marshall(data))
 | 
| -
 | 
| -
 | 
| -class Mutable(object):
 | 
| -  def __init__(self, x):
 | 
| -    self.x = x
 | 
| -
 | 
| -  @immutable
 | 
| -  def func_call_immutable_deep(self):
 | 
| -    return self.func_call_immutable()
 | 
| -
 | 
| -  @immutable
 | 
| -  def func_call_immutable(self):
 | 
| -    return self.func_immutable(100)
 | 
| -
 | 
| -  @immutable
 | 
| -  def func_immutable(self, key):
 | 
| -    return self.x + key
 | 
| -
 | 
| -  @immutable
 | 
| -  def func_call_mutable(self):
 | 
| -    # Raises.
 | 
| -    return self.func_mutable(10)
 | 
| -
 | 
| -  def func_mutable(self, new_value):
 | 
| -    self.x = new_value
 | 
| -    return new_value
 | 
| -
 | 
| -  @immutable
 | 
| -  def func_try_mutate(self):
 | 
| -    # Raises.
 | 
| -    self.x = 18
 | 
| -
 | 
| -  @immutable
 | 
| -  def func_call_mutable_deep(self):
 | 
| -    # Raises
 | 
| -    return self.func_call_mutable()
 | 
| -
 | 
| -
 | 
| -class MutableStatic(object):
 | 
| -  @immutable
 | 
| -  def func_call_static(self):
 | 
| -    return self.func_static()
 | 
| -
 | 
| -  @staticmethod
 | 
| -  def func_static():
 | 
| -    return 'static'
 | 
| -
 | 
| -
 | 
| -class MutableDel(object):
 | 
| -  def __init__(self, x):
 | 
| -    self.x = x
 | 
| -
 | 
| -  @immutable
 | 
| -  def func_del(self):
 | 
| -    # Raises
 | 
| -    del self.x
 | 
| -
 | 
| -  def func_del_and_replace(self):
 | 
| -    x = self.x
 | 
| -    del self.x
 | 
| -    assert getattr(self, 'x', 'bleh') == 'bleh'
 | 
| -    self.x = x
 | 
| -    return self.x
 | 
| -
 | 
| -
 | 
| -class MutableProperty(object):
 | 
| -  def __init__(self, x):
 | 
| -    self.x = x
 | 
| -
 | 
| -  @property
 | 
| -  def property_y(self):
 | 
| -    return self.x + 1
 | 
| -
 | 
| -  @immutable
 | 
| -  def func_call_property(self):
 | 
| -    return self.property_y
 | 
| -
 | 
| -
 | 
| -class Immutable(unittest.TestCase):
 | 
| -  def testImmutable(self):
 | 
| -    obj = Mutable(2)
 | 
| -    # 2 + 23
 | 
| -    self.assertEqual(25, obj.func_immutable(23))
 | 
| -    # 2 + 100
 | 
| -    self.assertEqual(102, obj.func_call_immutable())
 | 
| -    # 2 + 100
 | 
| -    self.assertEqual(102, obj.func_call_immutable_deep())
 | 
| -    self.assertEqual(2, obj.x)
 | 
| -    self.assertEqual(23, obj.func_mutable(23))
 | 
| -    self.assertEqual(23, obj.x)
 | 
| -    self.assertRaises(TypeError, obj.func_call_mutable)
 | 
| -    self.assertRaises(TypeError, obj.func_try_mutate)
 | 
| -    self.assertRaises(TypeError, obj.func_call_mutable_deep)
 | 
| -
 | 
| -  def testImmutableStatic(self):
 | 
| -    obj = MutableStatic()
 | 
| -    self.assertEqual('static', obj.func_static())
 | 
| -    self.assertEqual('static', obj.func_call_static())
 | 
| -
 | 
| -  def testImmutableDel(self):
 | 
| -    obj = MutableDel(2)
 | 
| -    self.assertEqual(2, obj.func_del_and_replace())
 | 
| -    self.assertRaises(TypeError, obj.func_del)
 | 
| -    self.assertEqual(2, obj.x)
 | 
| -
 | 
| -  def testImmutableProperty(self):
 | 
| -    obj = MutableProperty(2)
 | 
| -    self.assertEqual(2, obj.x)
 | 
| -    self.assertEqual(3, obj.property_y)
 | 
| -    self.assertEqual(3, obj.func_call_property())
 | 
| -    self.assertEqual(2, obj.x)
 | 
| -
 | 
| -
 | 
| -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()
 | 
| 
 |