[Zope3-checkins]
SVN: zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/
factored option parsing / cmdline UI into its own module
Christian Theune
ct at gocept.com
Sat May 3 11:09:33 EDT 2008
Log message for revision 86232:
factored option parsing / cmdline UI into its own module
Changed:
U zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/__init__.py
A zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/options.py
U zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/tests.py
-=-
Modified: zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/__init__.py
===================================================================
--- zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/__init__.py 2008-05-03 15:03:27 UTC (rev 86231)
+++ zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/__init__.py 2008-05-03 15:09:33 UTC (rev 86232)
@@ -19,7 +19,6 @@
import gc
import glob
import logging
-import optparse
import os
import errno
import pdb
@@ -42,6 +41,7 @@
from zope.testing.testrunner.find import find_tests, test_dirs
from zope.testing.testrunner.find import StartUpFailure, import_name
from zope.testing.testrunner.find import name_from_layer, _layer_name_cache
+from zope.testing.testrunner.options import get_options
real_pdb_set_trace = pdb.set_trace
@@ -586,11 +586,13 @@
output.stop_set_up(time.time() - t)
setup_layers[layer] = 1
+
def dependencies(bases, result):
for base in bases:
result[base] = 1
dependencies(base.__bases__, result)
+
class TestResult(unittest.TestResult):
def __init__(self, options, tests, layer_name=None):
@@ -727,6 +729,7 @@
pdb.post_mortem(exc_info[2])
raise EndRun
+
def print_doctest_location(err):
# This mimics pdb's output, which gives way cool results in emacs :)
filename = err.test.filename
@@ -734,6 +737,7 @@
filename = filename[:-1]
print "> %s(%s)_()" % (filename, err.test.lineno+err.example.lineno+1)
+
def ordered_layers(tests_by_layer_name):
layer_names = dict([(layer_from_name(layer_name), layer_name)
for layer_name in tests_by_layer_name])
@@ -741,12 +745,14 @@
layer_name = layer_names[layer]
yield layer_name, layer, tests_by_layer_name[layer_name]
+
def gather_layers(layer, result):
if layer is not object:
result.append(layer)
for b in layer.__bases__:
gather_layers(b, result)
+
def layer_from_name(layer_name):
"""Return the layer for the corresponding layer_name by discovering
and importing the necessary module if necessary.
@@ -762,6 +768,7 @@
layer_module, module_layer_name = layer_names[:-1], layer_names[-1]
return getattr(import_name('.'.join(layer_module)), module_layer_name)
+
def order_by_bases(layers):
"""Order the layers from least to most specific (bottom to top)
"""
@@ -782,11 +789,6 @@
return result
-
-
-
-
-
def configure_logging():
"""Initialize the logging module."""
import logging.config
@@ -811,6 +813,7 @@
level = int(os.environ["LOGGING"])
logging.getLogger().setLevel(level)
+
class NullHandler(logging.Handler):
"""Logging handler that drops everything on the floor.
@@ -901,6 +904,7 @@
self.delta = None
+
def type_or_class_title(t):
module = getattr(t, '__module__', '__builtin__')
if module == '__builtin__':
@@ -909,641 +913,6 @@
###############################################################################
-# Command-line UI
-
-parser = optparse.OptionParser("Usage: %prog [options] [MODULE] [TEST]")
-
-######################################################################
-# Searching and filtering
-
-searching = optparse.OptionGroup(parser, "Searching and filtering", """\
-Options in this group are used to define which tests to run.
-""")
-
-searching.add_option(
- '--package', '--dir', '-s', action="append", dest='package',
- help="""\
-Search the given package's directories for tests. This can be
-specified more than once to run tests in multiple parts of the source
-tree. For example, if refactoring interfaces, you don't want to see
-the way you have broken setups for tests in other packages. You *just*
-want to run the interface tests.
-
-Packages are supplied as dotted names. For compatibility with the old
-test runner, forward and backward slashed in package names are
-converted to dots.
-
-(In the special case of packages spread over multiple directories,
-only directories within the test search path are searched. See the
---path option.)
-
-""")
-
-searching.add_option(
- '--module', '-m', action="append", dest='module',
- help="""\
-Specify a test-module filter as a regular expression. This is a
-case-sensitive regular expression, used in search (not match) mode, to
-limit which test modules are searched for tests. The regular
-expressions are checked against dotted module names. In an extension
-of Python regexp notation, a leading "!" is stripped and causes the
-sense of the remaining regexp to be negated (so "!bc" matches any
-string that does not match "bc", and vice versa). The option can be
-specified multiple test-module filters. Test modules matching any of
-the test filters are searched. If no test-module filter is specified,
-then all test modules are used.
-""")
-
-searching.add_option(
- '--test', '-t', action="append", dest='test',
- help="""\
-Specify a test filter as a regular expression. This is a
-case-sensitive regular expression, used in search (not match) mode, to
-limit which tests are run. In an extension of Python regexp notation,
-a leading "!" is stripped and causes the sense of the remaining regexp
-to be negated (so "!bc" matches any string that does not match "bc",
-and vice versa). The option can be specified multiple test filters.
-Tests matching any of the test filters are included. If no test
-filter is specified, then all tests are run.
-""")
-
-searching.add_option(
- '--unit', '-u', action="store_true", dest='unit',
- help="""\
-Run only unit tests, ignoring any layer options.
-""")
-
-searching.add_option(
- '--non-unit', '-f', action="store_true", dest='non_unit',
- help="""\
-Run tests other than unit tests.
-""")
-
-searching.add_option(
- '--layer', action="append", dest='layer',
- help="""\
-Specify a test layer to run. The option can be given multiple times
-to specify more than one layer. If not specified, all layers are run.
-It is common for the running script to provide default values for this
-option. Layers are specified regular expressions, used in search
-mode, for dotted names of objects that define a layer. In an
-extension of Python regexp notation, a leading "!" is stripped and
-causes the sense of the remaining regexp to be negated (so "!bc"
-matches any string that does not match "bc", and vice versa). The
-layer named 'unit' is reserved for unit tests, however, take note of
-the --unit and non-unit options.
-""")
-
-searching.add_option(
- '-a', '--at-level', type='int', dest='at_level',
- help="""\
-Run the tests at the given level. Any test at a level at or below
-this is run, any test at a level above this is not run. Level 0
-runs all tests.
-""")
-
-searching.add_option(
- '--all', action="store_true", dest='all',
- help="Run tests at all levels.")
-
-searching.add_option(
- '--list-tests', action="store_true", dest='list_tests', default=False,
- help="List all tests that matched your filters. Do not run any tests.")
-
-parser.add_option_group(searching)
-
-######################################################################
-# Reporting
-
-reporting = optparse.OptionGroup(parser, "Reporting", """\
-Reporting options control basic aspects of test-runner output
-""")
-
-reporting.add_option(
- '--verbose', '-v', action="count", dest='verbose',
- help="""\
-Make output more verbose.
-Increment the verbosity level.
-""")
-
-reporting.add_option(
- '--quiet', '-q', action="store_true", dest='quiet',
- help="""\
-Make the output minimal, overriding any verbosity options.
-""")
-
-reporting.add_option(
- '--progress', '-p', action="store_true", dest='progress',
- help="""\
-Output progress status
-""")
-
-reporting.add_option(
- '--no-progress',action="store_false", dest='progress',
- help="""\
-Do not output progress status. This is the default, but can be used to
-counter a previous use of --progress or -p.
-""")
-
-# We use a noop callback because the actual processing will be done in the
-# get_options function, but we want optparse to generate appropriate help info
-# for us, so we add an option anyway.
-reporting.add_option(
- '--auto-progress', action="callback", callback=lambda *args: None,
- help="""\
-Output progress status, but only when stdout is a terminal.
-""")
-
-reporting.add_option(
- '--color', '-c', action="store_true", dest='color',
- help="""\
-Colorize the output.
-""")
-
-reporting.add_option(
- '--no-color', '-C', action="store_false", dest='color',
- help="""\
-Do not colorize the output. This is the default, but can be used to
-counter a previous use of --color or -c.
-""")
-
-# We use a noop callback because the actual processing will be done in the
-# get_options function, but we want optparse to generate appropriate help info
-# for us, so we add an option anyway.
-reporting.add_option(
- '--auto-color', action="callback", callback=lambda *args: None,
- help="""\
-Colorize the output, but only when stdout is a terminal.
-""")
-
-reporting.add_option(
- '--slow-test', type='float', dest='slow_test_threshold',
- metavar='N', default=10,
- help="""\
-With -c and -vvv, highlight tests that take longer than N seconds (default:
-%default).
-""")
-
-reporting.add_option(
- '-1', '--hide-secondary-failures',
- action="store_true", dest='report_only_first_failure',
- help="""\
-Report only the first failure in a doctest. (Examples after the
-failure are still executed, in case they do any cleanup.)
-""")
-
-reporting.add_option(
- '--show-secondary-failures',
- action="store_false", dest='report_only_first_failure',
- help="""\
-Report all failures in a doctest. This is the default, but can
-be used to counter a default use of -1 or --hide-secondary-failures.
-""")
-
-reporting.add_option(
- '--ndiff', action="store_true", dest="ndiff",
- help="""\
-When there is a doctest failure, show it as a diff using the ndiff.py utility.
-""")
-
-reporting.add_option(
- '--udiff', action="store_true", dest="udiff",
- help="""\
-When there is a doctest failure, show it as a unified diff.
-""")
-
-reporting.add_option(
- '--cdiff', action="store_true", dest="cdiff",
- help="""\
-When there is a doctest failure, show it as a context diff.
-""")
-
-parser.add_option_group(reporting)
-
-######################################################################
-# Analysis
-
-analysis = optparse.OptionGroup(parser, "Analysis", """\
-Analysis options provide tools for analysing test output.
-""")
-
-
-analysis.add_option(
- '--post-mortem', '-D', action="store_true", dest='post_mortem',
- help="Enable post-mortem debugging of test failures"
- )
-
-
-analysis.add_option(
- '--gc', '-g', action="append", dest='gc', type="int",
- help="""\
-Set the garbage collector generation threshold. This can be used
-to stress memory and gc correctness. Some crashes are only
-reproducible when the threshold is set to 1 (aggressive garbage
-collection). Do "--gc 0" to disable garbage collection altogether.
-
-The --gc option can be used up to 3 times to specify up to 3 of the 3
-Python gc_threshold settings.
-
-""")
-
-analysis.add_option(
- '--gc-option', '-G', action="append", dest='gc_option', type="choice",
- choices=['DEBUG_STATS', 'DEBUG_COLLECTABLE', 'DEBUG_UNCOLLECTABLE',
- 'DEBUG_INSTANCES', 'DEBUG_OBJECTS', 'DEBUG_SAVEALL',
- 'DEBUG_LEAK'],
- help="""\
-Set a Python gc-module debug flag. This option can be used more than
-once to set multiple flags.
-""")
-
-analysis.add_option(
- '--repeat', '-N', action="store", type="int", dest='repeat',
- help="""\
-Repeat the tests the given number of times. This option is used to
-make sure that tests leave their environment in the state they found
-it and, with the --report-refcounts option to look for memory leaks.
-""")
-
-analysis.add_option(
- '--report-refcounts', '-r', action="store_true", dest='report_refcounts',
- help="""\
-After each run of the tests, output a report summarizing changes in
-refcounts by object type. This option that requires that Python was
-built with the --with-pydebug option to configure.
-""")
-
-analysis.add_option(
- '--coverage', action="store", type='string', dest='coverage',
- help="""\
-Perform code-coverage analysis, saving trace data to the directory
-with the given name. A code coverage summary is printed to standard
-out.
-""")
-
-analysis.add_option(
- '--profile', action="store", dest='profile', type="choice",
- choices=available_profilers.keys(),
- help="""\
-Run the tests under cProfiler or hotshot and display the top 50 stats, sorted
-by cumulative time and number of calls.
-""")
-
-def do_pychecker(*args):
- if not os.environ.get("PYCHECKER"):
- os.environ["PYCHECKER"] = "-q"
- import pychecker.checker
-
-analysis.add_option(
- '--pychecker', action="callback", callback=do_pychecker,
- help="""\
-Run the tests under pychecker
-""")
-
-parser.add_option_group(analysis)
-
-######################################################################
-# Setup
-
-setup = optparse.OptionGroup(parser, "Setup", """\
-Setup options are normally supplied by the testrunner script, although
-they can be overridden by users.
-""")
-
-setup.add_option(
- '--path', action="append", dest='path',
- help="""\
-Specify a path to be added to Python's search path. This option can
-be used multiple times to specify multiple search paths. The path is
-usually specified by the test-runner script itself, rather than by
-users of the script, although it can be overridden by users. Only
-tests found in the path will be run.
-
-This option also specifies directories to be searched for tests.
-See the search_directory.
-""")
-
-setup.add_option(
- '--test-path', action="append", dest='test_path',
- help="""\
-Specify a path to be searched for tests, but not added to the Python
-search path. This option can be used multiple times to specify
-multiple search paths. The path is usually specified by the
-test-runner script itself, rather than by users of the script,
-although it can be overridden by users. Only tests found in the path
-will be run.
-""")
-
-setup.add_option(
- '--package-path', action="append", dest='package_path', nargs=2,
- help="""\
-Specify a path to be searched for tests, but not added to the Python
-search path. Also specify a package for files found in this path.
-This is used to deal with directories that are stitched into packages
-that are not otherwise searched for tests.
-
-This option takes 2 arguments. The first is a path name. The second is
-the package name.
-
-This option can be used multiple times to specify
-multiple search paths. The path is usually specified by the
-test-runner script itself, rather than by users of the script,
-although it can be overridden by users. Only tests found in the path
-will be run.
-""")
-
-setup.add_option(
- '--tests-pattern', action="store", dest='tests_pattern',
- help="""\
-The test runner looks for modules containing tests. It uses this
-pattern to identify these modules. The modules may be either packages
-or python files.
-
-If a test module is a package, it uses the value given by the
-test-file-pattern to identify python files within the package
-containing tests.
-""")
-
-setup.add_option(
- '--suite-name', action="store", dest='suite_name',
- help="""\
-Specify the name of the object in each test_module that contains the
-module's test suite.
-""")
-
-setup.add_option(
- '--test-file-pattern', action="store", dest='test_file_pattern',
- help="""\
-Specify a pattern for identifying python files within a tests package.
-See the documentation for the --tests-pattern option.
-""")
-
-setup.add_option(
- '--ignore_dir', action="append", dest='ignore_dir',
- help="""\
-Specifies the name of a directory to ignore when looking for tests.
-""")
-
-parser.add_option_group(setup)
-
-######################################################################
-# Other
-
-other = optparse.OptionGroup(parser, "Other", "Other options")
-
-other.add_option(
- '--keepbytecode', '-k', action="store_true", dest='keepbytecode',
- help="""\
-Normally, the test runner scans the test paths and the test
-directories looking for and deleting pyc or pyo files without
-corresponding py files. This is to prevent spurious test failures due
-to finding compiled modules where source modules have been deleted.
-This scan can be time consuming. Using this option disables this
-scan. If you know you haven't removed any modules since last running
-the tests, can make the test run go much faster.
-""")
-
-other.add_option(
- '--usecompiled', action="store_true", dest='usecompiled',
- help="""\
-Normally, a package must contain an __init__.py file, and only .py files
-can contain test code. When this option is specified, compiled Python
-files (.pyc and .pyo) can be used instead: a directory containing
-__init__.pyc or __init__.pyo is also considered to be a package, and if
-file XYZ.py contains tests but is absent while XYZ.pyc or XYZ.pyo exists
-then the compiled files will be used. This is necessary when running
-tests against a tree where the .py files have been removed after
-compilation to .pyc/.pyo. Use of this option implies --keepbytecode.
-""")
-
-other.add_option(
- '--exit-with-status', action="store_true", dest='exitwithstatus',
- help="""\
-Return an error exit status if the tests failed. This can be useful for
-an invoking process that wants to monitor the result of a test run.
-""")
-
-parser.add_option_group(other)
-
-######################################################################
-# Command-line processing
-
-def compile_filter(pattern):
- if pattern.startswith('!'):
- pattern = re.compile(pattern[1:]).search
- return (lambda s: not pattern(s))
- return re.compile(pattern).search
-
-def merge_options(options, defaults):
- odict = options.__dict__
- for name, value in defaults.__dict__.items():
- if (value is not None) and (odict[name] is None):
- odict[name] = value
-
-default_setup_args = [
- '--tests-pattern', '^tests$',
- '--at-level', '1',
- '--ignore', '.svn',
- '--ignore', 'CVS',
- '--ignore', '{arch}',
- '--ignore', '.arch-ids',
- '--ignore', '_darcs',
- '--test-file-pattern', '^test',
- '--suite-name', 'test_suite',
- ]
-
-
-def get_options(args=None, defaults=None):
- # Because we want to inspect stdout and decide to colorize or not, we
- # replace the --auto-color option with the appropriate --color or
- # --no-color option. That way the subprocess doesn't have to decide (which
- # it would do incorrectly anyway because stdout would be a pipe).
- def apply_auto_color(args):
- if args and '--auto-color' in args:
- if sys.stdout.isatty() and terminal_has_colors():
- colorization = '--color'
- else:
- colorization = '--no-color'
-
- args[:] = [arg.replace('--auto-color', colorization)
- for arg in args]
-
- # The comment of apply_auto_color applies here as well
- def apply_auto_progress(args):
- if args and '--auto-progress' in args:
- if sys.stdout.isatty():
- progress = '--progress'
- else:
- progress = '--no-progress'
-
- args[:] = [arg.replace('--auto-progress', progress)
- for arg in args]
-
- apply_auto_color(args)
- apply_auto_color(defaults)
- apply_auto_progress(args)
- apply_auto_progress(defaults)
-
- default_setup, _ = parser.parse_args(default_setup_args)
- assert not _
- if defaults:
- defaults, _ = parser.parse_args(defaults)
- assert not _
- merge_options(defaults, default_setup)
- else:
- defaults = default_setup
-
- if args is None:
- args = sys.argv
-
- original_testrunner_args = args
- args = args[1:]
-
- options, positional = parser.parse_args(args)
- merge_options(options, defaults)
- options.original_testrunner_args = original_testrunner_args
-
- if options.color:
- options.output = ColorfulOutputFormatter(options)
- options.output.slow_test_threshold = options.slow_test_threshold
- else:
- options.output = OutputFormatter(options)
-
- options.fail = False
-
- if positional:
- module_filter = positional.pop(0)
- if module_filter != '.':
- if options.module:
- options.module.append(module_filter)
- else:
- options.module = [module_filter]
-
- if positional:
- test_filter = positional.pop(0)
- if options.test:
- options.test.append(test_filter)
- else:
- options.test = [test_filter]
-
- if positional:
- parser.error("Too many positional arguments")
-
- options.ignore_dir = dict([(d,1) for d in options.ignore_dir])
- options.test_file_pattern = re.compile(options.test_file_pattern).search
- options.tests_pattern = re.compile(options.tests_pattern).search
- options.test = map(compile_filter, options.test or ('.'))
- options.module = map(compile_filter, options.module or ('.'))
-
- options.path = map(os.path.abspath, options.path or ())
- options.test_path = map(os.path.abspath, options.test_path or ())
- options.test_path += options.path
-
- options.test_path = ([(path, '') for path in options.test_path]
- +
- [(os.path.abspath(path), package)
- for (path, package) in options.package_path or ()
- ])
-
- if options.package:
- pkgmap = dict(options.test_path)
- options.package = [normalize_package(p, pkgmap)
- for p in options.package]
-
- options.prefix = [(path + os.path.sep, package)
- for (path, package) in options.test_path]
- if options.all:
- options.at_level = sys.maxint
-
- if options.unit and options.non_unit:
- # The test runner interprets this as "run only those tests that are
- # both unit and non-unit at the same time". The user, however, wants
- # to run both unit and non-unit tests. Disable the filtering so that
- # the user will get what she wants:
- options.unit = options.non_unit = False
-
- if options.unit:
- options.layer = ['unit']
- if options.layer:
- options.layer = map(compile_filter, options.layer)
-
- options.layer = options.layer and dict([(l, 1) for l in options.layer])
-
- if options.usecompiled:
- options.keepbytecode = options.usecompiled
-
- if options.quiet:
- options.verbose = 0
-
- if options.report_refcounts and options.repeat < 2:
- print """\
- You must use the --repeat (-N) option to specify a repeat
- count greater than 1 when using the --report_refcounts (-r)
- option.
- """
- options.fail = True
- return options
-
-
- if options.report_refcounts and not hasattr(sys, "gettotalrefcount"):
- print """\
- The Python you are running was not configured
- with --with-pydebug. This is required to use
- the --report-refcounts option.
- """
- options.fail = True
- return options
-
- return options
-
-def normalize_package(package, package_map={}):
- r"""Normalize package name passed to the --package option.
-
- >>> normalize_package('zope.testing')
- 'zope.testing'
-
- Converts path names into package names for compatibility with the old
- test runner.
-
- >>> normalize_package('zope/testing')
- 'zope.testing'
- >>> normalize_package('zope/testing/')
- 'zope.testing'
- >>> normalize_package('zope\\testing')
- 'zope.testing'
-
- Can use a map of absolute pathnames to package names
-
- >>> a = os.path.abspath
- >>> normalize_package('src/zope/testing/',
- ... {a('src'): ''})
- 'zope.testing'
- >>> normalize_package('src/zope_testing/',
- ... {a('src/zope_testing'): 'zope.testing'})
- 'zope.testing'
- >>> normalize_package('src/zope_something/tests',
- ... {a('src/zope_something'): 'zope.something',
- ... a('src'): ''})
- 'zope.something.tests'
-
- """
- package = package.replace('\\', '/')
- if package.endswith('/'):
- package = package[:-1]
- bits = package.split('/')
- for n in range(len(bits), 0, -1):
- pkg = package_map.get(os.path.abspath('/'.join(bits[:n])))
- if pkg is not None:
- bits = bits[n:]
- if pkg:
- bits = [pkg] + bits
- return '.'.join(bits)
- return package.replace('/', '.')
-
-# Command-line UI
-###############################################################################
-
-###############################################################################
# Install 2.4 TestSuite __iter__ into earlier versions
if sys.version_info < (2, 4):
Added: zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/options.py
===================================================================
--- zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/options.py (rev 0)
+++ zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/options.py 2008-05-03 15:09:33 UTC (rev 86232)
@@ -0,0 +1,656 @@
+##############################################################################
+#
+# Copyright (c) 2004-2008 Zope Corporation 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.
+#
+##############################################################################
+"""Command-line option parsing
+
+$Id: __init__.py 86231 2008-05-03 15:03:27Z ctheune $
+"""
+
+import optparse
+import re
+import os
+import sys
+
+from zope.testing.testrunner.profiling import available_profilers
+from zope.testing.testrunner.formatter import OutputFormatter, ColorfulOutputFormatter
+from zope.testing.testrunner.formatter import terminal_has_colors
+
+
+parser = optparse.OptionParser("Usage: %prog [options] [MODULE] [TEST]")
+
+######################################################################
+# Searching and filtering
+
+searching = optparse.OptionGroup(parser, "Searching and filtering", """\
+Options in this group are used to define which tests to run.
+""")
+
+searching.add_option(
+ '--package', '--dir', '-s', action="append", dest='package',
+ help="""\
+Search the given package's directories for tests. This can be
+specified more than once to run tests in multiple parts of the source
+tree. For example, if refactoring interfaces, you don't want to see
+the way you have broken setups for tests in other packages. You *just*
+want to run the interface tests.
+
+Packages are supplied as dotted names. For compatibility with the old
+test runner, forward and backward slashed in package names are
+converted to dots.
+
+(In the special case of packages spread over multiple directories,
+only directories within the test search path are searched. See the
+--path option.)
+
+""")
+
+searching.add_option(
+ '--module', '-m', action="append", dest='module',
+ help="""\
+Specify a test-module filter as a regular expression. This is a
+case-sensitive regular expression, used in search (not match) mode, to
+limit which test modules are searched for tests. The regular
+expressions are checked against dotted module names. In an extension
+of Python regexp notation, a leading "!" is stripped and causes the
+sense of the remaining regexp to be negated (so "!bc" matches any
+string that does not match "bc", and vice versa). The option can be
+specified multiple test-module filters. Test modules matching any of
+the test filters are searched. If no test-module filter is specified,
+then all test modules are used.
+""")
+
+searching.add_option(
+ '--test', '-t', action="append", dest='test',
+ help="""\
+Specify a test filter as a regular expression. This is a
+case-sensitive regular expression, used in search (not match) mode, to
+limit which tests are run. In an extension of Python regexp notation,
+a leading "!" is stripped and causes the sense of the remaining regexp
+to be negated (so "!bc" matches any string that does not match "bc",
+and vice versa). The option can be specified multiple test filters.
+Tests matching any of the test filters are included. If no test
+filter is specified, then all tests are run.
+""")
+
+searching.add_option(
+ '--unit', '-u', action="store_true", dest='unit',
+ help="""\
+Run only unit tests, ignoring any layer options.
+""")
+
+searching.add_option(
+ '--non-unit', '-f', action="store_true", dest='non_unit',
+ help="""\
+Run tests other than unit tests.
+""")
+
+searching.add_option(
+ '--layer', action="append", dest='layer',
+ help="""\
+Specify a test layer to run. The option can be given multiple times
+to specify more than one layer. If not specified, all layers are run.
+It is common for the running script to provide default values for this
+option. Layers are specified regular expressions, used in search
+mode, for dotted names of objects that define a layer. In an
+extension of Python regexp notation, a leading "!" is stripped and
+causes the sense of the remaining regexp to be negated (so "!bc"
+matches any string that does not match "bc", and vice versa). The
+layer named 'unit' is reserved for unit tests, however, take note of
+the --unit and non-unit options.
+""")
+
+searching.add_option(
+ '-a', '--at-level', type='int', dest='at_level',
+ help="""\
+Run the tests at the given level. Any test at a level at or below
+this is run, any test at a level above this is not run. Level 0
+runs all tests.
+""")
+
+searching.add_option(
+ '--all', action="store_true", dest='all',
+ help="Run tests at all levels.")
+
+searching.add_option(
+ '--list-tests', action="store_true", dest='list_tests', default=False,
+ help="List all tests that matched your filters. Do not run any tests.")
+
+parser.add_option_group(searching)
+
+######################################################################
+# Reporting
+
+reporting = optparse.OptionGroup(parser, "Reporting", """\
+Reporting options control basic aspects of test-runner output
+""")
+
+reporting.add_option(
+ '--verbose', '-v', action="count", dest='verbose',
+ help="""\
+Make output more verbose.
+Increment the verbosity level.
+""")
+
+reporting.add_option(
+ '--quiet', '-q', action="store_true", dest='quiet',
+ help="""\
+Make the output minimal, overriding any verbosity options.
+""")
+
+reporting.add_option(
+ '--progress', '-p', action="store_true", dest='progress',
+ help="""\
+Output progress status
+""")
+
+reporting.add_option(
+ '--no-progress',action="store_false", dest='progress',
+ help="""\
+Do not output progress status. This is the default, but can be used to
+counter a previous use of --progress or -p.
+""")
+
+# We use a noop callback because the actual processing will be done in the
+# get_options function, but we want optparse to generate appropriate help info
+# for us, so we add an option anyway.
+reporting.add_option(
+ '--auto-progress', action="callback", callback=lambda *args: None,
+ help="""\
+Output progress status, but only when stdout is a terminal.
+""")
+
+reporting.add_option(
+ '--color', '-c', action="store_true", dest='color',
+ help="""\
+Colorize the output.
+""")
+
+reporting.add_option(
+ '--no-color', '-C', action="store_false", dest='color',
+ help="""\
+Do not colorize the output. This is the default, but can be used to
+counter a previous use of --color or -c.
+""")
+
+# We use a noop callback because the actual processing will be done in the
+# get_options function, but we want optparse to generate appropriate help info
+# for us, so we add an option anyway.
+reporting.add_option(
+ '--auto-color', action="callback", callback=lambda *args: None,
+ help="""\
+Colorize the output, but only when stdout is a terminal.
+""")
+
+reporting.add_option(
+ '--slow-test', type='float', dest='slow_test_threshold',
+ metavar='N', default=10,
+ help="""\
+With -c and -vvv, highlight tests that take longer than N seconds (default:
+%default).
+""")
+
+reporting.add_option(
+ '-1', '--hide-secondary-failures',
+ action="store_true", dest='report_only_first_failure',
+ help="""\
+Report only the first failure in a doctest. (Examples after the
+failure are still executed, in case they do any cleanup.)
+""")
+
+reporting.add_option(
+ '--show-secondary-failures',
+ action="store_false", dest='report_only_first_failure',
+ help="""\
+Report all failures in a doctest. This is the default, but can
+be used to counter a default use of -1 or --hide-secondary-failures.
+""")
+
+reporting.add_option(
+ '--ndiff', action="store_true", dest="ndiff",
+ help="""\
+When there is a doctest failure, show it as a diff using the ndiff.py utility.
+""")
+
+reporting.add_option(
+ '--udiff', action="store_true", dest="udiff",
+ help="""\
+When there is a doctest failure, show it as a unified diff.
+""")
+
+reporting.add_option(
+ '--cdiff', action="store_true", dest="cdiff",
+ help="""\
+When there is a doctest failure, show it as a context diff.
+""")
+
+parser.add_option_group(reporting)
+
+######################################################################
+# Analysis
+
+analysis = optparse.OptionGroup(parser, "Analysis", """\
+Analysis options provide tools for analysing test output.
+""")
+
+
+analysis.add_option(
+ '--post-mortem', '-D', action="store_true", dest='post_mortem',
+ help="Enable post-mortem debugging of test failures"
+ )
+
+
+analysis.add_option(
+ '--gc', '-g', action="append", dest='gc', type="int",
+ help="""\
+Set the garbage collector generation threshold. This can be used
+to stress memory and gc correctness. Some crashes are only
+reproducible when the threshold is set to 1 (aggressive garbage
+collection). Do "--gc 0" to disable garbage collection altogether.
+
+The --gc option can be used up to 3 times to specify up to 3 of the 3
+Python gc_threshold settings.
+
+""")
+
+analysis.add_option(
+ '--gc-option', '-G', action="append", dest='gc_option', type="choice",
+ choices=['DEBUG_STATS', 'DEBUG_COLLECTABLE', 'DEBUG_UNCOLLECTABLE',
+ 'DEBUG_INSTANCES', 'DEBUG_OBJECTS', 'DEBUG_SAVEALL',
+ 'DEBUG_LEAK'],
+ help="""\
+Set a Python gc-module debug flag. This option can be used more than
+once to set multiple flags.
+""")
+
+analysis.add_option(
+ '--repeat', '-N', action="store", type="int", dest='repeat',
+ help="""\
+Repeat the tests the given number of times. This option is used to
+make sure that tests leave their environment in the state they found
+it and, with the --report-refcounts option to look for memory leaks.
+""")
+
+analysis.add_option(
+ '--report-refcounts', '-r', action="store_true", dest='report_refcounts',
+ help="""\
+After each run of the tests, output a report summarizing changes in
+refcounts by object type. This option that requires that Python was
+built with the --with-pydebug option to configure.
+""")
+
+analysis.add_option(
+ '--coverage', action="store", type='string', dest='coverage',
+ help="""\
+Perform code-coverage analysis, saving trace data to the directory
+with the given name. A code coverage summary is printed to standard
+out.
+""")
+
+analysis.add_option(
+ '--profile', action="store", dest='profile', type="choice",
+ choices=available_profilers.keys(),
+ help="""\
+Run the tests under cProfiler or hotshot and display the top 50 stats, sorted
+by cumulative time and number of calls.
+""")
+
+def do_pychecker(*args):
+ if not os.environ.get("PYCHECKER"):
+ os.environ["PYCHECKER"] = "-q"
+ import pychecker.checker
+
+analysis.add_option(
+ '--pychecker', action="callback", callback=do_pychecker,
+ help="""\
+Run the tests under pychecker
+""")
+
+parser.add_option_group(analysis)
+
+######################################################################
+# Setup
+
+setup = optparse.OptionGroup(parser, "Setup", """\
+Setup options are normally supplied by the testrunner script, although
+they can be overridden by users.
+""")
+
+setup.add_option(
+ '--path', action="append", dest='path',
+ help="""\
+Specify a path to be added to Python's search path. This option can
+be used multiple times to specify multiple search paths. The path is
+usually specified by the test-runner script itself, rather than by
+users of the script, although it can be overridden by users. Only
+tests found in the path will be run.
+
+This option also specifies directories to be searched for tests.
+See the search_directory.
+""")
+
+setup.add_option(
+ '--test-path', action="append", dest='test_path',
+ help="""\
+Specify a path to be searched for tests, but not added to the Python
+search path. This option can be used multiple times to specify
+multiple search paths. The path is usually specified by the
+test-runner script itself, rather than by users of the script,
+although it can be overridden by users. Only tests found in the path
+will be run.
+""")
+
+setup.add_option(
+ '--package-path', action="append", dest='package_path', nargs=2,
+ help="""\
+Specify a path to be searched for tests, but not added to the Python
+search path. Also specify a package for files found in this path.
+This is used to deal with directories that are stitched into packages
+that are not otherwise searched for tests.
+
+This option takes 2 arguments. The first is a path name. The second is
+the package name.
+
+This option can be used multiple times to specify
+multiple search paths. The path is usually specified by the
+test-runner script itself, rather than by users of the script,
+although it can be overridden by users. Only tests found in the path
+will be run.
+""")
+
+setup.add_option(
+ '--tests-pattern', action="store", dest='tests_pattern',
+ help="""\
+The test runner looks for modules containing tests. It uses this
+pattern to identify these modules. The modules may be either packages
+or python files.
+
+If a test module is a package, it uses the value given by the
+test-file-pattern to identify python files within the package
+containing tests.
+""")
+
+setup.add_option(
+ '--suite-name', action="store", dest='suite_name',
+ help="""\
+Specify the name of the object in each test_module that contains the
+module's test suite.
+""")
+
+setup.add_option(
+ '--test-file-pattern', action="store", dest='test_file_pattern',
+ help="""\
+Specify a pattern for identifying python files within a tests package.
+See the documentation for the --tests-pattern option.
+""")
+
+setup.add_option(
+ '--ignore_dir', action="append", dest='ignore_dir',
+ help="""\
+Specifies the name of a directory to ignore when looking for tests.
+""")
+
+parser.add_option_group(setup)
+
+######################################################################
+# Other
+
+other = optparse.OptionGroup(parser, "Other", "Other options")
+
+other.add_option(
+ '--keepbytecode', '-k', action="store_true", dest='keepbytecode',
+ help="""\
+Normally, the test runner scans the test paths and the test
+directories looking for and deleting pyc or pyo files without
+corresponding py files. This is to prevent spurious test failures due
+to finding compiled modules where source modules have been deleted.
+This scan can be time consuming. Using this option disables this
+scan. If you know you haven't removed any modules since last running
+the tests, can make the test run go much faster.
+""")
+
+other.add_option(
+ '--usecompiled', action="store_true", dest='usecompiled',
+ help="""\
+Normally, a package must contain an __init__.py file, and only .py files
+can contain test code. When this option is specified, compiled Python
+files (.pyc and .pyo) can be used instead: a directory containing
+__init__.pyc or __init__.pyo is also considered to be a package, and if
+file XYZ.py contains tests but is absent while XYZ.pyc or XYZ.pyo exists
+then the compiled files will be used. This is necessary when running
+tests against a tree where the .py files have been removed after
+compilation to .pyc/.pyo. Use of this option implies --keepbytecode.
+""")
+
+other.add_option(
+ '--exit-with-status', action="store_true", dest='exitwithstatus',
+ help="""\
+Return an error exit status if the tests failed. This can be useful for
+an invoking process that wants to monitor the result of a test run.
+""")
+
+parser.add_option_group(other)
+
+######################################################################
+# Command-line processing
+
+def compile_filter(pattern):
+ if pattern.startswith('!'):
+ pattern = re.compile(pattern[1:]).search
+ return (lambda s: not pattern(s))
+ return re.compile(pattern).search
+
+def merge_options(options, defaults):
+ odict = options.__dict__
+ for name, value in defaults.__dict__.items():
+ if (value is not None) and (odict[name] is None):
+ odict[name] = value
+
+default_setup_args = [
+ '--tests-pattern', '^tests$',
+ '--at-level', '1',
+ '--ignore', '.svn',
+ '--ignore', 'CVS',
+ '--ignore', '{arch}',
+ '--ignore', '.arch-ids',
+ '--ignore', '_darcs',
+ '--test-file-pattern', '^test',
+ '--suite-name', 'test_suite',
+ ]
+
+
+def get_options(args=None, defaults=None):
+ # Because we want to inspect stdout and decide to colorize or not, we
+ # replace the --auto-color option with the appropriate --color or
+ # --no-color option. That way the subprocess doesn't have to decide (which
+ # it would do incorrectly anyway because stdout would be a pipe).
+ def apply_auto_color(args):
+ if args and '--auto-color' in args:
+ if sys.stdout.isatty() and terminal_has_colors():
+ colorization = '--color'
+ else:
+ colorization = '--no-color'
+
+ args[:] = [arg.replace('--auto-color', colorization)
+ for arg in args]
+
+ # The comment of apply_auto_color applies here as well
+ def apply_auto_progress(args):
+ if args and '--auto-progress' in args:
+ if sys.stdout.isatty():
+ progress = '--progress'
+ else:
+ progress = '--no-progress'
+
+ args[:] = [arg.replace('--auto-progress', progress)
+ for arg in args]
+
+ apply_auto_color(args)
+ apply_auto_color(defaults)
+ apply_auto_progress(args)
+ apply_auto_progress(defaults)
+
+ default_setup, _ = parser.parse_args(default_setup_args)
+ assert not _
+ if defaults:
+ defaults, _ = parser.parse_args(defaults)
+ assert not _
+ merge_options(defaults, default_setup)
+ else:
+ defaults = default_setup
+
+ if args is None:
+ args = sys.argv
+
+ original_testrunner_args = args
+ args = args[1:]
+
+ options, positional = parser.parse_args(args)
+ merge_options(options, defaults)
+ options.original_testrunner_args = original_testrunner_args
+
+ if options.color:
+ options.output = ColorfulOutputFormatter(options)
+ options.output.slow_test_threshold = options.slow_test_threshold
+ else:
+ options.output = OutputFormatter(options)
+
+ options.fail = False
+
+ if positional:
+ module_filter = positional.pop(0)
+ if module_filter != '.':
+ if options.module:
+ options.module.append(module_filter)
+ else:
+ options.module = [module_filter]
+
+ if positional:
+ test_filter = positional.pop(0)
+ if options.test:
+ options.test.append(test_filter)
+ else:
+ options.test = [test_filter]
+
+ if positional:
+ parser.error("Too many positional arguments")
+
+ options.ignore_dir = dict([(d,1) for d in options.ignore_dir])
+ options.test_file_pattern = re.compile(options.test_file_pattern).search
+ options.tests_pattern = re.compile(options.tests_pattern).search
+ options.test = map(compile_filter, options.test or ('.'))
+ options.module = map(compile_filter, options.module or ('.'))
+
+ options.path = map(os.path.abspath, options.path or ())
+ options.test_path = map(os.path.abspath, options.test_path or ())
+ options.test_path += options.path
+
+ options.test_path = ([(path, '') for path in options.test_path]
+ +
+ [(os.path.abspath(path), package)
+ for (path, package) in options.package_path or ()
+ ])
+
+ if options.package:
+ pkgmap = dict(options.test_path)
+ options.package = [normalize_package(p, pkgmap)
+ for p in options.package]
+
+ options.prefix = [(path + os.path.sep, package)
+ for (path, package) in options.test_path]
+ if options.all:
+ options.at_level = sys.maxint
+
+ if options.unit and options.non_unit:
+ # The test runner interprets this as "run only those tests that are
+ # both unit and non-unit at the same time". The user, however, wants
+ # to run both unit and non-unit tests. Disable the filtering so that
+ # the user will get what she wants:
+ options.unit = options.non_unit = False
+
+ if options.unit:
+ options.layer = ['unit']
+ if options.layer:
+ options.layer = map(compile_filter, options.layer)
+
+ options.layer = options.layer and dict([(l, 1) for l in options.layer])
+
+ if options.usecompiled:
+ options.keepbytecode = options.usecompiled
+
+ if options.quiet:
+ options.verbose = 0
+
+ if options.report_refcounts and options.repeat < 2:
+ print """\
+ You must use the --repeat (-N) option to specify a repeat
+ count greater than 1 when using the --report_refcounts (-r)
+ option.
+ """
+ options.fail = True
+ return options
+
+
+ if options.report_refcounts and not hasattr(sys, "gettotalrefcount"):
+ print """\
+ The Python you are running was not configured
+ with --with-pydebug. This is required to use
+ the --report-refcounts option.
+ """
+ options.fail = True
+ return options
+
+ return options
+
+def normalize_package(package, package_map={}):
+ r"""Normalize package name passed to the --package option.
+
+ >>> normalize_package('zope.testing')
+ 'zope.testing'
+
+ Converts path names into package names for compatibility with the old
+ test runner.
+
+ >>> normalize_package('zope/testing')
+ 'zope.testing'
+ >>> normalize_package('zope/testing/')
+ 'zope.testing'
+ >>> normalize_package('zope\\testing')
+ 'zope.testing'
+
+ Can use a map of absolute pathnames to package names
+
+ >>> a = os.path.abspath
+ >>> normalize_package('src/zope/testing/',
+ ... {a('src'): ''})
+ 'zope.testing'
+ >>> normalize_package('src/zope_testing/',
+ ... {a('src/zope_testing'): 'zope.testing'})
+ 'zope.testing'
+ >>> normalize_package('src/zope_something/tests',
+ ... {a('src/zope_something'): 'zope.something',
+ ... a('src'): ''})
+ 'zope.something.tests'
+
+ """
+ package = package.replace('\\', '/')
+ if package.endswith('/'):
+ package = package[:-1]
+ bits = package.split('/')
+ for n in range(len(bits), 0, -1):
+ pkg = package_map.get(os.path.abspath('/'.join(bits[:n])))
+ if pkg is not None:
+ bits = bits[n:]
+ if pkg:
+ bits = [pkg] + bits
+ return '.'.join(bits)
+ return package.replace('/', '.')
Property changes on: zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/options.py
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/tests.py
===================================================================
--- zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/tests.py 2008-05-03 15:03:27 UTC (rev 86231)
+++ zope.testing/branches/ctheune-cleanup/src/zope/testing/testrunner/tests.py 2008-05-03 15:09:33 UTC (rev 86232)
@@ -108,7 +108,8 @@
optionflags=doctest.ELLIPSIS+doctest.NORMALIZE_WHITESPACE,
checker=checker),
doctest.DocTestSuite('zope.testing.testrunner'),
- doctest.DocTestSuite('zope.testing.testrunner.coverage')
+ doctest.DocTestSuite('zope.testing.testrunner.coverage'),
+ doctest.DocTestSuite('zope.testing.testrunner.options')
]
if sys.platform == 'win32':
More information about the Zope3-Checkins
mailing list