mirror of
https://github.com/python/cpython.git
synced 2025-09-18 22:50:26 +00:00
Issue #18948: improve SuppressCoreFiles to include Windows crash popup suppression, and use it in more tests.
Patch by Valerie Lambert and Zachary Ware.
This commit is contained in:
parent
3ebbb04af2
commit
77e904e6a6
8 changed files with 87 additions and 94 deletions
|
@ -442,13 +442,6 @@ The :mod:`test.support` module defines the following functions:
|
||||||
A decorator for running tests that require support for symbolic links.
|
A decorator for running tests that require support for symbolic links.
|
||||||
|
|
||||||
|
|
||||||
.. function:: suppress_crash_popup()
|
|
||||||
|
|
||||||
A context manager that disables Windows Error Reporting dialogs using
|
|
||||||
`SetErrorMode <http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621%28v=vs.85%29.aspx>`_.
|
|
||||||
On other platforms it's a no-op.
|
|
||||||
|
|
||||||
|
|
||||||
.. decorator:: anticipate_failure(condition)
|
.. decorator:: anticipate_failure(condition)
|
||||||
|
|
||||||
A decorator to conditionally mark tests with
|
A decorator to conditionally mark tests with
|
||||||
|
@ -593,6 +586,21 @@ The :mod:`test.support` module defines the following classes:
|
||||||
Temporarily unset the environment variable ``envvar``.
|
Temporarily unset the environment variable ``envvar``.
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: SuppressCrashReport()
|
||||||
|
|
||||||
|
A context manager used to try to prevent crash dialog popups on tests that
|
||||||
|
are expected to crash a subprocess.
|
||||||
|
|
||||||
|
On Windows, it disables Windows Error Reporting dialogs using
|
||||||
|
`SetErrorMode <http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621.aspx>`_.
|
||||||
|
|
||||||
|
On UNIX, :func:`resource.setrlimit` is used to set
|
||||||
|
:attr:`resource.RLIMIT_CORE`'s soft limit to 0 to prevent coredump file
|
||||||
|
creation.
|
||||||
|
|
||||||
|
On both platforms, the old value is restored by :meth:`__exit__`.
|
||||||
|
|
||||||
|
|
||||||
.. class:: WarningsRecorder()
|
.. class:: WarningsRecorder()
|
||||||
|
|
||||||
Class used to record warnings for unit tests. See documentation of
|
Class used to record warnings for unit tests. See documentation of
|
||||||
|
|
|
@ -81,8 +81,7 @@ __all__ = [
|
||||||
"TestHandler", "Matcher", "can_symlink", "skip_unless_symlink",
|
"TestHandler", "Matcher", "can_symlink", "skip_unless_symlink",
|
||||||
"skip_unless_xattr", "import_fresh_module", "requires_zlib",
|
"skip_unless_xattr", "import_fresh_module", "requires_zlib",
|
||||||
"PIPE_MAX_SIZE", "failfast", "anticipate_failure", "run_with_tz",
|
"PIPE_MAX_SIZE", "failfast", "anticipate_failure", "run_with_tz",
|
||||||
"requires_gzip", "requires_bz2", "requires_lzma", "suppress_crash_popup",
|
"requires_gzip", "requires_bz2", "requires_lzma", "SuppressCrashReport"
|
||||||
"SuppressCoreFiles",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
|
@ -2013,27 +2012,67 @@ def skip_unless_xattr(test):
|
||||||
return test if ok else unittest.skip(msg)(test)
|
return test if ok else unittest.skip(msg)(test)
|
||||||
|
|
||||||
|
|
||||||
if sys.platform.startswith('win'):
|
class SuppressCrashReport:
|
||||||
@contextlib.contextmanager
|
"""Try to prevent a crash report from popping up.
|
||||||
def suppress_crash_popup():
|
|
||||||
"""Disable Windows Error Reporting dialogs using SetErrorMode."""
|
On Windows, don't display the Windows Error Reporting dialog. On UNIX,
|
||||||
# see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621%28v=vs.85%29.aspx
|
disable the creation of coredump file.
|
||||||
# GetErrorMode is not available on Windows XP and Windows Server 2003,
|
"""
|
||||||
# but SetErrorMode returns the previous value, so we can use that
|
old_value = None
|
||||||
import ctypes
|
|
||||||
k32 = ctypes.windll.kernel32
|
def __enter__(self):
|
||||||
SEM_NOGPFAULTERRORBOX = 0x02
|
"""On Windows, disable Windows Error Reporting dialogs using
|
||||||
old_error_mode = k32.SetErrorMode(SEM_NOGPFAULTERRORBOX)
|
SetErrorMode.
|
||||||
k32.SetErrorMode(old_error_mode | SEM_NOGPFAULTERRORBOX)
|
|
||||||
try:
|
On UNIX, try to save the previous core file size limit, then set
|
||||||
yield
|
soft limit to 0.
|
||||||
finally:
|
"""
|
||||||
k32.SetErrorMode(old_error_mode)
|
if sys.platform.startswith('win'):
|
||||||
else:
|
# see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621.aspx
|
||||||
# this is a no-op for other platforms
|
# GetErrorMode is not available on Windows XP and Windows Server 2003,
|
||||||
@contextlib.contextmanager
|
# but SetErrorMode returns the previous value, so we can use that
|
||||||
def suppress_crash_popup():
|
import ctypes
|
||||||
yield
|
self._k32 = ctypes.windll.kernel32
|
||||||
|
SEM_NOGPFAULTERRORBOX = 0x02
|
||||||
|
self.old_value = self._k32.SetErrorMode(SEM_NOGPFAULTERRORBOX)
|
||||||
|
self._k32.SetErrorMode(self.old_value | SEM_NOGPFAULTERRORBOX)
|
||||||
|
else:
|
||||||
|
if resource is not None:
|
||||||
|
try:
|
||||||
|
self.old_value = resource.getrlimit(resource.RLIMIT_CORE)
|
||||||
|
resource.setrlimit(resource.RLIMIT_CORE,
|
||||||
|
(0, self.old_value[1]))
|
||||||
|
except (ValueError, OSError):
|
||||||
|
pass
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
# Check if the 'Crash Reporter' on OSX was configured
|
||||||
|
# in 'Developer' mode and warn that it will get triggered
|
||||||
|
# when it is.
|
||||||
|
#
|
||||||
|
# This assumes that this context manager is used in tests
|
||||||
|
# that might trigger the next manager.
|
||||||
|
value = subprocess.Popen(['/usr/bin/defaults', 'read',
|
||||||
|
'com.apple.CrashReporter', 'DialogType'],
|
||||||
|
stdout=subprocess.PIPE).communicate()[0]
|
||||||
|
if value.strip() == b'developer':
|
||||||
|
print("this test triggers the Crash Reporter, "
|
||||||
|
"that is intentional", end='', flush=True)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *ignore_exc):
|
||||||
|
"""Restore Windows ErrorMode or core file behavior to initial value."""
|
||||||
|
if self.old_value is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if sys.platform.startswith('win'):
|
||||||
|
self._k32.SetErrorMode(self.old_value)
|
||||||
|
else:
|
||||||
|
if resource is not None:
|
||||||
|
try:
|
||||||
|
resource.setrlimit(resource.RLIMIT_CORE, self.old_value)
|
||||||
|
except (ValueError, OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def patch(test_instance, object_to_patch, attr_name, new_value):
|
def patch(test_instance, object_to_patch, attr_name, new_value):
|
||||||
|
@ -2068,42 +2107,3 @@ def patch(test_instance, object_to_patch, attr_name, new_value):
|
||||||
|
|
||||||
# actually override the attribute
|
# actually override the attribute
|
||||||
setattr(object_to_patch, attr_name, new_value)
|
setattr(object_to_patch, attr_name, new_value)
|
||||||
|
|
||||||
|
|
||||||
class SuppressCoreFiles:
|
|
||||||
|
|
||||||
"""Try to prevent core files from being created."""
|
|
||||||
old_limit = None
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
"""Try to save previous ulimit, then set the soft limit to 0."""
|
|
||||||
if resource is not None:
|
|
||||||
try:
|
|
||||||
self.old_limit = resource.getrlimit(resource.RLIMIT_CORE)
|
|
||||||
resource.setrlimit(resource.RLIMIT_CORE, (0, self.old_limit[1]))
|
|
||||||
except (ValueError, OSError):
|
|
||||||
pass
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
# Check if the 'Crash Reporter' on OSX was configured
|
|
||||||
# in 'Developer' mode and warn that it will get triggered
|
|
||||||
# when it is.
|
|
||||||
#
|
|
||||||
# This assumes that this context manager is used in tests
|
|
||||||
# that might trigger the next manager.
|
|
||||||
value = subprocess.Popen(['/usr/bin/defaults', 'read',
|
|
||||||
'com.apple.CrashReporter', 'DialogType'],
|
|
||||||
stdout=subprocess.PIPE).communicate()[0]
|
|
||||||
if value.strip() == b'developer':
|
|
||||||
print("this test triggers the Crash Reporter, "
|
|
||||||
"that is intentional", end='')
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
def __exit__(self, *ignore_exc):
|
|
||||||
"""Return core file behavior to default."""
|
|
||||||
if self.old_limit is None:
|
|
||||||
return
|
|
||||||
if resource is not None:
|
|
||||||
try:
|
|
||||||
resource.setrlimit(resource.RLIMIT_CORE, self.old_limit)
|
|
||||||
except (ValueError, OSError):
|
|
||||||
pass
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class CAPITest(unittest.TestCase):
|
||||||
|
|
||||||
@unittest.skipUnless(threading, 'Threading required for this test.')
|
@unittest.skipUnless(threading, 'Threading required for this test.')
|
||||||
def test_no_FatalError_infinite_loop(self):
|
def test_no_FatalError_infinite_loop(self):
|
||||||
with support.suppress_crash_popup():
|
with support.SuppressCrashReport():
|
||||||
p = subprocess.Popen([sys.executable, "-c",
|
p = subprocess.Popen([sys.executable, "-c",
|
||||||
'import _testcapi;'
|
'import _testcapi;'
|
||||||
'_testcapi.crash_no_current_thread()'],
|
'_testcapi.crash_no_current_thread()'],
|
||||||
|
|
|
@ -19,18 +19,6 @@ except ImportError:
|
||||||
|
|
||||||
TIMEOUT = 0.5
|
TIMEOUT = 0.5
|
||||||
|
|
||||||
try:
|
|
||||||
from resource import setrlimit, RLIMIT_CORE, error as resource_error
|
|
||||||
except ImportError:
|
|
||||||
prepare_subprocess = None
|
|
||||||
else:
|
|
||||||
def prepare_subprocess():
|
|
||||||
# don't create core file
|
|
||||||
try:
|
|
||||||
setrlimit(RLIMIT_CORE, (0, 0))
|
|
||||||
except (ValueError, resource_error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def expected_traceback(lineno1, lineno2, header, min_count=1):
|
def expected_traceback(lineno1, lineno2, header, min_count=1):
|
||||||
regex = header
|
regex = header
|
||||||
regex += ' File "<string>", line %s in func\n' % lineno1
|
regex += ' File "<string>", line %s in func\n' % lineno1
|
||||||
|
@ -59,10 +47,8 @@ class FaultHandlerTests(unittest.TestCase):
|
||||||
build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
|
build, and replace "Current thread 0x00007f8d8fbd9700" by "Current
|
||||||
thread XXX".
|
thread XXX".
|
||||||
"""
|
"""
|
||||||
options = {}
|
with support.SuppressCrashReport():
|
||||||
if prepare_subprocess:
|
process = script_helper.spawn_python('-c', code)
|
||||||
options['preexec_fn'] = prepare_subprocess
|
|
||||||
process = script_helper.spawn_python('-c', code, **options)
|
|
||||||
stdout, stderr = process.communicate()
|
stdout, stderr = process.communicate()
|
||||||
exitcode = process.wait()
|
exitcode = process.wait()
|
||||||
output = support.strip_python_stderr(stdout)
|
output = support.strip_python_stderr(stdout)
|
||||||
|
@ -101,8 +87,7 @@ class FaultHandlerTests(unittest.TestCase):
|
||||||
header=re.escape(header))
|
header=re.escape(header))
|
||||||
if other_regex:
|
if other_regex:
|
||||||
regex += '|' + other_regex
|
regex += '|' + other_regex
|
||||||
with support.suppress_crash_popup():
|
output, exitcode = self.get_output(code, filename)
|
||||||
output, exitcode = self.get_output(code, filename)
|
|
||||||
output = '\n'.join(output)
|
output = '\n'.join(output)
|
||||||
self.assertRegex(output, regex)
|
self.assertRegex(output, regex)
|
||||||
self.assertNotEqual(exitcode, 0)
|
self.assertNotEqual(exitcode, 0)
|
||||||
|
@ -232,8 +217,7 @@ faulthandler.disable()
|
||||||
faulthandler._sigsegv()
|
faulthandler._sigsegv()
|
||||||
""".strip()
|
""".strip()
|
||||||
not_expected = 'Fatal Python error'
|
not_expected = 'Fatal Python error'
|
||||||
with support.suppress_crash_popup():
|
stderr, exitcode = self.get_output(code)
|
||||||
stderr, exitcode = self.get_output(code)
|
|
||||||
stder = '\n'.join(stderr)
|
stder = '\n'.join(stderr)
|
||||||
self.assertTrue(not_expected not in stderr,
|
self.assertTrue(not_expected not in stderr,
|
||||||
"%r is present in %r" % (not_expected, stderr))
|
"%r is present in %r" % (not_expected, stderr))
|
||||||
|
|
|
@ -1231,7 +1231,7 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||||
|
|
||||||
def test_run_abort(self):
|
def test_run_abort(self):
|
||||||
# returncode handles signal termination
|
# returncode handles signal termination
|
||||||
with support.SuppressCoreFiles():
|
with support.SuppressCrashReport():
|
||||||
p = subprocess.Popen([sys.executable, "-c",
|
p = subprocess.Popen([sys.executable, "-c",
|
||||||
'import os; os.abort()'])
|
'import os; os.abort()'])
|
||||||
p.wait()
|
p.wait()
|
||||||
|
|
|
@ -306,7 +306,7 @@ class TestSupport(unittest.TestCase):
|
||||||
# args_from_interpreter_flags
|
# args_from_interpreter_flags
|
||||||
# can_symlink
|
# can_symlink
|
||||||
# skip_unless_symlink
|
# skip_unless_symlink
|
||||||
# SuppressCoreFiles
|
# SuppressCrashReport
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
|
|
|
@ -250,7 +250,7 @@ class SysModuleTest(unittest.TestCase):
|
||||||
|
|
||||||
sys.setrecursionlimit(%d)
|
sys.setrecursionlimit(%d)
|
||||||
f()""")
|
f()""")
|
||||||
with test.support.suppress_crash_popup():
|
with test.support.SuppressCrashReport():
|
||||||
for i in (50, 1000):
|
for i in (50, 1000):
|
||||||
sub = subprocess.Popen([sys.executable, '-c', code % i],
|
sub = subprocess.Popen([sys.executable, '-c', code % i],
|
||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE)
|
||||||
|
|
|
@ -839,7 +839,8 @@ class SubinterpThreadingTests(BaseTestCase):
|
||||||
|
|
||||||
_testcapi.run_in_subinterp(%r)
|
_testcapi.run_in_subinterp(%r)
|
||||||
""" % (subinterp_code,)
|
""" % (subinterp_code,)
|
||||||
rc, out, err = assert_python_failure("-c", script)
|
with test.support.SuppressCrashReport():
|
||||||
|
rc, out, err = assert_python_failure("-c", script)
|
||||||
self.assertIn("Fatal Python error: Py_EndInterpreter: "
|
self.assertIn("Fatal Python error: Py_EndInterpreter: "
|
||||||
"not the last thread", err.decode())
|
"not the last thread", err.decode())
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue