@@ -19,6 +19,14 @@ def check_pass_thru():
1919 raise PassThru
2020 yield 1
2121
22+ class CustomHash :
23+ def __init__ (self , hash ):
24+ self .hash = hash
25+ def __hash__ (self ):
26+ return self .hash
27+ def __repr__ (self ):
28+ return f'<CustomHash { self .hash } at { id (self ):#x} >'
29+
2230class BadCmp :
2331 def __hash__ (self ):
2432 return 1
@@ -635,6 +643,38 @@ def __le__(self, some_set):
635643 myset >= myobj
636644 self .assertTrue (myobj .le_called )
637645
646+ def test_set_membership (self ):
647+ myfrozenset = frozenset (range (3 ))
648+ myset = {myfrozenset , "abc" , 1 }
649+ self .assertIn (set (range (3 )), myset )
650+ self .assertNotIn (set (range (1 )), myset )
651+ myset .discard (set (range (3 )))
652+ self .assertEqual (myset , {"abc" , 1 })
653+ self .assertRaises (KeyError , myset .remove , set (range (1 )))
654+ self .assertRaises (KeyError , myset .remove , set (range (3 )))
655+
656+ def test_hash_collision_remove_add (self ):
657+ self .maxDiff = None
658+ # There should be enough space, so all elements with unique hash
659+ # will be placed in corresponding cells without collision.
660+ n = 64
661+ elems = [CustomHash (h ) for h in range (n )]
662+ # Elements with hash collision.
663+ a = CustomHash (n )
664+ b = CustomHash (n )
665+ elems += [a , b ]
666+ s = self .thetype (elems )
667+ self .assertEqual (len (s ), len (elems ), s )
668+ s .remove (a )
669+ # "a" has been replaced with a dummy.
670+ del elems [n ]
671+ self .assertEqual (len (s ), len (elems ), s )
672+ self .assertEqual (s , set (elems ))
673+ s .add (b )
674+ # "b" should not replace the dummy.
675+ self .assertEqual (len (s ), len (elems ), s )
676+ self .assertEqual (s , set (elems ))
677+
638678
639679class SetSubclass (set ):
640680 pass
0 commit comments