mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-09 22:49:57 +09:00
Merge pull request #3423 from deantvv/os-waitstatus-to-exitcode
os: implement `os.waitstatus_to_exitcode`
This commit is contained in:
93
Lib/test/support/__init__.py
vendored
93
Lib/test/support/__init__.py
vendored
@@ -122,6 +122,48 @@ __all__ = [
|
||||
"ALWAYS_EQ", "LARGEST", "SMALLEST"
|
||||
]
|
||||
|
||||
# Timeout in seconds for tests using a network server listening on the network
|
||||
# local loopback interface like 127.0.0.1.
|
||||
#
|
||||
# The timeout is long enough to prevent test failure: it takes into account
|
||||
# that the client and the server can run in different threads or even different
|
||||
# processes.
|
||||
#
|
||||
# The timeout should be long enough for connect(), recv() and send() methods
|
||||
# of socket.socket.
|
||||
LOOPBACK_TIMEOUT = 5.0
|
||||
if sys.platform == 'win32' and ' 32 bit (ARM)' in sys.version:
|
||||
# bpo-37553: test_socket.SendfileUsingSendTest is taking longer than 2
|
||||
# seconds on Windows ARM32 buildbot
|
||||
LOOPBACK_TIMEOUT = 10
|
||||
elif sys.platform == 'vxworks':
|
||||
LOOPBACK_TIMEOUT = 10
|
||||
|
||||
# Timeout in seconds for network requests going to the internet. The timeout is
|
||||
# short enough to prevent a test to wait for too long if the internet request
|
||||
# is blocked for whatever reason.
|
||||
#
|
||||
# Usually, a timeout using INTERNET_TIMEOUT should not mark a test as failed,
|
||||
# but skip the test instead: see transient_internet().
|
||||
INTERNET_TIMEOUT = 60.0
|
||||
|
||||
# Timeout in seconds to mark a test as failed if the test takes "too long".
|
||||
#
|
||||
# The timeout value depends on the regrtest --timeout command line option.
|
||||
#
|
||||
# If a test using SHORT_TIMEOUT starts to fail randomly on slow buildbots, use
|
||||
# LONG_TIMEOUT instead.
|
||||
SHORT_TIMEOUT = 30.0
|
||||
|
||||
# Timeout in seconds to detect when a test hangs.
|
||||
#
|
||||
# It is long enough to reduce the risk of test failure on the slowest Python
|
||||
# buildbots. It should not be used to mark a test as failed if the test takes
|
||||
# "too long". The timeout value depends on the regrtest --timeout command line
|
||||
# option.
|
||||
LONG_TIMEOUT = 5 * 60.0
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Base class for regression test exceptions."""
|
||||
|
||||
@@ -3378,3 +3420,54 @@ 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":
|
||||
import signal
|
||||
|
||||
if timeout is None:
|
||||
timeout = SHORT_TIMEOUT
|
||||
t0 = time.monotonic()
|
||||
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)
|
||||
else:
|
||||
# Windows implementation
|
||||
pid2, status = os.waitpid(pid, 0)
|
||||
|
||||
exitcode2 = os.waitstatus_to_exitcode(status)
|
||||
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}")
|
||||
16
Lib/test/test_posix.py
vendored
16
Lib/test/test_posix.py
vendored
@@ -1538,8 +1538,6 @@ class _PosixSpawnMixin:
|
||||
# test_close_file() to fail.
|
||||
return (sys.executable, '-I', '-S', *args)
|
||||
|
||||
# TODO: RUSTPYTHON: AttributeError: module 'test.support' has no attribute 'wait_process'
|
||||
@unittest.expectedFailure
|
||||
def test_returns_pid(self):
|
||||
pidfile = support.TESTFN
|
||||
self.addCleanup(support.unlink, pidfile)
|
||||
@@ -1586,8 +1584,6 @@ class _PosixSpawnMixin:
|
||||
with open(envfile) as f:
|
||||
self.assertEqual(f.read(), 'bar')
|
||||
|
||||
# TODO: RUSTPYTHON: AttributeError: module 'test.support' has no attribute 'wait_process'
|
||||
@unittest.expectedFailure
|
||||
def test_none_file_actions(self):
|
||||
pid = self.spawn_func(
|
||||
self.NOOP_PROGRAM[0],
|
||||
@@ -1597,8 +1593,6 @@ class _PosixSpawnMixin:
|
||||
)
|
||||
support.wait_process(pid, exitcode=0)
|
||||
|
||||
# TODO: RUSTPYTHON: AttributeError: module 'test.support' has no attribute 'wait_process'
|
||||
@unittest.expectedFailure
|
||||
def test_empty_file_actions(self):
|
||||
pid = self.spawn_func(
|
||||
self.NOOP_PROGRAM[0],
|
||||
@@ -1795,8 +1789,6 @@ class _PosixSpawnMixin:
|
||||
)
|
||||
support.wait_process(pid, exitcode=0)
|
||||
|
||||
# TODO: RUSTPYTHON: AttributeError: module 'test.support' has no attribute 'wait_process'
|
||||
@unittest.expectedFailure
|
||||
def test_multiple_file_actions(self):
|
||||
file_actions = [
|
||||
(os.POSIX_SPAWN_OPEN, 3, os.path.realpath(__file__), os.O_RDONLY, 0),
|
||||
@@ -1838,8 +1830,6 @@ class _PosixSpawnMixin:
|
||||
3, __file__ + '\0',
|
||||
os.O_RDONLY, 0)])
|
||||
|
||||
# TODO: RUSTPYTHON: AttributeError: module 'test.support' has no attribute 'wait_process'
|
||||
@unittest.expectedFailure
|
||||
def test_open_file(self):
|
||||
outfile = support.TESTFN
|
||||
self.addCleanup(support.unlink, outfile)
|
||||
@@ -1860,7 +1850,7 @@ class _PosixSpawnMixin:
|
||||
with open(outfile) as f:
|
||||
self.assertEqual(f.read(), 'hello')
|
||||
|
||||
# TODO: RUSTPYTHON: AttributeError: module 'test.support' has no attribute 'wait_process'
|
||||
# TODO: RUSTPYTHON: FileNotFoundError: [Errno 2] No such file or directory (os error 2): '@test_55144_tmp' -> 'None'
|
||||
@unittest.expectedFailure
|
||||
def test_close_file(self):
|
||||
closefile = support.TESTFN
|
||||
@@ -1881,8 +1871,6 @@ class _PosixSpawnMixin:
|
||||
with open(closefile) as f:
|
||||
self.assertEqual(f.read(), 'is closed %d' % errno.EBADF)
|
||||
|
||||
# TODO: RUSTPYTHON: AttributeError: module 'test.support' has no attribute 'wait_process'
|
||||
@unittest.expectedFailure
|
||||
def test_dup2(self):
|
||||
dupfile = support.TESTFN
|
||||
self.addCleanup(support.unlink, dupfile)
|
||||
@@ -1911,8 +1899,6 @@ class TestPosixSpawn(unittest.TestCase, _PosixSpawnMixin):
|
||||
class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin):
|
||||
spawn_func = getattr(posix, 'posix_spawnp', None)
|
||||
|
||||
# TODO: RUSTPYTHON: AttributeError: module 'test.support' has no attribute 'wait_process'
|
||||
@unittest.expectedFailure
|
||||
@support.skip_unless_symlink
|
||||
def test_posix_spawnp(self):
|
||||
# Use a symlink to create a program in its own temporary directory
|
||||
|
||||
@@ -1668,6 +1668,31 @@ pub(super) mod _os {
|
||||
Ok((loadavg[0], loadavg[1], loadavg[2]))
|
||||
}
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
#[pyfunction]
|
||||
fn waitstatus_to_exitcode(status: i32, vm: &VirtualMachine) -> PyResult<i32> {
|
||||
let status = u32::try_from(status)
|
||||
.map_err(|_| vm.new_value_error(format!("invalid WEXITSTATUS: {}", status)))?;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(windows))] {
|
||||
let status = status as libc::c_int;
|
||||
if libc::WIFEXITED(status) {
|
||||
return Ok(libc::WEXITSTATUS(status));
|
||||
}
|
||||
|
||||
if libc::WIFSIGNALED(status) {
|
||||
return Ok(-libc::WTERMSIG(status));
|
||||
}
|
||||
|
||||
Err(vm.new_value_error(format!("Invalid wait status: {}", status)))
|
||||
} else {
|
||||
i32::try_from(status.rotate_right(8))
|
||||
.map_err(|_| vm.new_value_error(format!("invalid wait status: {}", status)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(module = "os", name = "terminal_size")]
|
||||
#[derive(PyStructSequence)]
|
||||
|
||||
Reference in New Issue
Block a user