"""test_typetools.py - Unit tests for the typetools module """ # Written by Nick Coghlan # Copyright (C) 2008 Python Software Foundation. import unittest from test import test_support import operator as ops import typetools COMPARISON_OPS = (ops.eq, ops.ge, ops.gt, ops.le, ops.lt, ops.ne) # Test delegation of operations class TestProxyMixin(unittest.TestCase): """Test delegation of assorted operations by ProxyMixin""" TEST_CLASS = typetools.ProxyMixin UNARY_OPERANDS = [1, 1.0, set('ab'), lambda:42] UNARY_OPERATIONS = [str, unicode, hex, oct, bool, hash, ops.neg, ops.pos, ops.abs, ops.invert, complex, int, float, long, ops.index, ops.truth] BINARY_OPERANDS = [(42, 42), (1, 2), (1.0, 2.0), (1.0, 2), (1, 2.0), ('ab', 'abcd'), ('ab', 5), (5, 'ab'), (set('ab'), set('abcd'))] BINARY_OPERATIONS = [ops.add, ops.and_, ops.div, ops.eq, ops.floordiv, ops.ge, ops.gt, ops.le, ops.lshift, ops.lt, ops.mod, ops.mul, ops.ne, ops.neg, ops.not_, ops.or_, ops.pos, ops.pow, ops.repeat, ops.rshift, ops.sub, ops.truediv, ops.xor] IN_PLACE_OPERATIONS = [ops.iadd, ops.iand, ops.iconcat, ops.idiv, ops.ifloordiv, ops.ilshift, ops.imod, ops.imul, ops.ior, ops.ipow, ops.irepeat, ops.irshift, ops.isub, ops.itruediv, ops.ixor] def _check_unary_op(self, op): for x in self.UNARY_OPERANDS: try: expected = op(x) except: pass else: msg_format = "Op: %s, Arg: %r, Got: %r Expected: %r" px = self.TEST_CLASS(x) actual = op(px) self.assertEqual(actual, expected, msg_format % (op.__name__, px, actual, expected)) def _check_binary_op(self, op): # For binary operations, implicit # unwrapping should work properly # regardless of whether the proxy # object is the left operand or # the right operand for x, y in self.BINARY_OPERANDS: try: expected = op(x, y) except: pass else: msg_format = "Op: %s, LHS: %r, RHS: %r, Got: %r, Expected: %r" def _check(lhs, rhs): actual = op(lhs, rhs) self.assertEqual(actual, expected, msg_format % (op.__name__, lhs, rhs, actual, expected)) px = self.TEST_CLASS(x) py = self.TEST_CLASS(y) # Work around the explicit comparison TypeErrors # raised by the 2.x set implementation if type(x) is not set or op not in COMPARISON_OPS: _check(x, py) _check(px, y) _check(px, py) def _check_in_place_op(self, op): # For in place operations, target object # must be a proxy objext for implicit # unwrapping to work properly for x, y in self.BINARY_OPERANDS: try: expected = op(x, y) except: pass else: msg_format = "Op: %s, LHS: %r, RHS: %r, Got: %r, Expected: %r" def _check(lhs, rhs): actual = op(lhs, rhs) self.assertEqual(actual, expected, msg_format % (op.__name__, lhs, rhs, actual, expected)) px = self.TEST_CLASS(x) py = self.TEST_CLASS(y) _check(px, y) _check(px, py) def test_repr(self): for x in self.UNARY_OPERANDS: px = self.TEST_CLASS(x) self.assertNotEqual(repr(x), repr(px)) self.assert_(repr(x) in repr(px)) def test_unary_ops(self): for op in self.UNARY_OPERATIONS: self._check_unary_op(op) def test_binary_ops(self): for op in self.BINARY_OPERATIONS: self._check_binary_op(op) def test_in_place_ops(self): for op in self.IN_PLACE_OPERATIONS: self._check_in_place_op(op) def test_ternary_pow(self): # For ternary pow(), first object # must be a proxy objext for implicit # unwrapping to work properly x, y, z = 1, 2, 3 expected = pow(x, y, z) msg_format = "Base: %r, Exp: %r, Mod: %r, Got: %r, Expected: %r" def _check(base, exp, mod): actual = pow(base, exp, mod) self.assertEqual(actual, expected, msg_format % (base, exp, mod, actual, expected)) px = self.TEST_CLASS(x) py = self.TEST_CLASS(y) pz = self.TEST_CLASS(z) _check(px, y, z) _check(px, y, pz) _check(px, py, z) _check(px, py, pz) def test_call(self): def f(): return 'Hello world!' pf = self.TEST_CLASS(f) expected = f() self.assertEqual(pf(), expected) def test_attributes(self): class C(object): pass x = C() x.a = 1 x.b = 2 x.c = 3 px = self.TEST_CLASS(x) self.assert_(px.a is x.a) px.d = 10 self.assert_(px.d is x.d) self.assert_(px.c is x.c) del px.c self.assertFalse(hasattr(x, 'c')) self.assertFalse(hasattr(px, 'c')) self.assert_(px.__class__ is x.__class__) def test_containment(self): x = dict(a=1, b=2, c=3) px = self.TEST_CLASS(x) self.assertEqual(len(px), len(x)) self.assert_(px['a'] is x['a']) px['d'] = 10 self.assert_(px['d'] is x['d']) self.assert_('c' in px) del px['c'] self.assert_('c' not in x) self.assert_('c' not in px) self.assertEqual(sorted(px), sorted(x)) self.assertEqual(sorted(px.items()), sorted(x.items())) # Test simple subclassing of ProxyMixin class ProxyMixinSubclass(typetools.ProxyMixin): pass class TestProxyMixinSubclass(TestProxyMixin): """Ensure proxy operations still work for a subclass""" TEST_CLASS = ProxyMixinSubclass # Test use of a descriptor for more complex target resolution class ProxyMixinDescriptor(typetools.ProxyMixin): def _make_target_property(): def pget(self): """Hidden reference to target object""" try: return object.__getattribute__(self, "_descr_target") except AttributeError: raise ReferenceError("Proxy reference not set") def pset(self, value): object.__setattr__(self, "_descr_target", value) def pdel(self): object.__delattr__(self, "_descr_target") return property(pget, pset, pdel) _target = _make_target_property() del _make_target_property class TestProxyMixinDescriptor(TestProxyMixin): """Ensure proxy operations still work with a descriptor for _target""" TEST_CLASS = ProxyMixinDescriptor def test_descriptor(self): # Check the descriptor is being invoked correctly x = 1 px = self.TEST_CLASS(x) self.assertRaises(AttributeError, getattr, px, "_target") object.__delattr__(px, "_target") self.assertRaises(ReferenceError, getattr, px, "_target") class TestProxyMixinCoverage(unittest.TestCase): """Ensure all operations in operator module are either covered explicitly or deliberately skipped""" SKIPPED_OPERATIONS =[ops.attrgetter, ops.concat, ops.contains, ops.countOf, ops.delitem, ops.delslice, ops.getitem, ops.getslice, ops.indexOf, ops.inv, ops.isCallable, ops.isMappingType, ops.isNumberType, ops.isSequenceType, ops.is_, ops.is_not, ops.itemgetter, ops.methodcaller, ops.not_, ops.sequenceIncludes, ops.setitem, ops.setslice] def test_ops_coverage(self): tested_ops = set(TestProxyMixin.UNARY_OPERATIONS) tested_ops |= set(TestProxyMixin.BINARY_OPERATIONS) tested_ops |= set(TestProxyMixin.IN_PLACE_OPERATIONS) skipped_ops = set(self.SKIPPED_OPERATIONS) operator_ops = set(y for x, y in ops.__dict__.items() if not x.startswith('__')) uncovered_ops = operator_ops - tested_ops - skipped_ops msg_format = "Uncovered operations in operator module: %s" self.assertFalse(uncovered_ops, msg_format % sorted(f.__name__ for f in uncovered_ops)) def test_main(verbose=None): import sys test_classes = ( TestProxyMixin, TestProxyMixinSubclass, TestProxyMixinDescriptor, TestProxyMixinCoverage, ) test_support.run_unittest(*test_classes) # verify reference counting if verbose and hasattr(sys, "gettotalrefcount"): import gc counts = [None] * 5 for i in xrange(len(counts)): test_support.run_unittest(*test_classes) gc.collect() counts[i] = sys.gettotalrefcount() print counts if __name__ == '__main__': test_main(verbose=True)