|
1 | 1 | # test the invariant that |
2 | 2 | # iff a==b then hash(a)==hash(b) |
3 | 3 | # |
| 4 | +# Also test that hash implementations are inherited as expected |
4 | 5 |
|
5 | 6 | import unittest |
6 | 7 | from test import test_support |
| 8 | +from collections import Hashable |
7 | 9 |
|
8 | 10 |
|
9 | 11 | class HashEqualityTestCase(unittest.TestCase): |
@@ -39,8 +41,83 @@ def test_coerced_floats(self): |
39 | 41 | self.same_hash(float(0.5), complex(0.5, 0.0)) |
40 | 42 |
|
41 | 43 |
|
| 44 | +_default_hash = object.__hash__ |
| 45 | +class DefaultHash(object): pass |
| 46 | + |
| 47 | +_FIXED_HASH_VALUE = 42 |
| 48 | +class FixedHash(object): |
| 49 | + def __hash__(self): |
| 50 | + return _FIXED_HASH_VALUE |
| 51 | + |
| 52 | +class OnlyEquality(object): |
| 53 | + def __eq__(self, other): |
| 54 | + return self is other |
| 55 | + |
| 56 | +class OnlyInequality(object): |
| 57 | + def __ne__(self, other): |
| 58 | + return self is not other |
| 59 | + |
| 60 | +class OnlyCmp(object): |
| 61 | + def __cmp__(self, other): |
| 62 | + return cmp(id(self), id(other)) |
| 63 | + |
| 64 | +class InheritedHashWithEquality(FixedHash, OnlyEquality): pass |
| 65 | +class InheritedHashWithInequality(FixedHash, OnlyInequality): pass |
| 66 | +class InheritedHashWithCmp(FixedHash, OnlyCmp): pass |
| 67 | + |
| 68 | +class NoHash(object): |
| 69 | + __hash__ = None |
| 70 | + |
| 71 | +class HashInheritanceTestCase(unittest.TestCase): |
| 72 | + default_expected = [object(), |
| 73 | + DefaultHash(), |
| 74 | + ] |
| 75 | + fixed_expected = [FixedHash(), |
| 76 | + InheritedHashWithEquality(), |
| 77 | + InheritedHashWithInequality(), |
| 78 | + InheritedHashWithCmp(), |
| 79 | + ] |
| 80 | + # TODO: Change these to expecting an exception |
| 81 | + # when forward porting to Py3k |
| 82 | + warning_expected = [OnlyEquality(), |
| 83 | + OnlyInequality(), |
| 84 | + OnlyCmp(), |
| 85 | + ] |
| 86 | + error_expected = [NoHash()] |
| 87 | + |
| 88 | + def test_default_hash(self): |
| 89 | + for obj in self.default_expected: |
| 90 | + self.assertEqual(hash(obj), _default_hash(obj)) |
| 91 | + |
| 92 | + def test_fixed_hash(self): |
| 93 | + for obj in self.fixed_expected: |
| 94 | + self.assertEqual(hash(obj), _FIXED_HASH_VALUE) |
| 95 | + |
| 96 | + def test_warning_hash(self): |
| 97 | + for obj in self.warning_expected: |
| 98 | + # TODO: Check for the expected Py3k warning here |
| 99 | + obj_hash = hash(obj) |
| 100 | + self.assertEqual(obj_hash, _default_hash(obj)) |
| 101 | + |
| 102 | + def test_error_hash(self): |
| 103 | + for obj in self.error_expected: |
| 104 | + self.assertRaises(TypeError, hash, obj) |
| 105 | + |
| 106 | + def test_hashable(self): |
| 107 | + objects = (self.default_expected + |
| 108 | + self.fixed_expected + |
| 109 | + self.warning_expected) |
| 110 | + for obj in objects: |
| 111 | + self.assert_(isinstance(obj, Hashable), repr(obj)) |
| 112 | + |
| 113 | + def test_not_hashable(self): |
| 114 | + for obj in self.error_expected: |
| 115 | + self.assertFalse(isinstance(obj, Hashable), repr(obj)) |
| 116 | + |
| 117 | + |
42 | 118 | def test_main(): |
43 | | - test_support.run_unittest(HashEqualityTestCase) |
| 119 | + test_support.run_unittest(HashEqualityTestCase, |
| 120 | + HashInheritanceTestCase) |
44 | 121 |
|
45 | 122 |
|
46 | 123 | if __name__ == "__main__": |
|
0 commit comments