Skip to content

Commit c97ff93

Browse files
committed
test: add hashlib compatibility test suite
Tests algorithms_available/guaranteed, digest_size, block_size, name, digest/hexdigest, update, copy, str rejection, data= keyword. Re-add algorithms_guaranteed lost in revert.
1 parent c3724ea commit c97ff93

2 files changed

Lines changed: 114 additions & 0 deletions

File tree

tests/test_hashlib_compat.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"""Tests for hashlib compatibility."""
2+
import unittest
3+
import xxhash
4+
5+
6+
class TestHashlibCompat(unittest.TestCase):
7+
"""Verify hashlib-compatible interface."""
8+
9+
data = b'hello world'
10+
11+
def test_algorithms_available(self):
12+
self.assertIsInstance(xxhash.algorithms_available, set)
13+
for a in ('xxh32', 'xxh64', 'xxh3_64', 'xxh3_128', 'xxh128'):
14+
self.assertIn(a, xxhash.algorithms_available)
15+
16+
def test_algorithms_guaranteed(self):
17+
self.assertEqual(xxhash.algorithms_guaranteed, xxhash.algorithms_available)
18+
19+
# ── str rejection ──────────────────────────────────────────────
20+
21+
def test_str_rejected(self):
22+
for algo in ('xxh32', 'xxh64', 'xxh3_64', 'xxh3_128'):
23+
for fn in (getattr(xxhash, f'{algo}_digest'),
24+
getattr(xxhash, f'{algo}_intdigest'),
25+
getattr(xxhash, f'{algo}_hexdigest')):
26+
with self.assertRaisesRegex(TypeError,
27+
'Strings must be encoded before hashing'):
28+
fn('hello')
29+
30+
def test_str_rejected_constructor(self):
31+
for algo in ('xxh32', 'xxh64', 'xxh3_64', 'xxh3_128'):
32+
cls = getattr(xxhash, algo)
33+
with self.assertRaisesRegex(TypeError,
34+
'Strings must be encoded before hashing'):
35+
cls('hello')
36+
37+
# ── data keyword ───────────────────────────────────────────────
38+
39+
def test_data_keyword(self):
40+
for algo in ('xxh32', 'xxh64', 'xxh3_64', 'xxh3_128'):
41+
obj = getattr(xxhash, algo)(self.data)
42+
d_fn = getattr(xxhash, f'{algo}_digest')
43+
i_fn = getattr(xxhash, f'{algo}_intdigest')
44+
h_fn = getattr(xxhash, f'{algo}_hexdigest')
45+
self.assertEqual(d_fn(data=self.data), obj.digest())
46+
self.assertEqual(i_fn(data=self.data), obj.intdigest())
47+
self.assertEqual(h_fn(data=self.data), obj.hexdigest())
48+
49+
def test_data_keyword_constructor(self):
50+
for algo in ('xxh32', 'xxh64', 'xxh3_64', 'xxh3_128'):
51+
cls = getattr(xxhash, algo)
52+
obj = cls(data=self.data)
53+
self.assertEqual(obj.intdigest(),
54+
getattr(xxhash, f'{algo}_intdigest')(self.data))
55+
56+
# ── digest_size / block_size / name ────────────────────────────
57+
58+
def test_digest_size(self):
59+
self.assertEqual(xxhash.xxh32().digest_size, 4)
60+
self.assertEqual(xxhash.xxh64().digest_size, 8)
61+
self.assertEqual(xxhash.xxh3_64().digest_size, 8)
62+
self.assertEqual(xxhash.xxh3_128().digest_size, 16)
63+
64+
def test_block_size(self):
65+
self.assertEqual(xxhash.xxh32().block_size, 16)
66+
self.assertEqual(xxhash.xxh64().block_size, 32)
67+
self.assertEqual(xxhash.xxh3_64().block_size, 32)
68+
self.assertEqual(xxhash.xxh3_128().block_size, 64)
69+
70+
def test_name(self):
71+
self.assertEqual(xxhash.xxh32().name, 'XXH32')
72+
self.assertEqual(xxhash.xxh64().name, 'XXH64')
73+
self.assertEqual(xxhash.xxh3_64().name, 'XXH3_64')
74+
self.assertEqual(xxhash.xxh3_128().name, 'XXH3_128')
75+
76+
# ── digest / hexdigest ─────────────────────────────────────────
77+
78+
def test_digest(self):
79+
for algo in ('xxh32', 'xxh64', 'xxh3_64', 'xxh3_128'):
80+
obj = getattr(xxhash, algo)(self.data)
81+
d_fn = getattr(xxhash, f'{algo}_digest')
82+
self.assertEqual(obj.digest(), d_fn(self.data))
83+
self.assertIsInstance(obj.digest(), bytes)
84+
self.assertEqual(len(obj.digest()), obj.digest_size)
85+
86+
def test_hexdigest(self):
87+
for algo in ('xxh32', 'xxh64', 'xxh3_64', 'xxh3_128'):
88+
obj = getattr(xxhash, algo)(self.data)
89+
h_fn = getattr(xxhash, f'{algo}_hexdigest')
90+
self.assertEqual(obj.hexdigest(), h_fn(self.data))
91+
self.assertIsInstance(obj.hexdigest(), str)
92+
self.assertEqual(len(obj.hexdigest()), obj.digest_size * 2)
93+
94+
# ── update ─────────────────────────────────────────────────────
95+
96+
def test_update(self):
97+
for algo in ('xxh32', 'xxh64', 'xxh3_64', 'xxh3_128'):
98+
a = getattr(xxhash, algo)()
99+
a.update(self.data)
100+
b = getattr(xxhash, algo)(self.data)
101+
self.assertEqual(a.digest(), b.digest())
102+
103+
# ── copy ───────────────────────────────────────────────────────
104+
105+
def test_copy(self):
106+
for algo in ('xxh32', 'xxh64', 'xxh3_64', 'xxh3_128'):
107+
a = getattr(xxhash, algo)(self.data)
108+
b = a.copy()
109+
self.assertEqual(a.digest(), b.digest())
110+
b.update(b'more')
111+
self.assertNotEqual(a.digest(), b.digest())

xxhash/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
"xxh3_128",
3535
])
3636

37+
algorithms_guaranteed = algorithms_available
38+
3739

3840
__all__ = [
3941
"xxh32",
@@ -60,4 +62,5 @@
6062
"VERSION_TUPLE",
6163
"XXHASH_VERSION",
6264
"algorithms_available",
65+
"algorithms_guaranteed",
6366
]

0 commit comments

Comments
 (0)