[3.14] Add regression test for add() after remove() with hash collision in set (GH-143781) (GH-143858)

(cherry picked from commit 565685f6e88fd333326baff6469f53cfff28e01e)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
Miss Islington (bot) 2026-01-15 10:06:21 +01:00 committed by GitHub
parent 2e493239b1
commit d210c7736b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 62 additions and 0 deletions

View File

@ -12,6 +12,15 @@ from test import support
from test.support import import_helper
class CustomHash:
def __init__(self, hash):
self.hash = hash
def __hash__(self):
return self.hash
def __repr__(self):
return f'<CustomHash {self.hash} at {id(self):#x}>'
class DictTest(unittest.TestCase):
def test_invalid_keyword_arguments(self):
@ -1648,6 +1657,29 @@ class DictTest(unittest.TestCase):
d[MyStr("attr1")] = 2
self.assertIsInstance(list(d)[0], MyStr)
def test_hash_collision_remove_add(self):
self.maxDiff = None
# There should be enough space, so all elements with unique hash
# will be placed in corresponding cells without collision.
n = 64
items = [(CustomHash(h), h) for h in range(n)]
# Keys with hash collision.
a = CustomHash(n)
b = CustomHash(n)
items += [(a, 'a'), (b, 'b')]
d = dict(items)
self.assertEqual(len(d), len(items), d)
del d[a]
# "a" has been replaced with a dummy.
del items[n]
self.assertEqual(len(d), len(items), d)
self.assertEqual(d, dict(items))
d[b] = 'c'
# "b" should not replace the dummy.
items[n] = (b, 'c')
self.assertEqual(len(d), len(items), d)
self.assertEqual(d, dict(items))
class CAPITest(unittest.TestCase):

View File

@ -20,6 +20,14 @@ def check_pass_thru():
raise PassThru
yield 1
class CustomHash:
def __init__(self, hash):
self.hash = hash
def __hash__(self):
return self.hash
def __repr__(self):
return f'<CustomHash {self.hash} at {id(self):#x}>'
class BadCmp:
def __hash__(self):
return 1
@ -675,6 +683,28 @@ class TestSet(TestJointOps, unittest.TestCase):
with self.assertRaises(KeyError):
myset.discard(elem2)
def test_hash_collision_remove_add(self):
self.maxDiff = None
# There should be enough space, so all elements with unique hash
# will be placed in corresponding cells without collision.
n = 64
elems = [CustomHash(h) for h in range(n)]
# Elements with hash collision.
a = CustomHash(n)
b = CustomHash(n)
elems += [a, b]
s = self.thetype(elems)
self.assertEqual(len(s), len(elems), s)
s.remove(a)
# "a" has been replaced with a dummy.
del elems[n]
self.assertEqual(len(s), len(elems), s)
self.assertEqual(s, set(elems))
s.add(b)
# "b" should not replace the dummy.
self.assertEqual(len(s), len(elems), s)
self.assertEqual(s, set(elems))
class SetSubclass(set):
pass