Skip to content

Commit 2097b9e

Browse files
authored
[2.7] bpo-30764: test_subprocess uses SuppressCrashReport (#2405) (#2412)
* bpo-30764: Backport support.SuppressCrashReport Backport test.support.SuppressCrashReport context-manager from master. Drop the Windows implementation since it depends on msvcrt.CrtSetReportMode() which isn't available on Python 2.7. * bpo-30764: test_subprocess uses SuppressCrashReport (#2405) bpo-30764, bpo-29335: test_child_terminated_in_stopped_state() of test_subprocess now uses support.SuppressCrashReport() to prevent the creation of a core dump on FreeBSD. (cherry picked from commit cdee3f1)
1 parent 8284883 commit 2097b9e

File tree

2 files changed

+103
-27
lines changed

2 files changed

+103
-27
lines changed

Lib/test/support/__init__.py

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import struct
2424
import sysconfig
2525
import types
26+
2627
try:
2728
import thread
2829
except ImportError:
@@ -43,7 +44,8 @@
4344
"threading_cleanup", "reap_threads", "start_threads", "cpython_only",
4445
"check_impl_detail", "get_attribute", "py3k_bytes",
4546
"import_fresh_module", "threading_cleanup", "reap_children",
46-
"strip_python_stderr", "IPV6_ENABLED", "run_with_tz"]
47+
"strip_python_stderr", "IPV6_ENABLED", "run_with_tz",
48+
"SuppressCrashReport"]
4749

4850
class Error(Exception):
4951
"""Base class for regression test exceptions."""
@@ -1842,3 +1844,78 @@ def python_is_optimized():
18421844
if opt.startswith('-O'):
18431845
final_opt = opt
18441846
return final_opt not in ('', '-O0', '-Og')
1847+
1848+
1849+
class SuppressCrashReport:
1850+
"""Try to prevent a crash report from popping up.
1851+
1852+
On Windows, don't display the Windows Error Reporting dialog. On UNIX,
1853+
disable the creation of coredump file.
1854+
"""
1855+
old_value = None
1856+
old_modes = None
1857+
1858+
def __enter__(self):
1859+
"""On Windows, disable Windows Error Reporting dialogs using
1860+
SetErrorMode.
1861+
1862+
On UNIX, try to save the previous core file size limit, then set
1863+
soft limit to 0.
1864+
"""
1865+
if sys.platform.startswith('win'):
1866+
# TODO: backport the Windows implementation
1867+
pass
1868+
else:
1869+
try:
1870+
import resource
1871+
except ImportError:
1872+
resource = None
1873+
1874+
if resource is not None:
1875+
try:
1876+
self.old_value = resource.getrlimit(resource.RLIMIT_CORE)
1877+
resource.setrlimit(resource.RLIMIT_CORE,
1878+
(0, self.old_value[1]))
1879+
except (ValueError, OSError):
1880+
pass
1881+
1882+
if sys.platform == 'darwin':
1883+
# Check if the 'Crash Reporter' on OSX was configured
1884+
# in 'Developer' mode and warn that it will get triggered
1885+
# when it is.
1886+
#
1887+
# This assumes that this context manager is used in tests
1888+
# that might trigger the next manager.
1889+
cmd = ['/usr/bin/defaults', 'read',
1890+
'com.apple.CrashReporter', 'DialogType']
1891+
proc = subprocess.Popen(cmd,
1892+
stdout=subprocess.PIPE,
1893+
stderr=subprocess.PIPE)
1894+
with proc:
1895+
stdout = proc.communicate()[0]
1896+
if stdout.strip() == b'developer':
1897+
sys.stdout.write("this test triggers the Crash Reporter, "
1898+
"that is intentional")
1899+
sys.stdout.flush()
1900+
1901+
return self
1902+
1903+
def __exit__(self, *ignore_exc):
1904+
"""Restore Windows ErrorMode or core file behavior to initial value."""
1905+
if self.old_value is None:
1906+
return
1907+
1908+
if sys.platform.startswith('win'):
1909+
# TODO: backport the Windows implementation
1910+
pass
1911+
else:
1912+
try:
1913+
import resource
1914+
except ImportError:
1915+
resource = None
1916+
1917+
if resource is not None:
1918+
try:
1919+
resource.setrlimit(resource.RLIMIT_CORE, self.old_value)
1920+
except (ValueError, OSError):
1921+
pass

Lib/test/test_subprocess.py

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010
import time
1111
import re
1212
import sysconfig
13+
import textwrap
1314

1415
try:
1516
import ctypes
1617
except ImportError:
1718
ctypes = None
19+
else:
20+
import ctypes.util
1821

1922
try:
2023
import resource
@@ -1262,43 +1265,39 @@ def test_pipe_cloexec(self):
12621265

12631266
self.assertEqual(p2.returncode, 0, "Unexpected error: " + repr(stderr))
12641267

1265-
_libc_file_extensions = {
1266-
'Linux': 'so.6',
1267-
'Darwin': 'dylib',
1268-
}
1269-
@unittest.skipIf(not ctypes, 'ctypes module required.')
1270-
@unittest.skipIf(platform.uname()[0] not in _libc_file_extensions,
1271-
'Test requires a libc this code can load with ctypes.')
1272-
@unittest.skipIf(not sys.executable, 'Test requires sys.executable.')
1268+
@unittest.skipIf(not ctypes, 'ctypes module required')
1269+
@unittest.skipIf(not sys.executable, 'Test requires sys.executable')
12731270
def test_child_terminated_in_stopped_state(self):
12741271
"""Test wait() behavior when waitpid returns WIFSTOPPED; issue29335."""
12751272
PTRACE_TRACEME = 0 # From glibc and MacOS (PT_TRACE_ME).
1276-
libc_name = 'libc.' + self._libc_file_extensions[platform.uname()[0]]
1273+
libc_name = ctypes.util.find_library('c')
12771274
libc = ctypes.CDLL(libc_name)
12781275
if not hasattr(libc, 'ptrace'):
1279-
raise unittest.SkipTest('ptrace() required.')
1280-
test_ptrace = subprocess.Popen(
1281-
[sys.executable, '-c', """if True:
1282-
import ctypes
1283-
libc = ctypes.CDLL({libc_name!r})
1284-
libc.ptrace({PTRACE_TRACEME}, 0, 0)
1285-
""".format(libc_name=libc_name, PTRACE_TRACEME=PTRACE_TRACEME)
1286-
])
1287-
if test_ptrace.wait() != 0:
1288-
raise unittest.SkipTest('ptrace() failed - unable to test.')
1289-
child = subprocess.Popen(
1290-
[sys.executable, '-c', """if True:
1276+
raise unittest.SkipTest('ptrace() required')
1277+
1278+
code = textwrap.dedent("""
12911279
import ctypes
1280+
from test.support import SuppressCrashReport
1281+
12921282
libc = ctypes.CDLL({libc_name!r})
12931283
libc.ptrace({PTRACE_TRACEME}, 0, 0)
1294-
libc.printf(ctypes.c_char_p(0xdeadbeef)) # Crash the process.
1295-
""".format(libc_name=libc_name, PTRACE_TRACEME=PTRACE_TRACEME)
1296-
])
1284+
""".format(libc_name=libc_name, PTRACE_TRACEME=PTRACE_TRACEME))
1285+
1286+
child = subprocess.Popen([sys.executable, '-c', code])
1287+
if child.wait() != 0:
1288+
raise unittest.SkipTest('ptrace() failed - unable to test')
1289+
1290+
code += textwrap.dedent("""
1291+
with SuppressCrashReport():
1292+
# Crash the process
1293+
libc.printf(ctypes.c_char_p(0xdeadbeef)) # Crash the process.
1294+
""")
1295+
child = subprocess.Popen([sys.executable, '-c', code])
12971296
try:
12981297
returncode = child.wait()
1299-
except Exception as e:
1298+
except:
13001299
child.kill() # Clean up the hung stopped process.
1301-
raise e
1300+
raise
13021301
self.assertNotEqual(0, returncode)
13031302
self.assertLess(returncode, 0) # signal death, likely SIGSEGV.
13041303

0 commit comments

Comments
 (0)