1# test the invariant that 2# iff a==b then hash(a)==hash(b) 3# 4# Also test that hash implementations are inherited as expected 5 6import os 7import sys 8import struct 9import datetime 10import unittest 11import subprocess 12 13from test import test_support 14from collections import Hashable 15 16IS_64BIT = (struct.calcsize('l') == 8) 17 18 19class HashEqualityTestCase(unittest.TestCase): 20 21 def same_hash(self, *objlist): 22 # Hash each object given and fail if 23 # the hash values are not all the same. 24 hashed = map(hash, objlist) 25 for h in hashed[1:]: 26 if h != hashed[0]: 27 self.fail("hashed values differ: %r" % (objlist,)) 28 29 def test_numeric_literals(self): 30 self.same_hash(1, 1L, 1.0, 1.0+0.0j) 31 self.same_hash(0, 0L, 0.0, 0.0+0.0j) 32 self.same_hash(-1, -1L, -1.0, -1.0+0.0j) 33 self.same_hash(-2, -2L, -2.0, -2.0+0.0j) 34 35 def test_coerced_integers(self): 36 self.same_hash(int(1), long(1), float(1), complex(1), 37 int('1'), float('1.0')) 38 self.same_hash(int(-2**31), long(-2**31), float(-2**31)) 39 self.same_hash(int(1-2**31), long(1-2**31), float(1-2**31)) 40 self.same_hash(int(2**31-1), long(2**31-1), float(2**31-1)) 41 # for 64-bit platforms 42 self.same_hash(int(2**31), long(2**31), float(2**31)) 43 self.same_hash(int(-2**63), long(-2**63), float(-2**63)) 44 self.same_hash(int(1-2**63), long(1-2**63)) 45 self.same_hash(int(2**63-1), long(2**63-1)) 46 self.same_hash(long(2**63), float(2**63)) 47 48 def test_coerced_floats(self): 49 self.same_hash(long(1.23e300), float(1.23e300)) 50 self.same_hash(float(0.5), complex(0.5, 0.0)) 51 52 53_default_hash = object.__hash__ 54class DefaultHash(object): pass 55 56_FIXED_HASH_VALUE = 42 57class FixedHash(object): 58 def __hash__(self): 59 return _FIXED_HASH_VALUE 60 61class OnlyEquality(object): 62 def __eq__(self, other): 63 return self is other 64 # Trick to suppress Py3k warning in 2.x 65 __hash__ = None 66del OnlyEquality.__hash__ 67 68class OnlyInequality(object): 69 def __ne__(self, other): 70 return self is not other 71 72class OnlyCmp(object): 73 def __cmp__(self, other): 74 return cmp(id(self), id(other)) 75 # Trick to suppress Py3k warning in 2.x 76 __hash__ = None 77del OnlyCmp.__hash__ 78 79class InheritedHashWithEquality(FixedHash, OnlyEquality): pass 80class InheritedHashWithInequality(FixedHash, OnlyInequality): pass 81class InheritedHashWithCmp(FixedHash, OnlyCmp): pass 82 83class NoHash(object): 84 __hash__ = None 85 86class HashInheritanceTestCase(unittest.TestCase): 87 default_expected = [object(), 88 DefaultHash(), 89 OnlyEquality(), 90 OnlyInequality(), 91 OnlyCmp(), 92 ] 93 fixed_expected = [FixedHash(), 94 InheritedHashWithEquality(), 95 InheritedHashWithInequality(), 96 InheritedHashWithCmp(), 97 ] 98 error_expected = [NoHash()] 99 100 def test_default_hash(self): 101 for obj in self.default_expected: 102 self.assertEqual(hash(obj), _default_hash(obj)) 103 104 def test_fixed_hash(self): 105 for obj in self.fixed_expected: 106 self.assertEqual(hash(obj), _FIXED_HASH_VALUE) 107 108 def test_error_hash(self): 109 for obj in self.error_expected: 110 self.assertRaises(TypeError, hash, obj) 111 112 def test_hashable(self): 113 objects = (self.default_expected + 114 self.fixed_expected) 115 for obj in objects: 116 self.assertIsInstance(obj, Hashable) 117 118 def test_not_hashable(self): 119 for obj in self.error_expected: 120 self.assertNotIsInstance(obj, Hashable) 121 122 123# Issue #4701: Check that some builtin types are correctly hashable 124# (This test only used to fail in Python 3.0, but has been included 125# in 2.x along with the lazy call to PyType_Ready in PyObject_Hash) 126class DefaultIterSeq(object): 127 seq = range(10) 128 def __len__(self): 129 return len(self.seq) 130 def __getitem__(self, index): 131 return self.seq[index] 132 133class HashBuiltinsTestCase(unittest.TestCase): 134 hashes_to_check = [xrange(10), 135 enumerate(xrange(10)), 136 iter(DefaultIterSeq()), 137 iter(lambda: 0, 0), 138 ] 139 140 def test_hashes(self): 141 _default_hash = object.__hash__ 142 for obj in self.hashes_to_check: 143 self.assertEqual(hash(obj), _default_hash(obj)) 144 145class HashRandomizationTests(unittest.TestCase): 146 147 # Each subclass should define a field "repr_", containing the repr() of 148 # an object to be tested 149 150 def get_hash_command(self, repr_): 151 return 'print(hash(%s))' % repr_ 152 153 def get_hash(self, repr_, seed=None): 154 env = os.environ.copy() 155 if seed is not None: 156 env['PYTHONHASHSEED'] = str(seed) 157 else: 158 env.pop('PYTHONHASHSEED', None) 159 cmd_line = [sys.executable, '-c', self.get_hash_command(repr_)] 160 p = subprocess.Popen(cmd_line, stdin=subprocess.PIPE, 161 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, 162 env=env) 163 out, err = p.communicate() 164 out = test_support.strip_python_stderr(out) 165 return int(out.strip()) 166 167 def test_randomized_hash(self): 168 # two runs should return different hashes 169 run1 = self.get_hash(self.repr_, seed='random') 170 run2 = self.get_hash(self.repr_, seed='random') 171 self.assertNotEqual(run1, run2) 172 173class StringlikeHashRandomizationTests(HashRandomizationTests): 174 def test_null_hash(self): 175 # PYTHONHASHSEED=0 disables the randomized hash 176 if IS_64BIT: 177 known_hash_of_obj = 1453079729188098211 178 else: 179 known_hash_of_obj = -1600925533 180 181 # Randomization is disabled by default: 182 self.assertEqual(self.get_hash(self.repr_), known_hash_of_obj) 183 184 # It can also be disabled by setting the seed to 0: 185 self.assertEqual(self.get_hash(self.repr_, seed=0), known_hash_of_obj) 186 187 def test_fixed_hash(self): 188 # test a fixed seed for the randomized hash 189 # Note that all types share the same values: 190 if IS_64BIT: 191 if sys.byteorder == 'little': 192 h = -4410911502303878509 193 else: 194 h = -3570150969479994130 195 else: 196 if sys.byteorder == 'little': 197 h = -206076799 198 else: 199 h = -1024014457 200 self.assertEqual(self.get_hash(self.repr_, seed=42), h) 201 202class StrHashRandomizationTests(StringlikeHashRandomizationTests): 203 repr_ = repr('abc') 204 205 def test_empty_string(self): 206 self.assertEqual(hash(""), 0) 207 208class UnicodeHashRandomizationTests(StringlikeHashRandomizationTests): 209 repr_ = repr(u'abc') 210 211 def test_empty_string(self): 212 self.assertEqual(hash(u""), 0) 213 214class BufferHashRandomizationTests(StringlikeHashRandomizationTests): 215 repr_ = 'buffer("abc")' 216 217 def test_empty_string(self): 218 with test_support.check_py3k_warnings(): 219 self.assertEqual(hash(buffer("")), 0) 220 221class DatetimeTests(HashRandomizationTests): 222 def get_hash_command(self, repr_): 223 return 'import datetime; print(hash(%s))' % repr_ 224 225class DatetimeDateTests(DatetimeTests): 226 repr_ = repr(datetime.date(1066, 10, 14)) 227 228class DatetimeDatetimeTests(DatetimeTests): 229 repr_ = repr(datetime.datetime(1, 2, 3, 4, 5, 6, 7)) 230 231class DatetimeTimeTests(DatetimeTests): 232 repr_ = repr(datetime.time(0)) 233 234 235def test_main(): 236 test_support.run_unittest(HashEqualityTestCase, 237 HashInheritanceTestCase, 238 HashBuiltinsTestCase, 239 StrHashRandomizationTests, 240 UnicodeHashRandomizationTests, 241 BufferHashRandomizationTests, 242 DatetimeDateTests, 243 DatetimeDatetimeTests, 244 DatetimeTimeTests) 245 246 247 248if __name__ == "__main__": 249 test_main() 250