1
##############################################################################
3
# Copyright (c) 2004-2008 Zope Foundation and Contributors.
6
# This software is subject to the provisions of the Zope Public License,
7
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
# FOR A PARTICULAR PURPOSE.
13
##############################################################################
14
"""Command-line option parsing
16
$Id: __init__.py 86231 2008-05-03 15:03:27Z ctheune $
26
from zope.testing.testrunner.profiling import available_profilers
27
from zope.testing.testrunner.formatter import (
29
ColorfulOutputFormatter,
30
SubunitOutputFormatter,
32
from zope.testing.testrunner.formatter import terminal_has_colors
35
parser = optparse.OptionParser("Usage: %prog [options] [MODULE] [TEST]")
37
######################################################################
38
# Searching and filtering
40
searching = optparse.OptionGroup(parser, "Searching and filtering", """\
41
Options in this group are used to define which tests to run.
45
'--package', '--dir', '-s', action="append", dest='package',
47
Search the given package's directories for tests. This can be
48
specified more than once to run tests in multiple parts of the source
49
tree. For example, if refactoring interfaces, you don't want to see
50
the way you have broken setups for tests in other packages. You *just*
51
want to run the interface tests.
53
Packages are supplied as dotted names. For compatibility with the old
54
test runner, forward and backward slashed in package names are
57
(In the special case of packages spread over multiple directories,
58
only directories within the test search path are searched. See the
64
'--module', '-m', action="append", dest='module',
66
Specify a test-module filter as a regular expression. This is a
67
case-sensitive regular expression, used in search (not match) mode, to
68
limit which test modules are searched for tests. The regular
69
expressions are checked against dotted module names. In an extension
70
of Python regexp notation, a leading "!" is stripped and causes the
71
sense of the remaining regexp to be negated (so "!bc" matches any
72
string that does not match "bc", and vice versa). The option can be
73
specified multiple test-module filters. Test modules matching any of
74
the test filters are searched. If no test-module filter is specified,
75
then all test modules are used.
79
'--test', '-t', action="append", dest='test',
81
Specify a test filter as a regular expression. This is a
82
case-sensitive regular expression, used in search (not match) mode, to
83
limit which tests are run. In an extension of Python regexp notation,
84
a leading "!" is stripped and causes the sense of the remaining regexp
85
to be negated (so "!bc" matches any string that does not match "bc",
86
and vice versa). The option can be specified multiple test filters.
87
Tests matching any of the test filters are included. If no test
88
filter is specified, then all tests are run.
92
'--unit', '-u', action="store_true", dest='unit',
94
Run only unit tests, ignoring any layer options.
98
'--non-unit', '-f', action="store_true", dest='non_unit',
100
Run tests other than unit tests.
103
searching.add_option(
104
'--layer', action="append", dest='layer',
106
Specify a test layer to run. The option can be given multiple times
107
to specify more than one layer. If not specified, all layers are run.
108
It is common for the running script to provide default values for this
109
option. Layers are specified regular expressions, used in search
110
mode, for dotted names of objects that define a layer. In an
111
extension of Python regexp notation, a leading "!" is stripped and
112
causes the sense of the remaining regexp to be negated (so "!bc"
113
matches any string that does not match "bc", and vice versa). The
114
layer named 'zope.testing.testrunner.layer.UnitTests' is reserved for
115
unit tests, however, take note of the --unit and non-unit options.
118
searching.add_option(
119
'-a', '--at-level', type='int', dest='at_level',
121
Run the tests at the given level. Any test at a level at or below
122
this is run, any test at a level above this is not run. Level 0
126
searching.add_option(
127
'--all', action="store_true", dest='all',
128
help="Run tests at all levels.")
130
searching.add_option(
131
'--list-tests', action="store_true", dest='list_tests',
132
help="List all tests that matched your filters. Do not run any tests.")
134
parser.add_option_group(searching)
136
######################################################################
139
reporting = optparse.OptionGroup(parser, "Reporting", """\
140
Reporting options control basic aspects of test-runner output
143
reporting.add_option(
144
'--verbose', '-v', action="count", dest='verbose',
146
Make output more verbose.
147
Increment the verbosity level.
150
reporting.add_option(
151
'--quiet', '-q', action="store_true", dest='quiet',
153
Make the output minimal, overriding any verbosity options.
156
reporting.add_option(
157
'--progress', '-p', action="store_true", dest='progress',
159
Output progress status
162
reporting.add_option(
163
'--no-progress',action="store_false", dest='progress',
165
Do not output progress status. This is the default, but can be used to
166
counter a previous use of --progress or -p.
169
# We use a noop callback because the actual processing will be done in the
170
# get_options function, but we want optparse to generate appropriate help info
171
# for us, so we add an option anyway.
172
reporting.add_option(
173
'--auto-progress', action="callback", callback=lambda *args: None,
175
Output progress status, but only when stdout is a terminal.
178
reporting.add_option(
179
'--color', '-c', action="store_true", dest='color',
184
reporting.add_option(
185
'--no-color', '-C', action="store_false", dest='color',
187
Do not colorize the output. This is the default, but can be used to
188
counter a previous use of --color or -c.
191
# We use a noop callback because the actual processing will be done in the
192
# get_options function, but we want optparse to generate appropriate help info
193
# for us, so we add an option anyway.
194
reporting.add_option(
195
'--auto-color', action="callback", callback=lambda *args: None,
197
Colorize the output, but only when stdout is a terminal.
200
reporting.add_option(
201
'--subunit', action="store_true", dest='subunit',
203
Use subunit output. Will not be colorized.
206
reporting.add_option(
207
'--slow-test', type='float', dest='slow_test_threshold', metavar='N',
209
With -c and -vvv, highlight tests that take longer than N seconds (default:
213
reporting.add_option(
214
'-1', '--hide-secondary-failures',
215
action="store_true", dest='report_only_first_failure',
217
Report only the first failure in a doctest. (Examples after the
218
failure are still executed, in case they do any cleanup.)
221
reporting.add_option(
222
'--show-secondary-failures',
223
action="store_false", dest='report_only_first_failure',
225
Report all failures in a doctest. This is the default, but can
226
be used to counter a default use of -1 or --hide-secondary-failures.
229
reporting.add_option(
230
'--ndiff', action="store_true", dest="ndiff",
232
When there is a doctest failure, show it as a diff using the ndiff.py utility.
235
reporting.add_option(
236
'--udiff', action="store_true", dest="udiff",
238
When there is a doctest failure, show it as a unified diff.
241
reporting.add_option(
242
'--cdiff', action="store_true", dest="cdiff",
244
When there is a doctest failure, show it as a context diff.
247
parser.add_option_group(reporting)
249
######################################################################
252
analysis = optparse.OptionGroup(parser, "Analysis", """\
253
Analysis options provide tools for analysing test output.
258
'--stop-on-error', '--stop', '-x', action="store_true",
259
dest='stop_on_error',
260
help="Stop running tests after first test failure or error."
264
'--post-mortem', '--pdb', '-D', action="store_true", dest='post_mortem',
265
help="Enable post-mortem debugging of test failures"
270
'--gc', '-g', action="append", dest='gc', type="int",
272
Set the garbage collector generation threshold. This can be used
273
to stress memory and gc correctness. Some crashes are only
274
reproducible when the threshold is set to 1 (aggressive garbage
275
collection). Do "--gc 0" to disable garbage collection altogether.
277
The --gc option can be used up to 3 times to specify up to 3 of the 3
278
Python gc_threshold settings.
283
'--gc-option', '-G', action="append", dest='gc_option', type="choice",
284
choices=['DEBUG_STATS', 'DEBUG_COLLECTABLE', 'DEBUG_UNCOLLECTABLE',
285
'DEBUG_INSTANCES', 'DEBUG_OBJECTS', 'DEBUG_SAVEALL',
288
Set a Python gc-module debug flag. This option can be used more than
289
once to set multiple flags.
293
'--repeat', '-N', action="store", type="int", dest='repeat',
295
Repeat the tests the given number of times. This option is used to
296
make sure that tests leave their environment in the state they found
297
it and, with the --report-refcounts option to look for memory leaks.
301
'--report-refcounts', '-r', action="store_true", dest='report_refcounts',
303
After each run of the tests, output a report summarizing changes in
304
refcounts by object type. This option that requires that Python was
305
built with the --with-pydebug option to configure.
309
'--coverage', action="store", type='string', dest='coverage',
311
Perform code-coverage analysis, saving trace data to the directory
312
with the given name. A code coverage summary is printed to standard
317
'--profile', action="store", dest='profile', type="choice",
318
choices=available_profilers.keys(),
320
Run the tests under cProfiler or hotshot and display the top 50 stats, sorted
321
by cumulative time and number of calls.
324
def do_pychecker(*args):
325
if not os.environ.get("PYCHECKER"):
326
os.environ["PYCHECKER"] = "-q"
327
import pychecker.checker
330
'--pychecker', action="callback", callback=do_pychecker,
332
Run the tests under pychecker
335
parser.add_option_group(analysis)
337
######################################################################
340
setup = optparse.OptionGroup(parser, "Setup", """\
341
Setup options are normally supplied by the testrunner script, although
342
they can be overridden by users.
346
'--path', action="append", dest='path',
348
Specify a path to be added to Python's search path. This option can
349
be used multiple times to specify multiple search paths. The path is
350
usually specified by the test-runner script itself, rather than by
351
users of the script, although it can be overridden by users. Only
352
tests found in the path will be run.
354
This option also specifies directories to be searched for tests.
355
See the search_directory.
359
'--test-path', action="append", dest='test_path',
361
Specify a path to be searched for tests, but not added to the Python
362
search path. This option can be used multiple times to specify
363
multiple search paths. The path is usually specified by the
364
test-runner script itself, rather than by users of the script,
365
although it can be overridden by users. Only tests found in the path
370
'--package-path', action="append", dest='package_path', nargs=2,
372
Specify a path to be searched for tests, but not added to the Python
373
search path. Also specify a package for files found in this path.
374
This is used to deal with directories that are stitched into packages
375
that are not otherwise searched for tests.
377
This option takes 2 arguments. The first is a path name. The second is
380
This option can be used multiple times to specify
381
multiple search paths. The path is usually specified by the
382
test-runner script itself, rather than by users of the script,
383
although it can be overridden by users. Only tests found in the path
388
'--tests-pattern', action="store", dest='tests_pattern',
390
The test runner looks for modules containing tests. It uses this
391
pattern to identify these modules. The modules may be either packages
394
If a test module is a package, it uses the value given by the
395
test-file-pattern to identify python files within the package
400
'--suite-name', action="store", dest='suite_name',
402
Specify the name of the object in each test_module that contains the
407
'--test-file-pattern', action="store", dest='test_file_pattern',
409
Specify a pattern for identifying python files within a tests package.
410
See the documentation for the --tests-pattern option.
414
'--ignore_dir', action="append", dest='ignore_dir',
416
Specifies the name of a directory to ignore when looking for tests.
420
'--shuffle', action="store_true", dest='shuffle',
422
Shuffles the order in which tests are ran.
426
'--shuffle-seed', action="store", dest='shuffle_seed', type="int",
428
Value used to initialize the tests shuffler. Specify a value to create
429
repeatable random ordered tests.
432
parser.add_option_group(setup)
434
######################################################################
437
other = optparse.OptionGroup(parser, "Other", "Other options")
440
'--version', action="store_true", dest='showversion',
441
help="Print the version of the testrunner, and exit.")
444
'-j', action="store", type="int", dest='processes',
446
Use up to given number of parallel processes to execute tests. May decrease
447
test run time substantially. Defaults to %default.
451
'--keepbytecode', '-k', action="store_true", dest='keepbytecode',
453
Normally, the test runner scans the test paths and the test
454
directories looking for and deleting pyc or pyo files without
455
corresponding py files. This is to prevent spurious test failures due
456
to finding compiled modules where source modules have been deleted.
457
This scan can be time consuming. Using this option disables this
458
scan. If you know you haven't removed any modules since last running
459
the tests, can make the test run go much faster.
463
'--usecompiled', action="store_true", dest='usecompiled',
465
Normally, a package must contain an __init__.py file, and only .py files
466
can contain test code. When this option is specified, compiled Python
467
files (.pyc and .pyo) can be used instead: a directory containing
468
__init__.pyc or __init__.pyo is also considered to be a package, and if
469
file XYZ.py contains tests but is absent while XYZ.pyc or XYZ.pyo exists
470
then the compiled files will be used. This is necessary when running
471
tests against a tree where the .py files have been removed after
472
compilation to .pyc/.pyo. Use of this option implies --keepbytecode.
476
'--exit-with-status', action="store_true", dest='exitwithstatus',
477
help="""DEPRECATED: The test runner will always exit with a status.\
481
parser.add_option_group(other)
483
######################################################################
487
ignore_dir=['.svn', 'CVS', '{arch}', '.arch-ids', '_darcs'],
488
tests_pattern='^tests$',
490
test_file_pattern='^test',
491
suite_name='test_suite',
493
slow_test_threshold=10,
498
######################################################################
499
# Command-line processing
501
def compile_filter(pattern):
502
if pattern.startswith('!'):
503
pattern = re.compile(pattern[1:]).search
504
return (lambda s: not pattern(s))
505
return re.compile(pattern).search
507
def merge_options(options, defaults):
508
odict = options.__dict__
509
for name, value in defaults.__dict__.items():
510
if (value is not None) and (odict[name] is None):
513
def get_options(args=None, defaults=None):
514
# Because we want to inspect stdout and decide to colorize or not, we
515
# replace the --auto-color option with the appropriate --color or
516
# --no-color option. That way the subprocess doesn't have to decide (which
517
# it would do incorrectly anyway because stdout would be a pipe).
518
def apply_auto_color(args):
519
if args and '--auto-color' in args:
520
if sys.stdout.isatty() and terminal_has_colors():
521
colorization = '--color'
523
colorization = '--no-color'
525
args[:] = [arg.replace('--auto-color', colorization)
528
# The comment of apply_auto_color applies here as well
529
def apply_auto_progress(args):
530
if args and '--auto-progress' in args:
531
if sys.stdout.isatty():
532
progress = '--progress'
534
progress = '--no-progress'
536
args[:] = [arg.replace('--auto-progress', progress)
539
apply_auto_color(args)
540
apply_auto_color(defaults)
541
apply_auto_progress(args)
542
apply_auto_progress(defaults)
545
defaults, _ = parser.parse_args(defaults)
553
options, positional = parser.parse_args(args[1:], defaults)
554
options.original_testrunner_args = args
556
if options.showversion:
557
dist = pkg_resources.require('zope.testing')[0]
558
print 'zope.app.testrunner version %s' % dist.version
567
Subunit is not installed. Please install Subunit
568
to generate subunit output.
573
options.output = SubunitOutputFormatter(options)
575
options.output = ColorfulOutputFormatter(options)
576
options.output.slow_test_threshold = options.slow_test_threshold
578
options.output = OutputFormatter(options)
583
module_filter = positional.pop(0)
584
if module_filter != '.':
586
options.module.append(module_filter)
588
options.module = [module_filter]
591
test_filter = positional.pop(0)
593
options.test.append(test_filter)
595
options.test = [test_filter]
598
parser.error("Too many positional arguments")
600
options.ignore_dir = dict([(d,1) for d in options.ignore_dir])
601
options.test_file_pattern = re.compile(options.test_file_pattern).search
602
options.tests_pattern = re.compile(options.tests_pattern).search
603
options.test = map(compile_filter, options.test or ('.'))
604
options.module = map(compile_filter, options.module or ('.'))
606
options.path = map(os.path.abspath, options.path or ())
607
options.test_path = map(os.path.abspath, options.test_path or ())
608
options.test_path += options.path
610
options.test_path = ([(path, '') for path in options.test_path]
612
[(os.path.abspath(path), package)
613
for (path, package) in options.package_path or ()
617
pkgmap = dict(options.test_path)
618
options.package = [normalize_package(p, pkgmap)
619
for p in options.package]
621
options.prefix = [(path + os.path.sep, package)
622
for (path, package) in options.test_path]
624
options.at_level = sys.maxint
626
if options.unit and options.non_unit:
627
# The test runner interprets this as "run only those tests that are
628
# both unit and non-unit at the same time". The user, however, wants
629
# to run both unit and non-unit tests. Disable the filtering so that
630
# the user will get what she wants:
631
options.unit = options.non_unit = False
635
options.layer = ['zope.testing.testrunner.layer.UnitTests']
637
options.layer = map(compile_filter, options.layer)
639
options.layer = options.layer and dict([(l, 1) for l in options.layer])
641
if options.usecompiled:
642
options.keepbytecode = options.usecompiled
647
if options.report_refcounts and options.repeat < 2:
649
You must use the --repeat (-N) option to specify a repeat
650
count greater than 1 when using the --report_refcounts (-r)
657
if options.report_refcounts and not hasattr(sys, "gettotalrefcount"):
659
The Python you are running was not configured
660
with --with-pydebug. This is required to use
661
the --report-refcounts option.
668
def normalize_package(package, package_map={}):
669
r"""Normalize package name passed to the --package option.
671
>>> normalize_package('zope.testing')
674
Converts path names into package names for compatibility with the old
677
>>> normalize_package('zope/testing')
679
>>> normalize_package('zope/testing/')
681
>>> normalize_package('zope\\testing')
684
Can use a map of absolute pathnames to package names
686
>>> a = os.path.abspath
687
>>> normalize_package('src/zope/testing/',
690
>>> normalize_package('src/zope_testing/',
691
... {a('src/zope_testing'): 'zope.testing'})
693
>>> normalize_package('src/zope_something/tests',
694
... {a('src/zope_something'): 'zope.something',
696
'zope.something.tests'
699
package = package.replace('\\', '/')
700
if package.endswith('/'):
701
package = package[:-1]
702
bits = package.split('/')
703
for n in range(len(bits), 0, -1):
704
pkg = package_map.get(os.path.abspath('/'.join(bits[:n])))
709
return '.'.join(bits)
710
return package.replace('/', '.')