Skip to content

Commit f910a11

Browse files
authored
Improve crash message: distinguish exit vs signal (#98)
When a forked test process exits with a non-zero exit code (signal == 0), the message previously said "CRASHED with signal 0" which is misleading — signal 0 is not a real signal and the process was not killed by one. Now: - Non-signal exit: "EXITED with status <N>" - Signal kill: "CRASHED with signal <N> (<NAME>)" e.g. "signal 11 (SIGSEGV)" signal.Signals() is called without a try/except because result.signal comes from os.WTERMSIG(), which extracts the signal from the kernel's wait status — the kernel can only kill a process with a valid signal number. Note: sys.exit() is caught by pytest as SystemExit and does not trigger report_process_crash; os._exit() bypasses the marshal pipe and does. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 8f59f07 commit f910a11

4 files changed

Lines changed: 43 additions & 6 deletions

File tree

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ UNRELEASED
22
==========
33

44
* Drop support for EOL Python versions: 3.7, 3.8, 3.9.
5+
* Print proper child process exit status.
56

67
v1.6.0
78
======

src/pytest_forked/__init__.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,23 @@ def runforked():
8383

8484
def report_process_crash(item, result):
8585
from _pytest._code import getfslineno
86+
import signal as signal_module
8687

8788
path, lineno = getfslineno(item)
88-
info = "%s:%s: running the test CRASHED with signal %d" % (
89-
path,
90-
lineno,
91-
result.signal,
92-
)
89+
if result.signal:
90+
sig_name = signal_module.Signals(result.signal).name
91+
info = "%s:%s: running the test CRASHED with signal %d (%s)" % (
92+
path,
93+
lineno,
94+
result.signal,
95+
sig_name,
96+
)
97+
else:
98+
info = "%s:%s: running the test EXITED with status %d" % (
99+
path,
100+
lineno,
101+
result.exitstatus,
102+
)
93103
from _pytest import runner
94104

95105
# pytest >= 4.1

testing/test_boxed.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,32 @@ def test_function():
6767
)
6868

6969

70+
@needsfork
71+
def test_crash_message_shows_signal_name(testdir):
72+
p1 = testdir.makepyfile(
73+
"""
74+
import os, signal
75+
def test_function():
76+
os.kill(os.getpid(), signal.SIGTERM)
77+
"""
78+
)
79+
result = testdir.runpytest(p1, "--forked")
80+
result.stdout.fnmatch_lines(["*CRASHED with signal 15 (SIGTERM)*", "*1 failed*"])
81+
82+
83+
@needsfork
84+
def test_crash_message_shows_exit_status(testdir):
85+
p1 = testdir.makepyfile(
86+
"""
87+
import os
88+
def test_function():
89+
os._exit(42)
90+
"""
91+
)
92+
result = testdir.runpytest(p1, "--forked")
93+
result.stdout.fnmatch_lines(["*EXITED with status 42*", "*1 failed*"])
94+
95+
7096
def test_is_not_boxed_by_default(testdir):
7197
config = testdir.parseconfig(testdir.tmpdir)
7298
assert not config.option.forked

testing/test_xfail_behavior.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def test_xfail(is_crashing, is_strict, testdir):
6969
reason_string = (
7070
f"reason: The process gets terminated; "
7171
f"pytest-forked reason: "
72-
f"*:*: running the test CRASHED with signal {sig_num:d}"
72+
f"*:*: running the test CRASHED with signal {sig_num:d}*"
7373
)
7474
if expected_lowercase == "xfailed" and PYTEST_GTE_7_2:
7575
short_test_summary += " - " + reason_string

0 commit comments

Comments
 (0)