summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2020-03-31 18:08:12 (GMT)
committerGitHub <noreply@github.com>2020-03-31 18:08:12 (GMT)
commit278c1e159c970da6cd6683d18c6211f5118674cc (patch)
treebe6eb27edca136282245e7ce374a23ba05f25cf7
parent400e1dbcad93061f1f7ab4735202daaa5e731507 (diff)
downloadcpython-278c1e159c970da6cd6683d18c6211f5118674cc.zip
cpython-278c1e159c970da6cd6683d18c6211f5118674cc.tar.gz
cpython-278c1e159c970da6cd6683d18c6211f5118674cc.tar.bz2
bpo-40094: Add test.support.wait_process() (GH-19254)
Moreover, the following tests now check the child process exit code: * test_os.PtyTests * test_mailbox.test_lock_conflict() * test_tempfile.test_process_awareness() * test_uuid.testIssue8621() * multiprocessing resource tracker tests
-rw-r--r--Doc/library/test.rst15
-rw-r--r--Lib/test/_test_multiprocessing.py2
-rw-r--r--Lib/test/fork_wait.py11
-rw-r--r--Lib/test/support/__init__.py59
-rw-r--r--Lib/test/test_builtin.py3
-rw-r--r--Lib/test/test_logging.py23
-rw-r--r--Lib/test/test_mailbox.py2
-rw-r--r--Lib/test/test_os.py12
-rw-r--r--Lib/test/test_platform.py4
-rw-r--r--Lib/test/test_posix.py51
-rw-r--r--Lib/test/test_random.py3
-rw-r--r--Lib/test/test_socketserver.py4
-rw-r--r--Lib/test/test_ssl.py3
-rw-r--r--Lib/test/test_subprocess.py11
-rw-r--r--Lib/test/test_support.py7
-rw-r--r--Lib/test/test_tempfile.py10
-rw-r--r--Lib/test/test_tracemalloc.py5
-rw-r--r--Lib/test/test_uuid.py2
-rw-r--r--Misc/NEWS.d/next/Tests/2020-03-31-18-57-52.bpo-40094.m3fTJe.rst1
19 files changed, 125 insertions, 103 deletions
diff --git a/Doc/library/test.rst b/Doc/library/test.rst
index 54ad620d..c33465d 100644
--- a/Doc/library/test.rst
+++ b/Doc/library/test.rst
@@ -825,6 +825,21 @@ The :mod:`test.support` module defines the following functions:
target of the "as" clause, if there is one.
+.. function:: wait_process(pid, *, exitcode, timeout=None)
+
+ Wait until process *pid* completes and check that the process exit code is
+ *exitcode*.
+
+ Raise an :exc:`AssertionError` if the process exit code is not equal to
+ *exitcode*.
+
+ If the process runs longer than *timeout* seconds (:data:`SHORT_TIMEOUT` by
+ default), kill the process and raise an :exc:`AssertionError`. The timeout
+ feature is not available on Windows.
+
+ .. versionadded:: 3.9
+
+
.. function:: wait_threads_exit(timeout=60.0)
Context manager to wait until all threads created in the ``with`` statement
diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py
index 4a87b17..d00e928 100644
--- a/Lib/test/_test_multiprocessing.py
+++ b/Lib/test/_test_multiprocessing.py
@@ -5124,7 +5124,7 @@ class TestResourceTracker(unittest.TestCase):
pid = _resource_tracker._pid
if pid is not None:
os.kill(pid, signal.SIGKILL)
- os.waitpid(pid, 0)
+ support.wait_process(pid, exitcode=-signal.SIGKILL)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
_resource_tracker.ensure_running()
diff --git a/Lib/test/fork_wait.py b/Lib/test/fork_wait.py
index f6bbffe..8c17755 100644
--- a/Lib/test/fork_wait.py
+++ b/Lib/test/fork_wait.py
@@ -44,16 +44,7 @@ class ForkWait(unittest.TestCase):
pass
def wait_impl(self, cpid):
- for i in range(10):
- # waitpid() shouldn't hang, but some of the buildbots seem to hang
- # in the forking tests. This is an attempt to fix the problem.
- spid, status = os.waitpid(cpid, os.WNOHANG)
- if spid == cpid:
- break
- time.sleep(2 * SHORTSLEEP)
-
- self.assertEqual(spid, cpid)
- self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8))
+ support.wait_process(cpid, exitcode=0)
def test_wait(self):
for i in range(NUM_THREADS):
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 259c706..5b9aebb 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -3400,3 +3400,62 @@ class catch_threading_exception:
del self.exc_value
del self.exc_traceback
del self.thread
+
+
+def wait_process(pid, *, exitcode, timeout=None):
+ """
+ Wait until process pid completes and check that the process exit code is
+ exitcode.
+
+ Raise an AssertionError if the process exit code is not equal to exitcode.
+
+ If the process runs longer than timeout seconds (SHORT_TIMEOUT by default),
+ kill the process (if signal.SIGKILL is available) and raise an
+ AssertionError. The timeout feature is not available on Windows.
+ """
+ if os.name != "nt":
+ if timeout is None:
+ timeout = SHORT_TIMEOUT
+ t0 = time.monotonic()
+ deadline = t0 + timeout
+ sleep = 0.001
+ max_sleep = 0.1
+ while True:
+ pid2, status = os.waitpid(pid, os.WNOHANG)
+ if pid2 != 0:
+ break
+ # process is still running
+
+ dt = time.monotonic() - t0
+ if dt > SHORT_TIMEOUT:
+ try:
+ os.kill(pid, signal.SIGKILL)
+ os.waitpid(pid, 0)
+ except OSError:
+ # Ignore errors like ChildProcessError or PermissionError
+ pass
+
+ raise AssertionError(f"process {pid} is still running "
+ f"after {dt:.1f} seconds")
+
+ sleep = min(sleep * 2, max_sleep)
+ time.sleep(sleep)
+
+ if os.WIFEXITED(status):
+ exitcode2 = os.WEXITSTATUS(status)
+ elif os.WIFSIGNALED(status):
+ exitcode2 = -os.WTERMSIG(status)
+ else:
+ raise ValueError(f"invalid wait status: {status!r}")
+ else:
+ # Windows implementation
+ pid2, status = os.waitpid(pid, 0)
+ exitcode2 = (status >> 8)
+
+ if exitcode2 != exitcode:
+ raise AssertionError(f"process {pid} exited with code {exitcode2}, "
+ f"but exit code {exitcode} is expected")
+
+ # sanity check: it should not fail in practice
+ if pid2 != pid:
+ raise AssertionError(f"pid {pid2} != pid {pid}")
diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
index 1e9012e..86e5f1d 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -25,6 +25,7 @@ from itertools import product
from textwrap import dedent
from types import AsyncGeneratorType, FunctionType
from operator import neg
+from test import support
from test.support import (
EnvironmentVarGuard, TESTFN, check_warnings, swap_attr, unlink,
maybe_get_event_loop_policy)
@@ -1890,7 +1891,7 @@ class PtyTests(unittest.TestCase):
os.close(fd)
# Wait until the child process completes
- os.waitpid(pid, 0)
+ support.wait_process(pid, exitcode=0)
return lines
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index e223522..2ad3c5c 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -727,30 +727,19 @@ class HandlerTest(BaseTest):
locks_held__ready_to_fork.wait()
pid = os.fork()
- if pid == 0: # Child.
+ if pid == 0:
+ # Child process
try:
test_logger.info(r'Child process did not deadlock. \o/')
finally:
os._exit(0)
- else: # Parent.
+ else:
+ # Parent process
test_logger.info(r'Parent process returned from fork. \o/')
fork_happened__release_locks_and_end_thread.set()
lock_holder_thread.join()
- start_time = time.monotonic()
- while True:
- test_logger.debug('Waiting for child process.')
- waited_pid, status = os.waitpid(pid, os.WNOHANG)
- if waited_pid == pid:
- break # child process exited.
- if time.monotonic() - start_time > 7:
- break # so long? implies child deadlock.
- time.sleep(0.05)
- test_logger.debug('Done waiting.')
- if waited_pid != pid:
- os.kill(pid, signal.SIGKILL)
- waited_pid, status = os.waitpid(pid, 0)
- self.fail("child process deadlocked.")
- self.assertEqual(status, 0, msg="child process error")
+
+ support.wait_process(pid, exitcode=0)
class BadStream(object):
diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py
index 36a2653..fdda1d1 100644
--- a/Lib/test/test_mailbox.py
+++ b/Lib/test/test_mailbox.py
@@ -1092,7 +1092,7 @@ class _TestMboxMMDF(_TestSingleFile):
# Signal the child it can now release the lock and exit.
p.send(b'p')
# Wait for child to exit. Locking should now succeed.
- exited_pid, status = os.waitpid(pid, 0)
+ support.wait_process(pid, exitcode=0)
self._box.lock()
self._box.unlock()
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 9c96544..be85616 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -2792,8 +2792,7 @@ class PidTests(unittest.TestCase):
args = [sys.executable, '-c', 'pass']
# Add an implicit test for PyUnicode_FSConverter().
pid = os.spawnv(os.P_NOWAIT, FakePath(args[0]), args)
- status = os.waitpid(pid, 0)
- self.assertEqual(status, (pid, 0))
+ support.wait_process(pid, exitcode=0)
class SpawnTests(unittest.TestCase):
@@ -2877,14 +2876,7 @@ class SpawnTests(unittest.TestCase):
def test_nowait(self):
args = self.create_args()
pid = os.spawnv(os.P_NOWAIT, args[0], args)
- result = os.waitpid(pid, 0)
- self.assertEqual(result[0], pid)
- status = result[1]
- if hasattr(os, 'WIFEXITED'):
- self.assertTrue(os.WIFEXITED(status))
- self.assertEqual(os.WEXITSTATUS(status), self.exitcode)
- else:
- self.assertEqual(status, self.exitcode << 8)
+ support.wait_process(pid, exitcode=self.exitcode)
@requires_os_func('spawnve')
def test_spawnve_bytes(self):
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
index 3084663..f167fb1 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -236,9 +236,7 @@ class PlatformTest(unittest.TestCase):
else:
# parent
- cpid, sts = os.waitpid(pid, 0)
- self.assertEqual(cpid, pid)
- self.assertEqual(sts, 0)
+ support.wait_process(pid, exitcode=0)
def test_libc_ver(self):
# check that libc_ver(executable) doesn't raise an exception
diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py
index fad26d8..be121ae 100644
--- a/Lib/test/test_posix.py
+++ b/Lib/test/test_posix.py
@@ -37,6 +37,7 @@ def _supports_sched():
requires_sched = unittest.skipUnless(_supports_sched(), 'requires POSIX scheduler API')
+
class PosixTester(unittest.TestCase):
def setUp(self):
@@ -180,7 +181,6 @@ class PosixTester(unittest.TestCase):
@unittest.skipUnless(getattr(os, 'execve', None) in os.supports_fd, "test needs execve() to support the fd parameter")
@unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()")
- @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()")
def test_fexecve(self):
fp = os.open(sys.executable, os.O_RDONLY)
try:
@@ -189,7 +189,7 @@ class PosixTester(unittest.TestCase):
os.chdir(os.path.split(sys.executable)[0])
posix.execve(fp, [sys.executable, '-c', 'pass'], os.environ)
else:
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
finally:
os.close(fp)
@@ -1539,7 +1539,7 @@ class _PosixSpawnMixin:
"""
args = self.python_args('-c', script)
pid = self.spawn_func(args[0], args, os.environ)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
with open(pidfile) as f:
self.assertEqual(f.read(), str(pid))
@@ -1569,7 +1569,7 @@ class _PosixSpawnMixin:
args = self.python_args('-c', script)
pid = self.spawn_func(args[0], args,
{**os.environ, 'foo': 'bar'})
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
with open(envfile) as f:
self.assertEqual(f.read(), 'bar')
@@ -1580,7 +1580,7 @@ class _PosixSpawnMixin:
os.environ,
file_actions=None
)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
def test_empty_file_actions(self):
pid = self.spawn_func(
@@ -1589,7 +1589,7 @@ class _PosixSpawnMixin:
os.environ,
file_actions=[]
)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
def test_resetids_explicit_default(self):
pid = self.spawn_func(
@@ -1598,7 +1598,7 @@ class _PosixSpawnMixin:
os.environ,
resetids=False
)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
def test_resetids(self):
pid = self.spawn_func(
@@ -1607,7 +1607,7 @@ class _PosixSpawnMixin:
os.environ,
resetids=True
)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
def test_resetids_wrong_type(self):
with self.assertRaises(TypeError):
@@ -1622,7 +1622,7 @@ class _PosixSpawnMixin:
os.environ,
setpgroup=os.getpgrp()
)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
def test_setpgroup_wrong_type(self):
with self.assertRaises(TypeError):
@@ -1643,7 +1643,7 @@ class _PosixSpawnMixin:
os.environ,
setsigmask=[signal.SIGUSR1]
)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
def test_setsigmask_wrong_type(self):
with self.assertRaises(TypeError):
@@ -1684,7 +1684,8 @@ class _PosixSpawnMixin:
finally:
os.close(wfd)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
+
output = os.read(rfd, 100)
child_sid = int(output)
parent_sid = os.getsid(os.getpid())
@@ -1707,10 +1708,7 @@ class _PosixSpawnMixin:
finally:
signal.signal(signal.SIGUSR1, original_handler)
- pid2, status = os.waitpid(pid, 0)
- self.assertEqual(pid2, pid)
- self.assertTrue(os.WIFSIGNALED(status), status)
- self.assertEqual(os.WTERMSIG(status), signal.SIGUSR1)
+ support.wait_process(pid, exitcode=-signal.SIGUSR1)
def test_setsigdef_wrong_type(self):
with self.assertRaises(TypeError):
@@ -1744,7 +1742,7 @@ class _PosixSpawnMixin:
os.environ,
scheduler=(None, os.sched_param(priority))
)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
@requires_sched
@unittest.skipIf(sys.platform.startswith(('freebsd', 'netbsd')),
@@ -1764,7 +1762,7 @@ class _PosixSpawnMixin:
os.environ,
scheduler=(policy, os.sched_param(priority))
)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
def test_multiple_file_actions(self):
file_actions = [
@@ -1776,7 +1774,7 @@ class _PosixSpawnMixin:
self.NOOP_PROGRAM,
os.environ,
file_actions=file_actions)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
def test_bad_file_actions(self):
args = self.NOOP_PROGRAM
@@ -1822,7 +1820,8 @@ class _PosixSpawnMixin:
args = self.python_args('-c', script)
pid = self.spawn_func(args[0], args, os.environ,
file_actions=file_actions)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+
+ support.wait_process(pid, exitcode=0)
with open(outfile) as f:
self.assertEqual(f.read(), 'hello')
@@ -1840,7 +1839,8 @@ class _PosixSpawnMixin:
args = self.python_args('-c', script)
pid = self.spawn_func(args[0], args, os.environ,
file_actions=[(os.POSIX_SPAWN_CLOSE, 0)])
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+
+ support.wait_process(pid, exitcode=0)
with open(closefile) as f:
self.assertEqual(f.read(), 'is closed %d' % errno.EBADF)
@@ -1858,7 +1858,7 @@ class _PosixSpawnMixin:
args = self.python_args('-c', script)
pid = self.spawn_func(args[0], args, os.environ,
file_actions=file_actions)
- self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ support.wait_process(pid, exitcode=0)
with open(dupfile) as f:
self.assertEqual(f.read(), 'hello')
@@ -1890,13 +1890,12 @@ class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin):
spawn_args = (program, '-I', '-S', '-c', 'pass')
code = textwrap.dedent("""
import os
+ from test import support
+
args = %a
pid = os.posix_spawnp(args[0], args, os.environ)
- pid2, status = os.waitpid(pid, 0)
- if pid2 != pid:
- raise Exception(f"pid {pid2} != {pid}")
- if status != 0:
- raise Exception(f"status {status} != 0")
+
+ support.wait_process(pid, exitcode=0)
""" % (spawn_args,))
# Use a subprocess to test os.posix_spawnp() with a modified PATH
diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py
index c147105..548af70 100644
--- a/Lib/test/test_random.py
+++ b/Lib/test/test_random.py
@@ -1103,8 +1103,7 @@ class TestModule(unittest.TestCase):
child_val = eval(f.read())
self.assertNotEqual(val, child_val)
- pid, status = os.waitpid(pid, 0)
- self.assertEqual(status, 0)
+ support.wait_process(pid, exitcode=0)
if __name__ == "__main__":
diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py
index 85382a0..f818df0 100644
--- a/Lib/test/test_socketserver.py
+++ b/Lib/test/test_socketserver.py
@@ -65,9 +65,7 @@ def simple_subprocess(testcase):
except:
raise
finally:
- pid2, status = os.waitpid(pid, 0)
- testcase.assertEqual(pid2, pid)
- testcase.assertEqual(72 << 8, status)
+ test.support.wait_process(pid, exitcode=72)
class SocketServerTest(unittest.TestCase):
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 0093a49..4184665 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -408,8 +408,7 @@ class BasicSocketTests(unittest.TestCase):
else:
os.close(wfd)
self.addCleanup(os.close, rfd)
- _, status = os.waitpid(pid, 0)
- self.assertEqual(status, 0)
+ support.wait_process(pid, exitcode=0)
child_random = os.read(rfd, 16)
self.assertEqual(len(child_random), 16)
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index 868f279..7cf31e1 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -3114,12 +3114,10 @@ class POSIXProcessTestCase(BaseTestCase):
proc = subprocess.Popen(args)
# Wait until the real process completes to avoid zombie process
- pid = proc.pid
- pid, status = os.waitpid(pid, 0)
- self.assertEqual(status, 0)
+ support.wait_process(proc.pid, exitcode=0)
status = _testcapi.W_STOPCODE(3)
- with mock.patch('subprocess.os.waitpid', return_value=(pid, status)):
+ with mock.patch('subprocess.os.waitpid', return_value=(proc.pid, status)):
returncode = proc.wait()
self.assertEqual(returncode, -3)
@@ -3130,10 +3128,7 @@ class POSIXProcessTestCase(BaseTestCase):
proc = subprocess.Popen(ZERO_RETURN_CMD)
# wait until the process completes without using the Popen APIs.
- pid, status = os.waitpid(proc.pid, 0)
- self.assertEqual(pid, proc.pid)
- self.assertTrue(os.WIFEXITED(status), status)
- self.assertEqual(os.WEXITSTATUS(status), 0)
+ support.wait_process(proc.pid, exitcode=0)
# returncode is still None but the process completed.
self.assertIsNone(proc.returncode)
diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py
index 175f7c8..99a4cad 100644
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -176,13 +176,10 @@ class TestSupport(unittest.TestCase):
with support.temp_cwd() as temp_path:
pid = os.fork()
if pid != 0:
- # parent process (child has pid == 0)
+ # parent process
# wait for the child to terminate
- (pid, status) = os.waitpid(pid, 0)
- if status != 0:
- raise AssertionError(f"Child process failed with exit "
- f"status indication 0x{status:x}.")
+ support.wait_process(pid, exitcode=0)
# Make sure that temp_path is still present. When the child
# process leaves the 'temp_cwd'-context, the __exit__()-
diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py
index 5fe9506..524ab7c 100644
--- a/Lib/test/test_tempfile.py
+++ b/Lib/test/test_tempfile.py
@@ -200,15 +200,7 @@ class TestRandomNameSequence(BaseTestCase):
child_value = os.read(read_fd, len(parent_value)).decode("ascii")
finally:
if pid:
- # best effort to ensure the process can't bleed out
- # via any bugs above
- try:
- os.kill(pid, signal.SIGKILL)
- except OSError:
- pass
-
- # Read the process exit status to avoid zombie process
- os.waitpid(pid, 0)
+ support.wait_process(pid, exitcode=0)
os.close(read_fd)
os.close(write_fd)
diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py
index 7283d9c..635a9d3 100644
--- a/Lib/test/test_tracemalloc.py
+++ b/Lib/test/test_tracemalloc.py
@@ -314,10 +314,7 @@ class TestTracemallocEnabled(unittest.TestCase):
finally:
os._exit(exitcode)
else:
- pid2, status = os.waitpid(pid, 0)
- self.assertTrue(os.WIFEXITED(status))
- exitcode = os.WEXITSTATUS(status)
- self.assertEqual(exitcode, 0)
+ support.wait_process(pid, exitcode=0)
class TestSnapshot(unittest.TestCase):
diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py
index 27fc56d..0b267f4 100644
--- a/Lib/test/test_uuid.py
+++ b/Lib/test/test_uuid.py
@@ -657,7 +657,7 @@ class BaseTestUUID:
os.close(fds[1])
self.addCleanup(os.close, fds[0])
parent_value = self.uuid.uuid4().hex
- os.waitpid(pid, 0)
+ support.wait_process(pid, exitcode=0)
child_value = os.read(fds[0], 100).decode('latin-1')
self.assertNotEqual(parent_value, child_value)
diff --git a/Misc/NEWS.d/next/Tests/2020-03-31-18-57-52.bpo-40094.m3fTJe.rst b/Misc/NEWS.d/next/Tests/2020-03-31-18-57-52.bpo-40094.m3fTJe.rst
new file mode 100644
index 0000000..cae001b
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2020-03-31-18-57-52.bpo-40094.m3fTJe.rst
@@ -0,0 +1 @@
+Add :func:`test.support.wait_process` function.