Kaydet (Commit) 77e904e6 authored tarafından Antoine Pitrou's avatar Antoine Pitrou

Issue #18948: improve SuppressCoreFiles to include Windows crash popup…

Issue #18948: improve SuppressCoreFiles to include Windows crash popup suppression, and use it in more tests.
Patch by Valerie Lambert and Zachary Ware.
üst 3ebbb04a
...@@ -442,13 +442,6 @@ The :mod:`test.support` module defines the following functions: ...@@ -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: ...@@ -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__ = [ ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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: ...@@ -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): ...@@ -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): ...@@ -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() ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment