Skip to content

Commit a095661

Browse files
committed
Improve unit tests.
The test_contains_frozenset() was not actually doing anything useful, rewrite it. Add test_contains_hash_mutate() which triggers unusual code paths due to `__hash__` on the key mutating the container.
1 parent 93c21fd commit a095661

File tree

1 file changed

+68
-15
lines changed

1 file changed

+68
-15
lines changed

Lib/test/test_free_threading/test_set.py

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def test_repr_clear(self):
1212
"""Test repr() of a set while another thread is calling clear()"""
1313
NUM_ITERS = 10
1414
NUM_REPR_THREADS = 10
15-
barrier = Barrier(NUM_REPR_THREADS + 1)
15+
barrier = Barrier(NUM_REPR_THREADS + 1, timeout=2)
1616
s = {1, 2, 3, 4, 5, 6, 7, 8}
1717

1818
def clear_set():
@@ -38,12 +38,12 @@ def repr_set():
3838

3939
def test_contains_mutate(self):
4040
"""Test set contains operation combined with mutation."""
41-
barrier = Barrier(2)
41+
barrier = Barrier(2, timeout=2)
4242
s = set()
4343
done = False
4444

45-
NUM_ITEMS = 2_000
46-
NUM_LOOPS = 20
45+
NUM_ITEMS = 200
46+
NUM_LOOPS = 200
4747

4848
def read_set():
4949
barrier.wait()
@@ -72,29 +72,82 @@ def mutate_set():
7272
t.join()
7373

7474
def test_contains_frozenset(self):
75-
barrier = Barrier(3)
75+
barrier = Barrier(3, timeout=2)
7676
done = False
7777

78-
NUM_ITEMS = 2_000
79-
NUM_LOOPS = 20
78+
NUM_LOOPS = 1_000
8079

81-
s = frozenset()
82-
def make_set():
83-
nonlocal s
80+
# This mutates the key used for contains test, not the container
81+
# itself. This works because frozenset allows the key to be a set().
82+
s = set()
83+
84+
def mutate_set():
8485
barrier.wait()
8586
while not done:
86-
s = frozenset(range(NUM_ITEMS))
87+
s.add(0)
88+
s.add(1)
89+
s.clear()
8790

8891
def read_set():
8992
nonlocal done
9093
barrier.wait()
94+
container = frozenset([frozenset([0])])
95+
self.assertTrue(set([0]) in container)
9196
for _ in range(NUM_LOOPS):
92-
for i in range(NUM_ITEMS):
93-
item = i >> 1
94-
result = item in s
97+
# Will return True when {0} is the key and False otherwise
98+
result = s in container
9599
done = True
96100

97-
threads = [Thread(target=read_set), Thread(target=read_set), Thread(target=make_set)]
101+
threads = [
102+
Thread(target=read_set),
103+
Thread(target=read_set),
104+
Thread(target=mutate_set),
105+
]
106+
for t in threads:
107+
t.start()
108+
for t in threads:
109+
t.join()
110+
111+
def test_contains_hash_mutate(self):
112+
"""Test set contains operation with mutating hash method."""
113+
barrier = Barrier(2, timeout=2)
114+
115+
NUM_ITEMS = 20 # should be larger than PySet_MINSIZE
116+
NUM_LOOPS = 1_000
117+
118+
s = set(range(NUM_ITEMS))
119+
120+
class Key:
121+
def __init__(self):
122+
self.count = 0
123+
self.value = 0
124+
125+
def __hash__(self):
126+
self.count += 1
127+
# This intends to trigger the SET_LOOKKEY_CHANGED case
128+
# of set_lookkey_threadsafe() since calling clear()
129+
# will cause the 'table' pointer to change.
130+
if self.count % 2 == 0:
131+
s.clear()
132+
else:
133+
s.update(range(NUM_ITEMS))
134+
return hash(self.value)
135+
136+
def __eq__(self, other):
137+
return self.value == other
138+
139+
key = Key()
140+
self.assertTrue(key in s)
141+
self.assertFalse(key in s)
142+
self.assertTrue(key in s)
143+
self.assertFalse(key in s)
144+
145+
def read_set():
146+
barrier.wait()
147+
for i in range(NUM_LOOPS):
148+
result = key in s
149+
150+
threads = [Thread(target=read_set), Thread(target=read_set)]
98151
for t in threads:
99152
t.start()
100153
for t in threads:

0 commit comments

Comments
 (0)