Merged revisions 80476 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r80476 | michael.foord | 2010-04-25 20:02:46 +0100 (Sun, 25 Apr 2010) | 1 line Adding unittest.removeHandler function / decorator for removing the signal.SIGINT signal handler. With tests and docs. ........
This commit is contained in:
parent
af30c5d32e
commit
de4ceabfd8
|
@ -95,40 +95,6 @@ need to derive from a specific class.
|
|||
A special-interest-group for discussion of testing, and testing tools,
|
||||
in Python.
|
||||
|
||||
.. _unittest-test-discovery:
|
||||
|
||||
Test Discovery
|
||||
--------------
|
||||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
unittest supports simple test discovery. For a project's tests to be
|
||||
compatible with test discovery they must all be importable from the top level
|
||||
directory of the project; i.e. they must all be in Python packages.
|
||||
|
||||
Test discovery is implemented in :meth:`TestLoader.discover`, but can also be
|
||||
used from the command line. The basic command line usage is::
|
||||
|
||||
cd project_directory
|
||||
python -m unittest discover
|
||||
|
||||
The ``discover`` sub-command has the following options:
|
||||
|
||||
-v, --verbose Verbose output
|
||||
-s directory Directory to start discovery ('.' default)
|
||||
-p pattern Pattern to match test files ('test*.py' default)
|
||||
-t directory Top level directory of project (default to
|
||||
start directory)
|
||||
|
||||
The -s, -p, & -t options can be passsed in as positional arguments. The
|
||||
following two command lines are equivalent::
|
||||
|
||||
python -m unittest discover -s project_directory -p '*_test.py'
|
||||
python -m unittest discover project_directory '*_test.py'
|
||||
|
||||
Test modules and packages can customize test loading and discovery by through
|
||||
the `load_tests protocol`_.
|
||||
|
||||
.. _unittest-minimal-example:
|
||||
|
||||
Basic example
|
||||
|
@ -1904,8 +1870,17 @@ allow the currently running test to complete, and the test run will then end
|
|||
and report all the results so far. A second control-c will raise a
|
||||
``KeyboardInterrupt`` in the usual way.
|
||||
|
||||
There are a few utility functions for framework authors to enable this
|
||||
functionality within test frameworks.
|
||||
The control-c handling signal handler attempts to remain compatible with code or
|
||||
tests that install their own :const:`signal.SIGINT` handler. If the ``unittest``
|
||||
handler is called but *isn't* the installed :const:`signal.SIGINT` handler,
|
||||
i.e. it has been replaced by the system under test and delegated to, then it
|
||||
calls the default handler. This will normally be the expected behavior by code
|
||||
that replaces an installed handler and delegates to it. For individual tests
|
||||
that need ``unittest`` control-c handling disabled the :func:`removeHandler`
|
||||
decorator can be used.
|
||||
|
||||
There are a few utility functions for framework authors to enable control-c
|
||||
handling functionality within test frameworks.
|
||||
|
||||
.. function:: installHandler()
|
||||
|
||||
|
@ -1919,9 +1894,23 @@ functionality within test frameworks.
|
|||
result stores a weak reference to it, so it doesn't prevent the result from
|
||||
being garbage collected.
|
||||
|
||||
Registering a :class:`TestResult` object has no side-effects if control-c
|
||||
handling is not enabled, so test frameworks can unconditionally register
|
||||
all results they create independently of whether or not handling is enabled.
|
||||
|
||||
.. function:: removeResult(result)
|
||||
|
||||
Remove a registered result. Once a result has been removed then
|
||||
:meth:`~TestResult.stop` will no longer be called on that result object in
|
||||
response to a control-c.
|
||||
|
||||
.. function:: removeHandler(function=None)
|
||||
|
||||
When called without arguments this function removes the control-c handler
|
||||
if it has been installed. This function can also be used as a test decorator
|
||||
to temporarily remove the handler whilst the test is being executed::
|
||||
|
||||
@unittest.removeHandler
|
||||
def test_signal_handling(self):
|
||||
...
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ __all__ = ['TestResult', 'TestCase', 'TestSuite',
|
|||
'TextTestRunner', 'TestLoader', 'FunctionTestCase', 'main',
|
||||
'defaultTestLoader', 'SkipTest', 'skip', 'skipIf', 'skipUnless',
|
||||
'expectedFailure', 'TextTestResult', 'installHandler',
|
||||
'registerResult', 'removeResult']
|
||||
'registerResult', 'removeResult', 'removeHandler']
|
||||
|
||||
# Expose obsolete functions for backwards compatibility
|
||||
__all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])
|
||||
|
@ -63,7 +63,7 @@ from .loader import (TestLoader, defaultTestLoader, makeSuite, getTestCaseNames,
|
|||
findTestCases)
|
||||
from .main import TestProgram, main
|
||||
from .runner import TextTestRunner, TextTestResult
|
||||
from .signals import installHandler, registerResult, removeResult
|
||||
from .signals import installHandler, registerResult, removeResult, removeHandler
|
||||
|
||||
# deprecated
|
||||
_TextTestResult = TextTestResult
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import signal
|
||||
import weakref
|
||||
|
||||
from functools import wraps
|
||||
|
||||
__unittest = True
|
||||
|
||||
|
||||
|
@ -36,3 +38,20 @@ def installHandler():
|
|||
default_handler = signal.getsignal(signal.SIGINT)
|
||||
_interrupt_handler = _InterruptHandler(default_handler)
|
||||
signal.signal(signal.SIGINT, _interrupt_handler)
|
||||
|
||||
|
||||
def removeHandler(method=None):
|
||||
if method is not None:
|
||||
@wraps(method)
|
||||
def inner(*args, **kwargs):
|
||||
initial = signal.getsignal(signal.SIGINT)
|
||||
removeHandler()
|
||||
try:
|
||||
return method(*args, **kwargs)
|
||||
finally:
|
||||
signal.signal(signal.SIGINT, initial)
|
||||
return inner
|
||||
|
||||
global _interrupt_handler
|
||||
if _interrupt_handler is not None:
|
||||
signal.signal(signal.SIGINT, _interrupt_handler.default_handler)
|
||||
|
|
|
@ -227,3 +227,24 @@ class TestBreak(unittest.TestCase):
|
|||
self.assertEqual(p.result, result)
|
||||
|
||||
self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler)
|
||||
|
||||
def testRemoveHandler(self):
|
||||
default_handler = signal.getsignal(signal.SIGINT)
|
||||
unittest.installHandler()
|
||||
unittest.removeHandler()
|
||||
self.assertEqual(signal.getsignal(signal.SIGINT), default_handler)
|
||||
|
||||
# check that calling removeHandler multiple times has no ill-effect
|
||||
unittest.removeHandler()
|
||||
self.assertEqual(signal.getsignal(signal.SIGINT), default_handler)
|
||||
|
||||
def testRemoveHandlerAsDecorator(self):
|
||||
default_handler = signal.getsignal(signal.SIGINT)
|
||||
unittest.installHandler()
|
||||
|
||||
@unittest.removeHandler
|
||||
def test():
|
||||
self.assertEqual(signal.getsignal(signal.SIGINT), default_handler)
|
||||
|
||||
test()
|
||||
self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler)
|
||||
|
|
Loading…
Reference in New Issue