Commit 3b6d025d authored by Barry Warsaw's avatar Barry Warsaw

Patch #868499, adds -T option for code coverage. The implementation is a

fairly simpleminded adaptation of Zope3's test.py -T flag.

I also changed some booleans to use True/False where appropriate.
parent 5c5eb863
...@@ -19,6 +19,7 @@ Command line options: ...@@ -19,6 +19,7 @@ Command line options:
-u: use -- specify which special resource intensive tests to run -u: use -- specify which special resource intensive tests to run
-h: help -- print this text and exit -h: help -- print this text and exit
-t: threshold -- call gc.set_threshold(N) -t: threshold -- call gc.set_threshold(N)
-T: coverage -- turn on code coverage using the trace module
If non-option arguments are present, they are names for tests to run, If non-option arguments are present, they are names for tests to run,
unless -x is given, in which case they are names for tests not to run. unless -x is given, in which case they are names for tests not to run.
...@@ -26,6 +27,8 @@ If no test names are given, all tests are run. ...@@ -26,6 +27,8 @@ If no test names are given, all tests are run.
-v is incompatible with -g and does not compare test output files. -v is incompatible with -g and does not compare test output files.
-T turns on code coverage tracing with the trace module.
-s means to run only a single test and exit. This is useful when -s means to run only a single test and exit. This is useful when
doing memory analysis on the Python interpreter (which tend to consume doing memory analysis on the Python interpreter (which tend to consume
too many resources to run the full regression test non-stop). The too many resources to run the full regression test non-stop). The
...@@ -68,13 +71,13 @@ example, to run all the tests except for the bsddb tests, give the ...@@ -68,13 +71,13 @@ example, to run all the tests except for the bsddb tests, give the
option '-uall,-bsddb'. option '-uall,-bsddb'.
""" """
import sys
import os import os
import sys
import getopt import getopt
import traceback
import random import random
import cStringIO
import warnings import warnings
import cStringIO
import traceback
# I see no other way to suppress these warnings; # I see no other way to suppress these warnings;
# putting them in test_grammar.py has no effect: # putting them in test_grammar.py has no effect:
...@@ -113,9 +116,9 @@ def usage(code, msg=''): ...@@ -113,9 +116,9 @@ def usage(code, msg=''):
sys.exit(code) sys.exit(code)
def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0, def main(tests=None, testdir=None, verbose=0, quiet=False, generate=False,
exclude=0, single=0, randomize=0, fromfile=None, findleaks=0, exclude=False, single=False, randomize=False, fromfile=None,
use_resources=None): findleaks=False, use_resources=None, trace=False):
"""Execute a test suite. """Execute a test suite.
This also parses command-line options and modifies its behavior This also parses command-line options and modifies its behavior
...@@ -132,19 +135,19 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0, ...@@ -132,19 +135,19 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0,
command-line will be used. If that's empty, too, then all *.py command-line will be used. If that's empty, too, then all *.py
files beginning with test_ will be used. files beginning with test_ will be used.
The other default arguments (verbose, quiet, generate, exclude, The other default arguments (verbose, quiet, generate, exclude, single,
single, randomize, findleaks, and use_resources) allow programmers randomize, findleaks, use_resources, and trace) allow programmers calling
calling main() directly to set the values that would normally be main() directly to set the values that would normally be set by flags on
set by flags on the command line. the command line.
""" """
test_support.record_original_stdout(sys.stdout) test_support.record_original_stdout(sys.stdout)
try: try:
opts, args = getopt.getopt(sys.argv[1:], 'hvgqxsrf:lu:t:', opts, args = getopt.getopt(sys.argv[1:], 'hvgqxsrf:lu:t:T',
['help', 'verbose', 'quiet', 'generate', ['help', 'verbose', 'quiet', 'generate',
'exclude', 'single', 'random', 'fromfile', 'exclude', 'single', 'random', 'fromfile',
'findleaks', 'use=', 'threshold=']) 'findleaks', 'use=', 'threshold=', 'trace',
])
except getopt.error, msg: except getopt.error, msg:
usage(2, msg) usage(2, msg)
...@@ -157,23 +160,25 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0, ...@@ -157,23 +160,25 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0,
elif o in ('-v', '--verbose'): elif o in ('-v', '--verbose'):
verbose += 1 verbose += 1
elif o in ('-q', '--quiet'): elif o in ('-q', '--quiet'):
quiet = 1; quiet = True;
verbose = 0 verbose = 0
elif o in ('-g', '--generate'): elif o in ('-g', '--generate'):
generate = 1 generate = True
elif o in ('-x', '--exclude'): elif o in ('-x', '--exclude'):
exclude = 1 exclude = True
elif o in ('-s', '--single'): elif o in ('-s', '--single'):
single = 1 single = True
elif o in ('-r', '--randomize'): elif o in ('-r', '--randomize'):
randomize = 1 randomize = True
elif o in ('-f', '--fromfile'): elif o in ('-f', '--fromfile'):
fromfile = a fromfile = a
elif o in ('-l', '--findleaks'): elif o in ('-l', '--findleaks'):
findleaks = 1 findleaks = True
elif o in ('-t', '--threshold'): elif o in ('-t', '--threshold'):
import gc import gc
gc.set_threshold(int(a)) gc.set_threshold(int(a))
elif o in ('-T', '--coverage'):
trace = True
elif o in ('-u', '--use'): elif o in ('-u', '--use'):
u = [x.lower() for x in a.split(',')] u = [x.lower() for x in a.split(',')]
for r in u: for r in u:
...@@ -206,7 +211,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0, ...@@ -206,7 +211,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0,
import gc import gc
except ImportError: except ImportError:
print 'No GC available, disabling findleaks.' print 'No GC available, disabling findleaks.'
findleaks = 0 findleaks = False
else: else:
# Uncomment the line below to report garbage that is not # Uncomment the line below to report garbage that is not
# freeable by reference counting alone. By default only # freeable by reference counting alone. By default only
...@@ -253,6 +258,11 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0, ...@@ -253,6 +258,11 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0,
tests = tests[:1] tests = tests[:1]
if randomize: if randomize:
random.shuffle(tests) random.shuffle(tests)
if trace:
import trace
tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix],
trace=False, count=True)
coverdir = os.path.join(os.getcwd(), 'coverage')
test_support.verbose = verbose # Tell tests to be moderately quiet test_support.verbose = verbose # Tell tests to be moderately quiet
test_support.use_resources = use_resources test_support.use_resources = use_resources
save_modules = sys.modules.keys() save_modules = sys.modules.keys()
...@@ -260,6 +270,12 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0, ...@@ -260,6 +270,12 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0,
if not quiet: if not quiet:
print test print test
sys.stdout.flush() sys.stdout.flush()
if trace:
# If we're tracing code coverage, then we don't exit with status
# if on a false return value from main.
tracer.runctx('runtest(test, generate, verbose, quiet, testdir)',
globals=globals(), locals=vars())
else:
ok = runtest(test, generate, verbose, quiet, testdir) ok = runtest(test, generate, verbose, quiet, testdir)
if ok > 0: if ok > 0:
good.append(test) good.append(test)
...@@ -330,6 +346,10 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0, ...@@ -330,6 +346,10 @@ def main(tests=None, testdir=None, verbose=0, quiet=0, generate=0,
else: else:
os.unlink(filename) os.unlink(filename)
if trace:
r = tracer.results()
r.write_results(show_missing=True, summary=True, coverdir=coverdir)
sys.exit(len(bad) > 0) sys.exit(len(bad) > 0)
...@@ -362,7 +382,7 @@ def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS): ...@@ -362,7 +382,7 @@ def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS):
tests.sort() tests.sort()
return stdtests + tests return stdtests + tests
def runtest(test, generate, verbose, quiet, testdir = None): def runtest(test, generate, verbose, quiet, testdir=None):
"""Run a single test. """Run a single test.
test -- the name of the test test -- the name of the test
generate -- if true, generate output, instead of running the test generate -- if true, generate output, instead of running the test
...@@ -372,7 +392,8 @@ def runtest(test, generate, verbose, quiet, testdir = None): ...@@ -372,7 +392,8 @@ def runtest(test, generate, verbose, quiet, testdir = None):
testdir -- test directory testdir -- test directory
""" """
test_support.unload(test) test_support.unload(test)
if not testdir: testdir = findtestdir() if not testdir:
testdir = findtestdir()
outputdir = os.path.join(testdir, "output") outputdir = os.path.join(testdir, "output")
outputfile = os.path.join(outputdir, test) outputfile = os.path.join(outputdir, test)
if verbose: if verbose:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment