#8155: Preserve backward compatibility for test_support.check_warnings(). Add regression tests.

This commit is contained in:
Florent Xicluna 2010-03-18 19:51:47 +00:00
parent f3e9b2a996
commit 735885428d
3 changed files with 87 additions and 65 deletions

View File

@ -90,17 +90,17 @@ The goal for regression testing is to try to break code. This leads to a few
guidelines to be followed: guidelines to be followed:
* The testing suite should exercise all classes, functions, and constants. This * The testing suite should exercise all classes, functions, and constants. This
includes not just the external API that is to be presented to the outside world includes not just the external API that is to be presented to the outside
but also "private" code. world but also "private" code.
* Whitebox testing (examining the code being tested when the tests are being * Whitebox testing (examining the code being tested when the tests are being
written) is preferred. Blackbox testing (testing only the published user written) is preferred. Blackbox testing (testing only the published user
interface) is not complete enough to make sure all boundary and edge cases are interface) is not complete enough to make sure all boundary and edge cases
tested. are tested.
* Make sure all possible values are tested including invalid ones. This makes * Make sure all possible values are tested including invalid ones. This makes
sure that not only all valid values are acceptable but also that improper values sure that not only all valid values are acceptable but also that improper
are handled correctly. values are handled correctly.
* Exhaust as many code paths as possible. Test where branching occurs and thus * Exhaust as many code paths as possible. Test where branching occurs and thus
tailor input to make sure as many different paths through the code are taken. tailor input to make sure as many different paths through the code are taken.
@ -120,8 +120,8 @@ guidelines to be followed:
behavior from side-effects of importing a module. behavior from side-effects of importing a module.
* Try to maximize code reuse. On occasion, tests will vary by something as small * Try to maximize code reuse. On occasion, tests will vary by something as small
as what type of input is used. Minimize code duplication by subclassing a basic as what type of input is used. Minimize code duplication by subclassing a
test class with a class that specifies the input:: basic test class with a class that specifies the input::
class TestFuncAcceptsSequences(unittest.TestCase): class TestFuncAcceptsSequences(unittest.TestCase):
@ -155,10 +155,10 @@ Running tests using :mod:`test.regrtest`
suite. Running the script by itself automatically starts running all regression suite. Running the script by itself automatically starts running all regression
tests in the :mod:`test` package. It does this by finding all modules in the tests in the :mod:`test` package. It does this by finding all modules in the
package whose name starts with ``test_``, importing them, and executing the package whose name starts with ``test_``, importing them, and executing the
function :func:`test_main` if present. The names of tests to execute may also be function :func:`test_main` if present. The names of tests to execute may also
passed to the script. Specifying a single regression test (:program:`python be passed to the script. Specifying a single regression test (:program:`python
regrtest.py` :option:`test_spam.py`) will minimize output and only print whether regrtest.py` :option:`test_spam.py`) will minimize output and only print
the test passed or failed and thus minimize output. whether the test passed or failed and thus minimize output.
Running :mod:`test.regrtest` directly allows what resources are available for Running :mod:`test.regrtest` directly allows what resources are available for
tests to use to be set. You do this by using the :option:`-u` command-line tests to use to be set. You do this by using the :option:`-u` command-line
@ -173,10 +173,10 @@ list of all resources and more command-line options, run :program:`python
regrtest.py` :option:`-h`. regrtest.py` :option:`-h`.
Some other ways to execute the regression tests depend on what platform the Some other ways to execute the regression tests depend on what platform the
tests are being executed on. On Unix, you can run :program:`make` :option:`test` tests are being executed on. On Unix, you can run :program:`make`
at the top-level directory where Python was built. On Windows, executing :option:`test` at the top-level directory where Python was built. On Windows,
:program:`rt.bat` from your :file:`PCBuild` directory will run all regression executing :program:`rt.bat` from your :file:`PCBuild` directory will run all
tests. regression tests.
:mod:`test.test_support` --- Utility functions for tests :mod:`test.test_support` --- Utility functions for tests
@ -209,8 +209,9 @@ This module defines the following exceptions:
.. exception:: ResourceDenied .. exception:: ResourceDenied
Subclass of :exc:`unittest.SkipTest`. Raised when a resource (such as a network Subclass of :exc:`unittest.SkipTest`. Raised when a resource (such as a
connection) is not available. Raised by the :func:`requires` function. network connection) is not available. Raised by the :func:`requires`
function.
The :mod:`test.test_support` module defines the following constants: The :mod:`test.test_support` module defines the following constants:
@ -256,22 +257,23 @@ The :mod:`test.test_support` module defines the following functions:
.. function:: requires(resource[, msg]) .. function:: requires(resource[, msg])
Raise :exc:`ResourceDenied` if *resource* is not available. *msg* is the Raise :exc:`ResourceDenied` if *resource* is not available. *msg* is the
argument to :exc:`ResourceDenied` if it is raised. Always returns True if called argument to :exc:`ResourceDenied` if it is raised. Always returns
by a function whose ``__name__`` is ``'__main__'``. Used when tests are executed :const:`True` if called by a function whose ``__name__`` is ``'__main__'``.
by :mod:`test.regrtest`. Used when tests are executed by :mod:`test.regrtest`.
.. function:: findfile(filename) .. function:: findfile(filename)
Return the path to the file named *filename*. If no match is found *filename* is Return the path to the file named *filename*. If no match is found
returned. This does not equal a failure since it could be the path to the file. *filename* is returned. This does not equal a failure since it could be the
path to the file.
.. function:: run_unittest(*classes) .. function:: run_unittest(*classes)
Execute :class:`unittest.TestCase` subclasses passed to the function. The Execute :class:`unittest.TestCase` subclasses passed to the function. The
function scans the classes for methods starting with the prefix ``test_`` and function scans the classes for methods starting with the prefix ``test_``
executes the tests individually. and executes the tests individually.
It is also legal to pass strings as parameters; these should be keys in It is also legal to pass strings as parameters; these should be keys in
``sys.modules``. Each associated module will be scanned by ``sys.modules``. Each associated module will be scanned by
@ -284,7 +286,7 @@ The :mod:`test.test_support` module defines the following functions:
This will run all tests defined in the named module. This will run all tests defined in the named module.
.. function:: check_warnings(*filters, quiet=False) .. function:: check_warnings(*filters, quiet=None)
A convenience wrapper for ``warnings.catch_warnings()`` that makes A convenience wrapper for ``warnings.catch_warnings()`` that makes
it easier to test that a warning was correctly raised with a single it easier to test that a warning was correctly raised with a single
@ -292,30 +294,31 @@ The :mod:`test.test_support` module defines the following functions:
``warnings.catch_warnings(record=True)``. ``warnings.catch_warnings(record=True)``.
It accepts 2-tuples ``("message regexp", WarningCategory)`` as positional It accepts 2-tuples ``("message regexp", WarningCategory)`` as positional
arguments. When the optional keyword argument ``quiet`` is True, it does arguments. If there's some ``*filters`` defined, or if the optional keyword
not fail if a filter catches nothing. Without argument, it defaults to:: argument ``quiet`` is :const:`False`, it checks if the warnings are
effective. If some filter did not catch any warning, the test fails. If some
warnings are not caught, the test fails, too. To disable these checks, set
argument ``quiet`` to :const:`True`.
check_warnings(("", Warning), quiet=False) Without argument, it defaults to::
The main difference is that it verifies the warnings raised. If some filter check_warnings(("", Warning), quiet=True)
did not catch any warning, the test fails. If some warnings are not caught,
the test fails, too. To disable these checks, use argument ``quiet=True``.
Another significant difference is that on entry to the context manager, a Additionally, on entry to the context manager, a :class:`WarningRecorder`
:class:`WarningRecorder` instance is returned instead of a simple list. instance is returned. The underlying warnings list is available via the
The underlying warnings list is available via the recorder object's recorder object's :attr:`warnings` attribute, while the attributes of the
:attr:`warnings` attribute, while the attributes of the last raised last raised warning are also accessible directly on the object. If no
warning are also accessible directly on the object. If no warning has warning has been raised, then the latter attributes will all be
been raised, then the latter attributes will all be :const:`None`. :const:`None`.
A :meth:`reset` method is also provided on the recorder object. This A :meth:`reset` method is also provided on the recorder object. This
method simply clears the warning list. method simply clears the warnings list.
The context manager may be used like this:: The context manager may be used like this::
import warnings import warnings
with check_warnings(): with check_warnings(quiet=False):
exec('assert(False, "Hey!")') exec('assert(False, "Hey!")')
warnings.warn(UserWarning("Hide me!")) warnings.warn(UserWarning("Hide me!"))
@ -337,7 +340,6 @@ The :mod:`test.test_support` module defines the following functions:
.. versionadded:: 2.6 .. versionadded:: 2.6
.. versionchanged:: 2.7 .. versionchanged:: 2.7
The test fails when the context manager do not catch any warning.
New optional attributes ``*filters`` and ``quiet``. New optional attributes ``*filters`` and ``quiet``.
@ -348,8 +350,9 @@ The :mod:`test.test_support` module defines the following functions:
If ``sys.py3kwarning == 0``, it checks that no warning is raised. If ``sys.py3kwarning == 0``, it checks that no warning is raised.
It accepts 2-tuples ``("message regexp", WarningCategory)`` as positional It accepts 2-tuples ``("message regexp", WarningCategory)`` as positional
arguments. When the optional keyword argument ``quiet`` is True, it does arguments. When the optional keyword argument ``quiet`` is :const:`True`, it
not fail if a filter catches nothing. Without argument, it defaults to:: does not fail if a filter catches nothing. Without argument, it defaults
to::
check_py3k_warnings(("", DeprecationWarning), quiet=False) check_py3k_warnings(("", DeprecationWarning), quiet=False)
@ -432,11 +435,11 @@ The :mod:`test.test_support` module defines the following classes:
.. versionadded:: 2.6 .. versionadded:: 2.6
.. class:: EnvironmentVarGuard() .. class:: EnvironmentVarGuard()
Class used to temporarily set or unset environment variables. Instances can be Class used to temporarily set or unset environment variables. Instances can
used as a context manager and have a complete dictionary interface for be used as a context manager and have a complete dictionary interface for
querying/modifying the underlying ``os.environ``. After exit from the context querying/modifying the underlying ``os.environ``. After exit from the
manager all changes to environment variables done through this instance will context manager all changes to environment variables done through this
be rolled back. instance will be rolled back.
.. versionadded:: 2.6 .. versionadded:: 2.6
.. versionchanged:: 2.7 .. versionchanged:: 2.7
@ -445,7 +448,8 @@ The :mod:`test.test_support` module defines the following classes:
.. method:: EnvironmentVarGuard.set(envvar, value) .. method:: EnvironmentVarGuard.set(envvar, value)
Temporarily set the environment variable ``envvar`` to the value of ``value``. Temporarily set the environment variable ``envvar`` to the value of
``value``.
.. method:: EnvironmentVarGuard.unset(envvar) .. method:: EnvironmentVarGuard.unset(envvar)
@ -459,4 +463,3 @@ The :mod:`test.test_support` module defines the following classes:
:func:`check_warnings` above for more details. :func:`check_warnings` above for more details.
.. versionadded:: 2.6 .. versionadded:: 2.6

View File

@ -577,14 +577,19 @@ def check_warnings(*filters, **kwargs):
Optional argument: Optional argument:
- if 'quiet' is True, it does not fail if a filter catches nothing - if 'quiet' is True, it does not fail if a filter catches nothing
(default False) (default True without argument,
default False if some filters are defined)
Without argument, it defaults to: Without argument, it defaults to:
check_warnings(("", Warning), quiet=False) check_warnings(("", Warning), quiet=True)
""" """
quiet = kwargs.get('quiet')
if not filters: if not filters:
filters = (("", Warning),) filters = (("", Warning),)
return _filterwarnings(filters, kwargs.get('quiet')) # Preserve backward compatibility
if quiet is None:
quiet = True
return _filterwarnings(filters, quiet)
@contextlib.contextmanager @contextlib.contextmanager

View File

@ -633,19 +633,33 @@ class CatchWarningTests(BaseTest):
def test_check_warnings(self): def test_check_warnings(self):
# Explicit tests for the test_support convenience wrapper # Explicit tests for the test_support convenience wrapper
wmod = self.module wmod = self.module
if wmod is sys.modules['warnings']: if wmod is not sys.modules['warnings']:
with test_support.check_warnings() as w: return
self.assertEqual(w.warnings, []) with test_support.check_warnings(quiet=False) as w:
wmod.simplefilter("always") self.assertEqual(w.warnings, [])
wmod.warn("foo") wmod.simplefilter("always")
self.assertEqual(str(w.message), "foo") wmod.warn("foo")
wmod.warn("bar") self.assertEqual(str(w.message), "foo")
self.assertEqual(str(w.message), "bar") wmod.warn("bar")
self.assertEqual(str(w.warnings[0].message), "foo") self.assertEqual(str(w.message), "bar")
self.assertEqual(str(w.warnings[1].message), "bar") self.assertEqual(str(w.warnings[0].message), "foo")
w.reset() self.assertEqual(str(w.warnings[1].message), "bar")
self.assertEqual(w.warnings, []) w.reset()
self.assertEqual(w.warnings, [])
with test_support.check_warnings():
# defaults to quiet=True without argument
pass
with test_support.check_warnings(('foo', UserWarning)):
wmod.warn("foo")
with self.assertRaises(AssertionError):
with test_support.check_warnings(('', RuntimeWarning)):
# defaults to quiet=False with argument
pass
with self.assertRaises(AssertionError):
with test_support.check_warnings(('foo', RuntimeWarning)):
wmod.warn("foo")
class CCatchWarningTests(CatchWarningTests): class CCatchWarningTests(CatchWarningTests):