| OLD | NEW |
| 1 # test the invariant that | 1 # test the invariant that |
| 2 # iff a==b then hash(a)==hash(b) | 2 # iff a==b then hash(a)==hash(b) |
| 3 # | 3 # |
| 4 # Also test that hash implementations are inherited as expected | 4 # Also test that hash implementations are inherited as expected |
| 5 | 5 |
| 6 import datetime |
| 7 import os |
| 8 import struct |
| 6 import unittest | 9 import unittest |
| 7 from test import support | 10 from test import support |
| 11 from test.script_helper import assert_python_ok |
| 8 from collections import Hashable | 12 from collections import Hashable |
| 13 |
| 14 IS_64BIT = (struct.calcsize('l') == 8) |
| 9 | 15 |
| 10 | 16 |
| 11 class HashEqualityTestCase(unittest.TestCase): | 17 class HashEqualityTestCase(unittest.TestCase): |
| 12 | 18 |
| 13 def same_hash(self, *objlist): | 19 def same_hash(self, *objlist): |
| 14 # Hash each object given and fail if | 20 # Hash each object given and fail if |
| 15 # the hash values are not all the same. | 21 # the hash values are not all the same. |
| 16 hashed = list(map(hash, objlist)) | 22 hashed = list(map(hash, objlist)) |
| 17 for h in hashed[1:]: | 23 for h in hashed[1:]: |
| 18 if h != hashed[0]: | 24 if h != hashed[0]: |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 111 enumerate(range(10)), | 117 enumerate(range(10)), |
| 112 iter(DefaultIterSeq()), | 118 iter(DefaultIterSeq()), |
| 113 iter(lambda: 0, 0), | 119 iter(lambda: 0, 0), |
| 114 ] | 120 ] |
| 115 | 121 |
| 116 def test_hashes(self): | 122 def test_hashes(self): |
| 117 _default_hash = object.__hash__ | 123 _default_hash = object.__hash__ |
| 118 for obj in self.hashes_to_check: | 124 for obj in self.hashes_to_check: |
| 119 self.assertEqual(hash(obj), _default_hash(obj)) | 125 self.assertEqual(hash(obj), _default_hash(obj)) |
| 120 | 126 |
| 127 class HashRandomizationTests(unittest.TestCase): |
| 128 |
| 129 # Each subclass should define a field "repr_", containing the repr() of |
| 130 # an object to be tested |
| 131 |
| 132 def get_hash_command(self, repr_): |
| 133 return 'print(hash(%s))' % repr_ |
| 134 |
| 135 def get_hash(self, repr_, seed=None): |
| 136 env = os.environ.copy() |
| 137 env['__cleanenv'] = True # signal to assert_python not to do a copy |
| 138 # of os.environ on its own |
| 139 if seed is not None: |
| 140 env['PYTHONHASHSEED'] = str(seed) |
| 141 else: |
| 142 env.pop('PYTHONHASHSEED', None) |
| 143 out = assert_python_ok( |
| 144 '-c', self.get_hash_command(repr_), |
| 145 **env) |
| 146 stdout = out[1].strip() |
| 147 return int(stdout) |
| 148 |
| 149 def test_randomized_hash(self): |
| 150 # two runs should return different hashes |
| 151 run1 = self.get_hash(self.repr_, seed='random') |
| 152 run2 = self.get_hash(self.repr_, seed='random') |
| 153 self.assertNotEqual(run1, run2) |
| 154 |
| 155 class StringlikeHashRandomizationTests(HashRandomizationTests): |
| 156 def test_null_hash(self): |
| 157 # PYTHONHASHSEED=0 disables the randomized hash |
| 158 if IS_64BIT: |
| 159 known_hash_of_obj = 1453079729188098211 |
| 160 else: |
| 161 known_hash_of_obj = -1600925533 |
| 162 |
| 163 # Randomization is disabled by default: |
| 164 self.assertEqual(self.get_hash(self.repr_), known_hash_of_obj) |
| 165 |
| 166 # It can also be disabled by setting the seed to 0: |
| 167 self.assertEqual(self.get_hash(self.repr_, seed=0), known_hash_of_obj) |
| 168 |
| 169 def test_fixed_hash(self): |
| 170 # test a fixed seed for the randomized hash |
| 171 # Note that all types share the same values: |
| 172 if IS_64BIT: |
| 173 h = -4410911502303878509 |
| 174 else: |
| 175 h = -206076799 |
| 176 self.assertEqual(self.get_hash(self.repr_, seed=42), h) |
| 177 |
| 178 class StrHashRandomizationTests(StringlikeHashRandomizationTests): |
| 179 repr_ = repr('abc') |
| 180 |
| 181 def test_empty_string(self): |
| 182 self.assertEqual(hash(""), 0) |
| 183 |
| 184 class BytesHashRandomizationTests(StringlikeHashRandomizationTests): |
| 185 repr_ = repr(b'abc') |
| 186 |
| 187 def test_empty_string(self): |
| 188 self.assertEqual(hash(b""), 0) |
| 189 |
| 190 class DatetimeTests(HashRandomizationTests): |
| 191 def get_hash_command(self, repr_): |
| 192 return 'import datetime; print(hash(%s))' % repr_ |
| 193 |
| 194 class DatetimeDateTests(DatetimeTests): |
| 195 repr_ = repr(datetime.date(1066, 10, 14)) |
| 196 |
| 197 class DatetimeDatetimeTests(DatetimeTests): |
| 198 repr_ = repr(datetime.datetime(1, 2, 3, 4, 5, 6, 7)) |
| 199 |
| 200 class DatetimeTimeTests(DatetimeTests): |
| 201 repr_ = repr(datetime.time(0)) |
| 202 |
| 203 |
| 121 def test_main(): | 204 def test_main(): |
| 122 support.run_unittest(HashEqualityTestCase, | 205 support.run_unittest(HashEqualityTestCase, |
| 123 HashInheritanceTestCase, | 206 HashInheritanceTestCase, |
| 124 HashBuiltinsTestCase) | 207 HashBuiltinsTestCase, |
| 208 StrHashRandomizationTests, |
| 209 BytesHashRandomizationTests, |
| 210 DatetimeDateTests, |
| 211 DatetimeDatetimeTests, |
| 212 DatetimeTimeTests) |
| 125 | 213 |
| 126 | 214 |
| 127 if __name__ == "__main__": | 215 if __name__ == "__main__": |
| 128 test_main() | 216 test_main() |
| OLD | NEW |