Skip to content

Commit 24f2524

Browse files
authored
Added "os.wait" tests from 3.13.5 (#6027)
* Added "wait" tests from 3.13.5 * Trigger CI
1 parent 74bee7c commit 24f2524

File tree

3 files changed

+171
-0
lines changed

3 files changed

+171
-0
lines changed

Lib/test/fork_wait.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""This test case provides support for checking forking and wait behavior.
2+
3+
To test different wait behavior, override the wait_impl method.
4+
5+
We want fork1() semantics -- only the forking thread survives in the
6+
child after a fork().
7+
8+
On some systems (e.g. Solaris without posix threads) we find that all
9+
active threads survive in the child after a fork(); this is an error.
10+
"""
11+
12+
import os, time, unittest
13+
import threading
14+
from test import support
15+
from test.support import threading_helper
16+
import warnings
17+
18+
19+
LONGSLEEP = 2
20+
SHORTSLEEP = 0.5
21+
NUM_THREADS = 4
22+
23+
class ForkWait(unittest.TestCase):
24+
25+
def setUp(self):
26+
self._threading_key = threading_helper.threading_setup()
27+
self.alive = {}
28+
self.stop = 0
29+
self.threads = []
30+
31+
def tearDown(self):
32+
# Stop threads
33+
self.stop = 1
34+
for thread in self.threads:
35+
thread.join()
36+
thread = None
37+
self.threads.clear()
38+
threading_helper.threading_cleanup(*self._threading_key)
39+
40+
def f(self, id):
41+
while not self.stop:
42+
self.alive[id] = os.getpid()
43+
try:
44+
time.sleep(SHORTSLEEP)
45+
except OSError:
46+
pass
47+
48+
def wait_impl(self, cpid, *, exitcode):
49+
support.wait_process(cpid, exitcode=exitcode)
50+
51+
def test_wait(self):
52+
for i in range(NUM_THREADS):
53+
thread = threading.Thread(target=self.f, args=(i,))
54+
thread.start()
55+
self.threads.append(thread)
56+
57+
# busy-loop to wait for threads
58+
for _ in support.sleeping_retry(support.SHORT_TIMEOUT):
59+
if len(self.alive) >= NUM_THREADS:
60+
break
61+
62+
a = sorted(self.alive.keys())
63+
self.assertEqual(a, list(range(NUM_THREADS)))
64+
65+
prefork_lives = self.alive.copy()
66+
67+
# Ignore the warning about fork with threads.
68+
with warnings.catch_warnings(category=DeprecationWarning,
69+
action="ignore"):
70+
if (cpid := os.fork()) == 0:
71+
# Child
72+
time.sleep(LONGSLEEP)
73+
n = 0
74+
for key in self.alive:
75+
if self.alive[key] != prefork_lives[key]:
76+
n += 1
77+
os._exit(n)
78+
else:
79+
# Parent
80+
self.wait_impl(cpid, exitcode=0)

Lib/test/test_wait3.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""This test checks for correct wait3() behavior.
2+
"""
3+
4+
import os
5+
import subprocess
6+
import sys
7+
import unittest
8+
from test.fork_wait import ForkWait
9+
from test import support
10+
11+
if not support.has_fork_support:
12+
raise unittest.SkipTest("requires working os.fork()")
13+
14+
if not hasattr(os, 'wait3'):
15+
raise unittest.SkipTest("os.wait3 not defined")
16+
17+
class Wait3Test(ForkWait):
18+
def wait_impl(self, cpid, *, exitcode):
19+
# This many iterations can be required, since some previously run
20+
# tests (e.g. test_ctypes) could have spawned a lot of children
21+
# very quickly.
22+
for _ in support.sleeping_retry(support.SHORT_TIMEOUT):
23+
# wait3() shouldn't hang, but some of the buildbots seem to hang
24+
# in the forking tests. This is an attempt to fix the problem.
25+
spid, status, rusage = os.wait3(os.WNOHANG)
26+
if spid == cpid:
27+
break
28+
29+
self.assertEqual(spid, cpid)
30+
self.assertEqual(os.waitstatus_to_exitcode(status), exitcode)
31+
self.assertTrue(rusage)
32+
33+
def test_wait3_rusage_initialized(self):
34+
# Ensure a successful wait3() call where no child was ready to report
35+
# its exit status does not return uninitialized memory in the rusage
36+
# structure. See bpo-36279.
37+
args = [sys.executable, '-c', 'import sys; sys.stdin.read()']
38+
proc = subprocess.Popen(args, stdin=subprocess.PIPE)
39+
try:
40+
pid, status, rusage = os.wait3(os.WNOHANG)
41+
self.assertEqual(0, pid)
42+
self.assertEqual(0, status)
43+
self.assertEqual(0, sum(rusage))
44+
finally:
45+
proc.stdin.close()
46+
proc.wait()
47+
48+
49+
def tearDownModule():
50+
support.reap_children()
51+
52+
if __name__ == "__main__":
53+
unittest.main()

Lib/test/test_wait4.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""This test checks for correct wait4() behavior.
2+
"""
3+
4+
import os
5+
import sys
6+
import unittest
7+
from test.fork_wait import ForkWait
8+
from test import support
9+
10+
# If either of these do not exist, skip this test.
11+
if not support.has_fork_support:
12+
raise unittest.SkipTest("requires working os.fork()")
13+
14+
support.get_attribute(os, 'wait4')
15+
16+
17+
class Wait4Test(ForkWait):
18+
def wait_impl(self, cpid, *, exitcode):
19+
option = os.WNOHANG
20+
if sys.platform.startswith('aix'):
21+
# Issue #11185: wait4 is broken on AIX and will always return 0
22+
# with WNOHANG.
23+
option = 0
24+
for _ in support.sleeping_retry(support.SHORT_TIMEOUT):
25+
# wait4() shouldn't hang, but some of the buildbots seem to hang
26+
# in the forking tests. This is an attempt to fix the problem.
27+
spid, status, rusage = os.wait4(cpid, option)
28+
if spid == cpid:
29+
break
30+
self.assertEqual(spid, cpid)
31+
self.assertEqual(os.waitstatus_to_exitcode(status), exitcode)
32+
self.assertTrue(rusage)
33+
34+
def tearDownModule():
35+
support.reap_children()
36+
37+
if __name__ == "__main__":
38+
unittest.main()

0 commit comments

Comments
 (0)