Home | History | Annotate | Download | only in test
      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 
      6 import os
      7 import sys
      8 import struct
      9 import datetime
     10 import unittest
     11 import subprocess
     12 
     13 from test import test_support
     14 from collections import Hashable
     15 
     16 IS_64BIT = (struct.calcsize('l') == 8)
     17 
     18 
     19 class 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__
     54 class DefaultHash(object): pass
     55 
     56 _FIXED_HASH_VALUE = 42
     57 class FixedHash(object):
     58     def __hash__(self):
     59         return _FIXED_HASH_VALUE
     60 
     61 class OnlyEquality(object):
     62     def __eq__(self, other):
     63         return self is other
     64     # Trick to suppress Py3k warning in 2.x
     65     __hash__ = None
     66 del OnlyEquality.__hash__
     67 
     68 class OnlyInequality(object):
     69     def __ne__(self, other):
     70         return self is not other
     71 
     72 class 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
     77 del OnlyCmp.__hash__
     78 
     79 class InheritedHashWithEquality(FixedHash, OnlyEquality): pass
     80 class InheritedHashWithInequality(FixedHash, OnlyInequality): pass
     81 class InheritedHashWithCmp(FixedHash, OnlyCmp): pass
     82 
     83 class NoHash(object):
     84     __hash__ = None
     85 
     86 class 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)
    126 class 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 
    133 class 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 
    145 class 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 
    173 class 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 
    202 class StrHashRandomizationTests(StringlikeHashRandomizationTests):
    203     repr_ = repr('abc')
    204 
    205     def test_empty_string(self):
    206         self.assertEqual(hash(""), 0)
    207 
    208 class UnicodeHashRandomizationTests(StringlikeHashRandomizationTests):
    209     repr_ = repr(u'abc')
    210 
    211     def test_empty_string(self):
    212         self.assertEqual(hash(u""), 0)
    213 
    214 class BufferHashRandomizationTests(StringlikeHashRandomizationTests):
    215     repr_ = 'buffer("abc")'
    216 
    217     def test_empty_string(self):
    218         self.assertEqual(hash(buffer("")), 0)
    219 
    220 class DatetimeTests(HashRandomizationTests):
    221     def get_hash_command(self, repr_):
    222         return 'import datetime; print(hash(%s))' % repr_
    223 
    224 class DatetimeDateTests(DatetimeTests):
    225     repr_ = repr(datetime.date(1066, 10, 14))
    226 
    227 class DatetimeDatetimeTests(DatetimeTests):
    228     repr_ = repr(datetime.datetime(1, 2, 3, 4, 5, 6, 7))
    229 
    230 class DatetimeTimeTests(DatetimeTests):
    231     repr_ = repr(datetime.time(0))
    232 
    233 
    234 def test_main():
    235     test_support.run_unittest(HashEqualityTestCase,
    236                               HashInheritanceTestCase,
    237                               HashBuiltinsTestCase,
    238                               StrHashRandomizationTests,
    239                               UnicodeHashRandomizationTests,
    240                               BufferHashRandomizationTests,
    241                               DatetimeDateTests,
    242                               DatetimeDatetimeTests,
    243                               DatetimeTimeTests)
    244 
    245 
    246 
    247 if __name__ == "__main__":
    248     test_main()
    249