• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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