[Zope3-checkins] SVN: zope.testing/trunk/ - Added context-manager support to ``zope.testing.setupstack``
Jim Fulton
jim at zope.com
Sat Jan 28 21:12:59 UTC 2012
Log message for revision 124225:
- Added context-manager support to ``zope.testing.setupstack``
- Added the ``wait_until`` module, which makes it easier to deal with
non-deterministic timing issues.
- Renamed ``zope.testing.renormalizing.RENormalizing`` to
``zope.testing.renormalizing.OutputChecker``. The old name is an
alias.
- Updated tests to run with Python 3.
- More clearly labeled which features were supported by Python 3.
- Reorganized documentation.
Changed:
U zope.testing/trunk/CHANGES.txt
U zope.testing/trunk/README.txt
U zope.testing/trunk/setup.py
U zope.testing/trunk/src/zope/testing/formparser.txt
U zope.testing/trunk/src/zope/testing/loggingsupport.py
A zope.testing/trunk/src/zope/testing/loggingsupport.txt
U zope.testing/trunk/src/zope/testing/renormalizing/__init__.py
A zope.testing/trunk/src/zope/testing/renormalizing.txt
U zope.testing/trunk/src/zope/testing/setupstack.py
U zope.testing/trunk/src/zope/testing/setupstack.txt
U zope.testing/trunk/src/zope/testing/tests.py
A zope.testing/trunk/src/zope/testing/wait_until.py
A zope.testing/trunk/src/zope/testing/wait_until.txt
-=-
Modified: zope.testing/trunk/CHANGES.txt
===================================================================
--- zope.testing/trunk/CHANGES.txt 2012-01-27 21:57:17 UTC (rev 124224)
+++ zope.testing/trunk/CHANGES.txt 2012-01-28 21:12:57 UTC (rev 124225)
@@ -1,11 +1,22 @@
zope.testing Changelog
**********************
-4.0.1 (unreleased)
+4.1.0 (unreleased)
==================
-- None yet.
+- Added context-manager support to ``zope.testing.setupstack``
+- Added the ``wait_until`` module, which makes it easier to deal with
+ non-deterministic timing issues.
+
+- Renamed ``zope.testing.renormalizing.RENormalizing`` to
+ ``zope.testing.renormalizing.OutputChecker``. The old name is an
+ alias.
+
+- Updated tests to run with Python 3.
+
+- More clearly labeled which features were supported by Python 3.
+
4.0.0 (2011-11-09)
==================
Modified: zope.testing/trunk/README.txt
===================================================================
--- zope.testing/trunk/README.txt 2012-01-27 21:57:17 UTC (rev 124224)
+++ zope.testing/trunk/README.txt 2012-01-28 21:12:57 UTC (rev 124225)
@@ -4,65 +4,55 @@
.. contents::
-This package provides a number of testing frameworks. It includes a
-flexible test runner, and supports both doctest and unittest.
+This package provides a number of testing frameworks.
-cleanup.py
+cleanup
Provides a mixin class for cleaning up after tests that
make global changes.
-doctest.py
- Enhanced version of python's standard doctest.py.
- Better test count (one per block instead of one per docstring).
- See doctest.txt.
-
- (We need to merge this with the standard doctest module.)
-
-doctestunit.py
- Provides a pprint function that always sorts dictionary entries
- (pprint.pprint from the standard library doesn't sort very short ones,
- sometimes causing test failures when the internal order changes).
-
-formparser.py
+formparser
An HTML parser that extracts form information.
+ **Python 2 only**
+
This is intended to support functional tests that need to extract
information from HTML forms returned by the publisher.
See formparser.txt.
-loggingsupport.py
+loggingsupport
Support for testing logging code
If you want to test that your code generates proper log output, you
can create and install a handler that collects output.
-loghandler.py
+loghandler
Logging handler for tests that check logging output.
-module.py
+module
Lets a doctest pretend to be a Python module.
See module.txt.
-renormalizing.py
+renormalizing
Regular expression pattern normalizing output checker.
Useful for doctests.
-server.py
+server
Provides a simple HTTP server compatible with the zope.app.testing
functional testing API. Lets you interactively play with the system
under test. Helpful in debugging functional doctest failures.
-setupstack.py
+ **Python 2 only**
+
+setupstack
A simple framework for automating doctest set-up and tear-down.
See setupstack.txt.
-testrunner
- The test runner package. This is typically wrapped by a test.py script that
- sets up options to run a particular set of tests.
+wait_until
+ A small utility for dealing with timing non-determinism
+ See wait_until.txt.
-
Getting started
***************
Modified: zope.testing/trunk/setup.py
===================================================================
--- zope.testing/trunk/setup.py 2012-01-27 21:57:17 UTC (rev 124224)
+++ zope.testing/trunk/setup.py 2012-01-28 21:12:57 UTC (rev 124225)
@@ -38,18 +38,20 @@
chapters = '\n'.join([
open(os.path.join('src', 'zope', 'testing', name)).read()
for name in (
- 'formparser.txt',
- 'setupstack.txt',
+ 'formparser.txt',
+ 'loggingsupport.txt',
+ 'renormalizing.txt',
+ 'setupstack.txt',
+ 'wait_until.txt',
)])
long_description=(
open('README.txt').read()
+ '\n' +
- open('CHANGES.txt').read()
- + '\n' +
'Detailed Documentation\n'
'**********************\n'
+ '\n' + chapters
+ + '\n' + open('CHANGES.txt').read()
)
setup(
@@ -76,10 +78,10 @@
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Testing",
],
-
- packages=["zope",
- "zope.testing",
- "zope.testing.doctest",
+
+ packages=["zope",
+ "zope.testing",
+ "zope.testing.doctest",
"zope.testing.renormalizing"],
package_dir = {'': 'src'},
namespace_packages=['zope',],
Modified: zope.testing/trunk/src/zope/testing/formparser.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/formparser.txt 2012-01-27 21:57:17 UTC (rev 124224)
+++ zope.testing/trunk/src/zope/testing/formparser.txt 2012-01-28 21:12:57 UTC (rev 124225)
@@ -5,6 +5,9 @@
be extracted in order to re-submit it as part of a subsequent request.
The `zope.testing.formparser` module can be used for this purpose.
+NOTE
+ formparser doesn't support Python 3.
+
The scanner is implemented using the `FormParser` class. The
constructor arguments are the page data containing the form and
(optionally) the URL from which the page was retrieved:
Modified: zope.testing/trunk/src/zope/testing/loggingsupport.py
===================================================================
--- zope.testing/trunk/src/zope/testing/loggingsupport.py 2012-01-27 21:57:17 UTC (rev 124224)
+++ zope.testing/trunk/src/zope/testing/loggingsupport.py 2012-01-28 21:12:57 UTC (rev 124225)
@@ -11,64 +11,6 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Support for testing logging code
-
-If you want to test that your code generates proper log output, you
-can create and install a handler that collects output:
-
- >>> handler = InstalledHandler('foo.bar')
-
-The handler is installed into loggers for all of the names passed. In
-addition, the logger level is set to 1, which means, log
-everything. If you want to log less than everything, you can provide a
-level keyword argument. The level setting effects only the named
-loggers.
-
- >>> handler_with_levels = InstalledHandler('baz', level=logging.WARNING)
-
-Then, any log output is collected in the handler:
-
- >>> logging.getLogger('foo.bar').exception('eek')
- >>> logging.getLogger('foo.bar').info('blah blah')
-
- >>> for record in handler.records:
- ... print record.name, record.levelname
- ... print ' ', record.getMessage()
- foo.bar ERROR
- eek
- foo.bar INFO
- blah blah
-
-A similar effect can be gotten by just printing the handler:
-
- >>> print handler
- foo.bar ERROR
- eek
- foo.bar INFO
- blah blah
-
-After checking the log output, you need to uninstall the handler:
-
- >>> handler.uninstall()
- >>> handler_with_levels.uninstall()
-
-At which point, the handler won't get any more log output.
-Let's clear the handler:
-
- >>> handler.clear()
- >>> handler.records
- []
-
-And then log something:
-
- >>> logging.getLogger('foo.bar').info('blah')
-
-and, sure enough, we still have no output:
-
- >>> handler.records
- []
-"""
-
import logging
class Handler(logging.Handler):
Added: zope.testing/trunk/src/zope/testing/loggingsupport.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/loggingsupport.txt (rev 0)
+++ zope.testing/trunk/src/zope/testing/loggingsupport.txt 2012-01-28 21:12:57 UTC (rev 124225)
@@ -0,0 +1,59 @@
+Support for testing logging code
+================================
+
+If you want to test that your code generates proper log output, you
+can create and install a handler that collects output:
+
+ >>> from zope.testing.loggingsupport import InstalledHandler
+ >>> handler = InstalledHandler('foo.bar')
+
+The handler is installed into loggers for all of the names passed. In
+addition, the logger level is set to 1, which means, log
+everything. If you want to log less than everything, you can provide a
+level keyword argument. The level setting effects only the named
+loggers.
+
+ >>> import logging
+ >>> handler_with_levels = InstalledHandler('baz', level=logging.WARNING)
+
+Then, any log output is collected in the handler:
+
+ >>> logging.getLogger('foo.bar').exception('eek')
+ >>> logging.getLogger('foo.bar').info('blah blah')
+
+ >>> for record in handler.records:
+ ... print_(record.name, record.levelname)
+ ... print_(' ', record.getMessage())
+ foo.bar ERROR
+ eek
+ foo.bar INFO
+ blah blah
+
+A similar effect can be gotten by just printing the handler:
+
+ >>> print_(handler)
+ foo.bar ERROR
+ eek
+ foo.bar INFO
+ blah blah
+
+After checking the log output, you need to uninstall the handler:
+
+ >>> handler.uninstall()
+ >>> handler_with_levels.uninstall()
+
+At which point, the handler won't get any more log output.
+Let's clear the handler:
+
+ >>> handler.clear()
+ >>> handler.records
+ []
+
+And then log something:
+
+ >>> logging.getLogger('foo.bar').info('blah')
+
+and, sure enough, we still have no output:
+
+ >>> handler.records
+ []
Property changes on: zope.testing/trunk/src/zope/testing/loggingsupport.txt
___________________________________________________________________
Added: svn:eol-style
+ native
Modified: zope.testing/trunk/src/zope/testing/renormalizing/__init__.py
===================================================================
--- zope.testing/trunk/src/zope/testing/renormalizing/__init__.py 2012-01-27 21:57:17 UTC (rev 124224)
+++ zope.testing/trunk/src/zope/testing/renormalizing/__init__.py 2012-01-28 21:12:57 UTC (rev 124225)
@@ -24,244 +24,9 @@
# is not available on Python 2.4 which we still support.
#
##############################################################################
-r"""Regular expression pattern normalizing output checker
-
-The pattern-normalizing output checker extends the default output checker with
-an option to normalize expected and actual output.
-
-You specify a sequence of patterns and replacements. The replacements are
-applied to the expected and actual outputs before calling the default outputs
-checker. Let's look at an example. In this example, we have some times and
-addresses:
-
- >>> want = '''\
- ... <object object at 0xb7f14438>
- ... completed in 1.234 seconds.
- ... <BLANKLINE>
- ... <object object at 0xb7f14440>
- ... completed in 123.234 seconds.
- ... <BLANKLINE>
- ... <object object at 0xb7f14448>
- ... completed in .234 seconds.
- ... <BLANKLINE>
- ... <object object at 0xb7f14450>
- ... completed in 1.234 seconds.
- ... <BLANKLINE>
- ... '''
-
- >>> got = '''\
- ... <object object at 0xb7f14458>
- ... completed in 1.235 seconds.
- ...
- ... <object object at 0xb7f14460>
- ... completed in 123.233 seconds.
- ...
- ... <object object at 0xb7f14468>
- ... completed in .231 seconds.
- ...
- ... <object object at 0xb7f14470>
- ... completed in 1.23 seconds.
- ...
- ... '''
-
-We may wish to consider these two strings to match, even though they differ in
-actual addresses and times. The default output checker will consider them
-different:
-
- >>> doctest.OutputChecker().check_output(want, got, 0)
- False
-
-We'll use the RENormalizing to normalize both the wanted and gotten strings to
-ignore differences in times and addresses:
-
- >>> import re
- >>> checker = RENormalizing([
- ... (re.compile('[0-9]*[.][0-9]* seconds'), '<SOME NUMBER OF> seconds'),
- ... (re.compile('at 0x[0-9a-f]+'), 'at <SOME ADDRESS>'),
- ... ])
-
- >>> checker.check_output(want, got, 0)
- True
-
-Usual OutputChecker options work as expected:
-
- >>> want_ellided = '''\
- ... <object object at 0xb7f14438>
- ... completed in 1.234 seconds.
- ... ...
- ... <object object at 0xb7f14450>
- ... completed in 1.234 seconds.
- ... <BLANKLINE>
- ... '''
-
- >>> checker.check_output(want_ellided, got, 0)
- False
-
- >>> checker.check_output(want_ellided, got, doctest.ELLIPSIS)
- True
-
-When we get differencs, we output them with normalized text:
-
- >>> source = '''\
- ... >>> do_something()
- ... <object object at 0xb7f14438>
- ... completed in 1.234 seconds.
- ... ...
- ... <object object at 0xb7f14450>
- ... completed in 1.234 seconds.
- ... <BLANKLINE>
- ... '''
-
- >>> example = doctest.Example(source, want_ellided)
-
- >>> print checker.output_difference(example, got, 0)
- Expected:
- <object object at <SOME ADDRESS>>
- completed in <SOME NUMBER OF> seconds.
- ...
- <object object at <SOME ADDRESS>>
- completed in <SOME NUMBER OF> seconds.
- <BLANKLINE>
- Got:
- <object object at <SOME ADDRESS>>
- completed in <SOME NUMBER OF> seconds.
- <BLANKLINE>
- <object object at <SOME ADDRESS>>
- completed in <SOME NUMBER OF> seconds.
- <BLANKLINE>
- <object object at <SOME ADDRESS>>
- completed in <SOME NUMBER OF> seconds.
- <BLANKLINE>
- <object object at <SOME ADDRESS>>
- completed in <SOME NUMBER OF> seconds.
- <BLANKLINE>
- <BLANKLINE>
-
- >>> print checker.output_difference(example, got,
- ... doctest.REPORT_NDIFF)
- Differences (ndiff with -expected +actual):
- - <object object at <SOME ADDRESS>>
- - completed in <SOME NUMBER OF> seconds.
- - ...
- <object object at <SOME ADDRESS>>
- completed in <SOME NUMBER OF> seconds.
- <BLANKLINE>
- + <object object at <SOME ADDRESS>>
- + completed in <SOME NUMBER OF> seconds.
- + <BLANKLINE>
- + <object object at <SOME ADDRESS>>
- + completed in <SOME NUMBER OF> seconds.
- + <BLANKLINE>
- + <object object at <SOME ADDRESS>>
- + completed in <SOME NUMBER OF> seconds.
- + <BLANKLINE>
- <BLANKLINE>
-
- If the wanted text is empty, however, we don't transform the actual output.
- This is usful when writing tests. We leave the expected output empty, run
- the test, and use the actual output as expected, after reviewing it.
-
- >>> source = '''\
- ... >>> do_something()
- ... '''
-
- >>> example = doctest.Example(source, '\n')
- >>> print checker.output_difference(example, got, 0)
- Expected:
- <BLANKLINE>
- Got:
- <object object at 0xb7f14458>
- completed in 1.235 seconds.
- <BLANKLINE>
- <object object at 0xb7f14460>
- completed in 123.233 seconds.
- <BLANKLINE>
- <object object at 0xb7f14468>
- completed in .231 seconds.
- <BLANKLINE>
- <object object at 0xb7f14470>
- completed in 1.23 seconds.
- <BLANKLINE>
- <BLANKLINE>
-
-If regular expressions aren't expressive enough, you can use arbitrary Python
-callables to transform the text. For example, suppose you want to ignore
-case during comparison:
-
- >>> checker = RENormalizing([
- ... lambda s: s.lower(),
- ... lambda s: s.replace('<blankline>', '<BLANKLINE>'),
- ... ])
-
- >>> want = '''\
- ... Usage: thundermonkey [options] [url]
- ... <BLANKLINE>
- ... Options:
- ... -h display this help message
- ... '''
-
- >>> got = '''\
- ... usage: thundermonkey [options] [URL]
- ...
- ... options:
- ... -h Display this help message
- ... '''
-
- >>> checker.check_output(want, got, 0)
- True
-
-Suppose we forgot that <BLANKLINE> must be in upper case:
-
- >>> checker = RENormalizing([
- ... lambda s: s.lower(),
- ... ])
-
- >>> checker.check_output(want, got, 0)
- False
-
-The difference would show us that:
-
- >>> source = '''\
- ... >>> print_help_message()
- ... ''' + want
- >>> example = doctest.Example(source, want)
- >>> print checker.output_difference(example, got,
- ... doctest.REPORT_NDIFF)
- Differences (ndiff with -expected +actual):
- usage: thundermonkey [options] [url]
- - <blankline>
- + <BLANKLINE>
- options:
- -h display this help message
- <BLANKLINE>
-
-
-It is possible to combine RENormalizing checkers for easy reuse:
-
- >>> address_and_time_checker = RENormalizing([
- ... (re.compile('[0-9]*[.][0-9]* seconds'), '<SOME NUMBER OF> seconds'),
- ... (re.compile('at 0x[0-9a-f]+'), 'at <SOME ADDRESS>'),
- ... ])
- >>> lowercase_checker = RENormalizing([
- ... lambda s: s.lower(),
- ... ])
- >>> combined_checker = address_and_time_checker + lowercase_checker
- >>> len(combined_checker.transformers)
- 3
-
-Combining a checker with something else does not work:
-
- >>> lowercase_checker + 5 #doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- TypeError: unsupported operand type(s) for +: ...
-
-"""
-
import doctest
-
-class RENormalizing(doctest.OutputChecker):
+class OutputChecker(doctest.OutputChecker):
"""Pattern-normalizing outout checker
"""
@@ -315,3 +80,6 @@
example.want = original
return result
+
+RENormalizing = OutputChecker
+
Added: zope.testing/trunk/src/zope/testing/renormalizing.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/renormalizing.txt (rev 0)
+++ zope.testing/trunk/src/zope/testing/renormalizing.txt 2012-01-28 21:12:57 UTC (rev 124225)
@@ -0,0 +1,236 @@
+Regular expression pattern normalizing output checker
+=====================================================
+
+The pattern-normalizing output checker extends the default output checker with
+an option to normalize expected and actual output.
+
+You specify a sequence of patterns and replacements. The replacements are
+applied to the expected and actual outputs before calling the default outputs
+checker. Let's look at an example. In this example, we have some times and
+addresses:
+
+ >>> want = '''\
+ ... <object object at 0xb7f14438>
+ ... completed in 1.234 seconds.
+ ... <BLANKLINE>
+ ... <object object at 0xb7f14440>
+ ... completed in 123.234 seconds.
+ ... <BLANKLINE>
+ ... <object object at 0xb7f14448>
+ ... completed in .234 seconds.
+ ... <BLANKLINE>
+ ... <object object at 0xb7f14450>
+ ... completed in 1.234 seconds.
+ ... <BLANKLINE>
+ ... '''
+
+ >>> got = '''\
+ ... <object object at 0xb7f14458>
+ ... completed in 1.235 seconds.
+ ...
+ ... <object object at 0xb7f14460>
+ ... completed in 123.233 seconds.
+ ...
+ ... <object object at 0xb7f14468>
+ ... completed in .231 seconds.
+ ...
+ ... <object object at 0xb7f14470>
+ ... completed in 1.23 seconds.
+ ...
+ ... '''
+
+We may wish to consider these two strings to match, even though they differ in
+actual addresses and times. The default output checker will consider them
+different:
+
+ >>> import doctest
+ >>> doctest.OutputChecker().check_output(want, got, 0)
+ False
+
+We'll use the zope.testing.renormalizing.OutputChecker to normalize both the
+wanted and gotten strings to ignore differences in times and
+addresses:
+
+ >>> import re
+ >>> from zope.testing.renormalizing import OutputChecker
+ >>> checker = OutputChecker([
+ ... (re.compile('[0-9]*[.][0-9]* seconds'), '<SOME NUMBER OF> seconds'),
+ ... (re.compile('at 0x[0-9a-f]+'), 'at <SOME ADDRESS>'),
+ ... ])
+
+ >>> checker.check_output(want, got, 0)
+ True
+
+Usual OutputChecker options work as expected:
+
+ >>> want_ellided = '''\
+ ... <object object at 0xb7f14438>
+ ... completed in 1.234 seconds.
+ ... ...
+ ... <object object at 0xb7f14450>
+ ... completed in 1.234 seconds.
+ ... <BLANKLINE>
+ ... '''
+
+ >>> checker.check_output(want_ellided, got, 0)
+ False
+
+ >>> checker.check_output(want_ellided, got, doctest.ELLIPSIS)
+ True
+
+When we get differencs, we output them with normalized text:
+
+ >>> source = '''\
+ ... >>> do_something()
+ ... <object object at 0xb7f14438>
+ ... completed in 1.234 seconds.
+ ... ...
+ ... <object object at 0xb7f14450>
+ ... completed in 1.234 seconds.
+ ... <BLANKLINE>
+ ... '''
+
+ >>> example = doctest.Example(source, want_ellided)
+
+ >>> print_(checker.output_difference(example, got, 0))
+ Expected:
+ <object object at <SOME ADDRESS>>
+ completed in <SOME NUMBER OF> seconds.
+ ...
+ <object object at <SOME ADDRESS>>
+ completed in <SOME NUMBER OF> seconds.
+ <BLANKLINE>
+ Got:
+ <object object at <SOME ADDRESS>>
+ completed in <SOME NUMBER OF> seconds.
+ <BLANKLINE>
+ <object object at <SOME ADDRESS>>
+ completed in <SOME NUMBER OF> seconds.
+ <BLANKLINE>
+ <object object at <SOME ADDRESS>>
+ completed in <SOME NUMBER OF> seconds.
+ <BLANKLINE>
+ <object object at <SOME ADDRESS>>
+ completed in <SOME NUMBER OF> seconds.
+ <BLANKLINE>
+ <BLANKLINE>
+
+ >>> print_(checker.output_difference(example, got,
+ ... doctest.REPORT_NDIFF))
+ Differences (ndiff with -expected +actual):
+ - <object object at <SOME ADDRESS>>
+ - completed in <SOME NUMBER OF> seconds.
+ - ...
+ <object object at <SOME ADDRESS>>
+ completed in <SOME NUMBER OF> seconds.
+ <BLANKLINE>
+ + <object object at <SOME ADDRESS>>
+ + completed in <SOME NUMBER OF> seconds.
+ + <BLANKLINE>
+ + <object object at <SOME ADDRESS>>
+ + completed in <SOME NUMBER OF> seconds.
+ + <BLANKLINE>
+ + <object object at <SOME ADDRESS>>
+ + completed in <SOME NUMBER OF> seconds.
+ + <BLANKLINE>
+ <BLANKLINE>
+
+ If the wanted text is empty, however, we don't transform the actual output.
+ This is usful when writing tests. We leave the expected output empty, run
+ the test, and use the actual output as expected, after reviewing it.
+
+ >>> source = '''\
+ ... >>> do_something()
+ ... '''
+
+ >>> example = doctest.Example(source, '\n')
+ >>> print_(checker.output_difference(example, got, 0))
+ Expected:
+ <BLANKLINE>
+ Got:
+ <object object at 0xb7f14458>
+ completed in 1.235 seconds.
+ <BLANKLINE>
+ <object object at 0xb7f14460>
+ completed in 123.233 seconds.
+ <BLANKLINE>
+ <object object at 0xb7f14468>
+ completed in .231 seconds.
+ <BLANKLINE>
+ <object object at 0xb7f14470>
+ completed in 1.23 seconds.
+ <BLANKLINE>
+ <BLANKLINE>
+
+If regular expressions aren't expressive enough, you can use arbitrary Python
+callables to transform the text. For example, suppose you want to ignore
+case during comparison:
+
+ >>> checker = OutputChecker([
+ ... lambda s: s.lower(),
+ ... lambda s: s.replace('<blankline>', '<BLANKLINE>'),
+ ... ])
+
+ >>> want = '''\
+ ... Usage: thundermonkey [options] [url]
+ ... <BLANKLINE>
+ ... Options:
+ ... -h display this help message
+ ... '''
+
+ >>> got = '''\
+ ... usage: thundermonkey [options] [URL]
+ ...
+ ... options:
+ ... -h Display this help message
+ ... '''
+
+ >>> checker.check_output(want, got, 0)
+ True
+
+Suppose we forgot that <BLANKLINE> must be in upper case:
+
+ >>> checker = OutputChecker([
+ ... lambda s: s.lower(),
+ ... ])
+
+ >>> checker.check_output(want, got, 0)
+ False
+
+The difference would show us that:
+
+ >>> source = '''\
+ ... >>> print_help_message()
+ ... ''' + want
+ >>> example = doctest.Example(source, want)
+ >>> print_(checker.output_difference(example, got,
+ ... doctest.REPORT_NDIFF))
+ Differences (ndiff with -expected +actual):
+ usage: thundermonkey [options] [url]
+ - <blankline>
+ + <BLANKLINE>
+ options:
+ -h display this help message
+ <BLANKLINE>
+
+
+It is possible to combine OutputChecker checkers for easy reuse:
+
+ >>> address_and_time_checker = OutputChecker([
+ ... (re.compile('[0-9]*[.][0-9]* seconds'), '<SOME NUMBER OF> seconds'),
+ ... (re.compile('at 0x[0-9a-f]+'), 'at <SOME ADDRESS>'),
+ ... ])
+ >>> lowercase_checker = OutputChecker([
+ ... lambda s: s.lower(),
+ ... ])
+ >>> combined_checker = address_and_time_checker + lowercase_checker
+ >>> len(combined_checker.transformers)
+ 3
+
+Combining a checker with something else does not work:
+
+ >>> lowercase_checker + 5 #doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ TypeError: unsupported operand type(s) for +: ...
+
Property changes on: zope.testing/trunk/src/zope/testing/renormalizing.txt
___________________________________________________________________
Added: svn:eol-style
+ native
Modified: zope.testing/trunk/src/zope/testing/setupstack.py
===================================================================
--- zope.testing/trunk/src/zope/testing/setupstack.py 2012-01-27 21:57:17 UTC (rev 124224)
+++ zope.testing/trunk/src/zope/testing/setupstack.py 2012-01-28 21:12:57 UTC (rev 124225)
@@ -20,14 +20,22 @@
key = '__' + __name__
+def globs(test):
+ try:
+ return test.globs
+ except AttributeError:
+ return test.__dict__
+
def register(test, function, *args, **kw):
- stack = test.globs.get(key)
+ tglobs = globs(test)
+ stack = tglobs.get(key)
if stack is None:
- stack = test.globs[key] = []
+ stack = tglobs[key] = []
stack.append((function, args, kw))
def tearDown(test):
- stack = test.globs.get(key)
+ tglobs = globs(test)
+ stack = tglobs.get(key)
while stack:
f, p, k = stack.pop()
f(*p, **k)
@@ -50,4 +58,9 @@
dname = os.path.join(path, dname)
os.rmdir(dname)
os.rmdir(path)
-
+
+def context_manager(test, manager):
+ result = manager.__enter__()
+ register(test, manager.__exit__, None, None, None)
+ return result
+
Modified: zope.testing/trunk/src/zope/testing/setupstack.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/setupstack.txt 2012-01-27 21:57:17 UTC (rev 124224)
+++ zope.testing/trunk/src/zope/testing/setupstack.txt 2012-01-28 21:12:57 UTC (rev 124225)
@@ -1,5 +1,5 @@
-Stack-based test doctest setUp and tearDown
-============================================
+Stack-based test setUp and tearDown
+===================================
Writing doctest setUp and tearDown functions can be a bit tedious,
especially when setUp/tearDown functions are combined.
@@ -109,3 +109,63 @@
>>> os.path.exists(os.path.join(setupstack_cwd, 'Data.fs'))
False
+
+Context-manager support
+-----------------------
+
+You can leverage context managers using the ``contextmanager`` method.
+The result of calling the content manager's __enter__ method will be
+returned. The context-manager's __exit__ method will be called as part
+of test tear down:
+
+ >>> class Manager(object):
+ ... def __enter__(self):
+ ... print_('enter')
+ ... return 42
+ ... def __exit__(self, *args):
+ ... print_('exit', args)
+
+ >>> manager = Manager()
+ >>> test = Test()
+
+ >>> zope.testing.setupstack.context_manager(test, manager)
+ enter
+ 42
+
+ >>> zope.testing.setupstack.tearDown(test)
+ exit (None, None, None)
+
+globs
+-----
+
+Doctests have ``globs`` attributes used to hold test globals.
+``setupstack`` was originally designed to work with doctests, but can
+now work with either doctests, or other test objects, as long as the
+test objects have either a ``globs`` attribute or a ``__dict__``
+attribute. The ``zope.testing.setupstack.globs`` function is used to
+get the globals for a test object:
+
+ >>> zope.testing.setupstack.globs(test) is test.globs
+ True
+
+Here, because the test object had a ``globs`` attribute, it was
+returned. Because we used the test object above, it has a setupstack:
+
+ >>> '__zope.testing.setupstack' in test.globs
+ True
+
+If we remove the ``globs`` attribute, the object's instance dictionary
+will be used:
+
+ >>> del test.globs
+ >>> zope.testing.setupstack.globs(test) is test.__dict__
+ True
+ >>> zope.testing.setupstack.context_manager(test, manager)
+ enter
+ 42
+
+ >>> '__zope.testing.setupstack' in test.__dict__
+ True
+
+The ``globs`` function is used internally, but can also be used by
+setup code to support either doctests or other test objects.
Modified: zope.testing/trunk/src/zope/testing/tests.py
===================================================================
--- zope.testing/trunk/src/zope/testing/tests.py 2012-01-27 21:57:17 UTC (rev 124224)
+++ zope.testing/trunk/src/zope/testing/tests.py 2012-01-28 21:12:57 UTC (rev 124225)
@@ -12,26 +12,28 @@
"""Tests for the testing framework.
"""
+import doctest
import sys
import re
import unittest
import warnings
from zope.testing import renormalizing
-# Yes, it is deprecated, but we want to run tests on it here.
-warnings.filterwarnings("ignore", "zope.testing.doctest is deprecated",
- DeprecationWarning, __name__, 0)
+if sys.version < '3':
+ # Yes, it is deprecated, but we want to run tests on it here.
+ warnings.filterwarnings("ignore", "zope.testing.doctest is deprecated",
+ DeprecationWarning, __name__, 0)
-from zope.testing import doctest
+ from zope.testing import doctest
+def print_(*args):
+ sys.stdout.write(' '.join(map(str, args))+'\n')
+def setUp(test):
+ test.globs['print_'] = print_
+
def test_suite():
suite = unittest.TestSuite((
- doctest.DocTestSuite('zope.testing.loggingsupport'),
- doctest.DocTestSuite('zope.testing.renormalizing'),
- doctest.DocTestSuite('zope.testing.server'),
- doctest.DocFileSuite('doctest.txt'),
- doctest.DocFileSuite('formparser.txt'),
doctest.DocFileSuite(
'module.txt',
# when this test is run in isolation, the error message shows the
@@ -41,9 +43,21 @@
checker=renormalizing.RENormalizing([
(re.compile('No module named zope.testing.unlikelymodulename'),
'No module named unlikelymodulename')])),
- doctest.DocFileSuite('setupstack.txt'),
+ doctest.DocFileSuite('loggingsupport.txt', setUp=setUp),
+ doctest.DocFileSuite('renormalizing.txt', setUp=setUp),
+ doctest.DocFileSuite('setupstack.txt', setUp=setUp),
+ doctest.DocFileSuite(
+ 'wait_until.txt', setUp=setUp,
+ checker=renormalizing.RENormalizing([
+ (re.compile('zope.testing.wait_until.TimeOutWaitingFor: '),
+ 'TimeOutWaitingFor: '),
+ ])
+ ),
))
if sys.version < '3':
+ suite.addTests(doctest.DocFileSuite('doctest.txt'))
suite.addTests(doctest.DocFileSuite('unicode.txt'))
+ suite.addTests(doctest.DocTestSuite('zope.testing.server'))
+ suite.addTests(doctest.DocFileSuite('formparser.txt'))
return suite
Added: zope.testing/trunk/src/zope/testing/wait_until.py
===================================================================
--- zope.testing/trunk/src/zope/testing/wait_until.py (rev 0)
+++ zope.testing/trunk/src/zope/testing/wait_until.py 2012-01-28 21:12:57 UTC (rev 124225)
@@ -0,0 +1,68 @@
+##############################################################################
+#
+# Copyright Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+import time
+
+class WaitUntil:
+
+ class TimeOutWaitingFor(Exception):
+ "A test condition timed out"
+
+ timeout = 9
+ wait = .01
+
+ def __init__(self,
+ timeout=None, wait=None, exception=None,
+ getnow=(lambda : time.time), getsleep=(lambda : time.sleep)):
+
+ if timeout is not None:
+ self.timeout = timeout
+
+ if wait is not None:
+ self.wait = wait
+
+ if exception is not None:
+ self.TimeOutWaitingFor = exception
+
+ self.getnow = getnow
+ self.getsleep = getsleep
+
+ def __call__(self, func=None, timeout=None, wait=None, message=None):
+ if func is None:
+ return lambda func: self(func, timeout, wait, message)
+
+ if func():
+ return
+
+ now = self.getnow()
+ sleep = self.getsleep()
+ if timeout is None:
+ timeout = self.timeout
+ if wait is None:
+ wait = self.wait
+ wait = float(wait)
+
+ deadline = now() + timeout
+ while 1:
+ sleep(wait)
+ if func():
+ return
+ if now() > deadline:
+ raise self.TimeOutWaitingFor(
+ message or
+ getattr(func, '__doc__') or
+ getattr(func, '__name__')
+ )
+
+wait_until = WaitUntil()
Property changes on: zope.testing/trunk/src/zope/testing/wait_until.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: zope.testing/trunk/src/zope/testing/wait_until.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/wait_until.txt (rev 0)
+++ zope.testing/trunk/src/zope/testing/wait_until.txt 2012-01-28 21:12:57 UTC (rev 124225)
@@ -0,0 +1,170 @@
+Wait until a condition holds (or until a time out)
+==================================================
+
+Often, in tests, you need to wait until some condition holds. This
+may be because you're testing interaction with an external system or
+testing threaded (threads, processes, greenlet's, etc.) interactions.
+
+You can add sleeps to your tests, but it's often hard to know how
+long to sleep.
+
+``zope.testing.wait_until`` provides a convenient way to wait until
+some condition holds. It will test a condition and, when true,
+return. It will sleep a short time between tests.
+
+Here's a silly example, that illustrates it's use:
+
+ >>> from zope.testing.wait_until import wait_until
+ >>> wait_until(lambda : True)
+
+Since the condition we passed is always True, it returned
+immediately. If the condition doesn't hold, then we'll get a timeout:
+
+ >>> wait_until((lambda : False), timeout=.01)
+ Traceback (most recent call last):
+ ...
+ TimeOutWaitingFor: <lambda>
+
+``wait_until`` has some keyword options:
+
+timeout
+ How long, in seconds, to wait for the condition to hold
+
+ Defaults to 9 seconds.
+
+wait
+ How long to wait between calls.
+
+ Defaults to .01 seconds.
+
+message
+ A message (or other data) to pass to the timeout exception.
+
+ This defaults to ``None``. If this is false, then the callable's
+ doc string or ``__name__`` is used.
+
+``wait_until`` can be used as a decorator:
+
+ >>> @wait_until
+ ... def ok():
+ ... return True
+
+ >>> @wait_until(timeout=.01)
+ ... def no_way():
+ ... pass
+ Traceback (most recent call last):
+ ...
+ TimeOutWaitingFor: no_way
+
+ >>> @wait_until(timeout=.01)
+ ... def no_way():
+ ... "never true"
+ Traceback (most recent call last):
+ ...
+ TimeOutWaitingFor: never true
+
+.. more tests
+
+ >>> import time
+ >>> now = time.time()
+ >>> @wait_until(timeout=.01, message='dang')
+ ... def no_way():
+ ... "never true"
+ Traceback (most recent call last):
+ ...
+ TimeOutWaitingFor: dang
+
+ >>> .01 < (time.time() - now) < .03
+ True
+
+
+Customization
+-------------
+
+``wait_until`` is an instance of ``WaitUntil``. With ``WaitUntil``,
+you can create you're own custom ``wait_until`` utilities. For
+example, if you're testing something that uses getevent, you'd want to
+use gevent's sleep function:
+
+ >>> import zope.testing.wait_until
+ >>> wait_until = zope.testing.wait_until.WaitUntil(
+ ... getsleep=lambda : gevent.sleep)
+
+WaitUntil takes a number of customization parameters:
+
+exception
+ Timeout exception class
+
+getnow
+ Function used to get a function for getting the current time.
+
+ Default: lambda : time.time
+
+getsleep
+ Function used to get a sleep function.
+
+ Default: lambda : time.sleep
+
+timeout
+ Default timeout
+
+ Default: 9
+
+wait
+ Default time to wait between attempts
+
+ Default: .01
+
+
+.. more tests
+
+ >>> def mysleep(t):
+ ... print_('mysleep', t)
+ ... time.sleep(t)
+
+ >>> def mynow():
+ ... print_('mynow')
+ ... return time.time()
+
+ >>> wait_until = zope.testing.wait_until.WaitUntil(
+ ... getnow=(lambda : mynow), getsleep=(lambda : mysleep),
+ ... exception=ValueError, timeout=.02, wait=.0001)
+
+ >>> @wait_until
+ ... def _(state=[]):
+ ... if len(state) > 1:
+ ... return True
+ ... state.append(0)
+ mynow
+ mysleep 0.0001
+ mynow
+ mysleep 0.0001
+
+ >>> @wait_until(wait=.002)
+ ... def _(state=[]):
+ ... if len(state) > 1:
+ ... return True
+ ... state.append(0)
+ mynow
+ mysleep 0.002
+ mynow
+ mysleep 0.002
+
+ >>> @wait_until(timeout=0)
+ ... def _(state=[]):
+ ... if len(state) > 1:
+ ... return True
+ ... state.append(0)
+ Traceback (most recent call last):
+ ...
+ ValueError: _
+
+ >>> wait_until = zope.testing.wait_until.WaitUntil(timeout=0)
+ >>> @wait_until(timeout=0)
+ ... def _(state=[]):
+ ... if len(state) > 1:
+ ... return True
+ ... state.append(0)
+ Traceback (most recent call last):
+ ...
+ TimeOutWaitingFor: _
Property changes on: zope.testing/trunk/src/zope/testing/wait_until.txt
___________________________________________________________________
Added: svn:eol-style
+ native
More information about the Zope3-Checkins
mailing list