| #! /usr/bin/env python |
| |
| """Regression test. |
| |
| This will find all modules whose name is "test_*" in the test |
| directory, and run them. Various command line options provide |
| additional facilities. |
| |
| Command line options: |
| |
| -v: verbose -- run tests in verbose mode with output to stdout |
| -w: verbose2 -- re-run failed tests in verbose mode |
| -d: debug -- print traceback for failed tests |
| -q: quiet -- don't print anything except if a test fails |
| -x: exclude -- arguments are tests to *exclude* |
| -s: single -- run only a single test (see below) |
| -S: slow -- print the slowest 10 tests |
| -r: random -- randomize test execution order |
| -f: fromfile -- read names of tests to run from a file (see below) |
| -l: findleaks -- if GC is available detect tests that leak memory |
| -u: use -- specify which special resource intensive tests to run |
| -h: help -- print this text and exit |
| -t: threshold -- call gc.set_threshold(N) |
| -T: coverage -- turn on code coverage using the trace module |
| -D: coverdir -- Directory where coverage files are put |
| -N: nocoverdir -- Put coverage files alongside modules |
| -L: runleaks -- run the leaks(1) command just before exit |
| -R: huntrleaks -- search for reference leaks (needs debug build, v. slow) |
| -M: memlimit -- run very large memory-consuming tests |
| -n: nowindows -- suppress error message boxes on Windows |
| |
| 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. |
| If no test names are given, all tests are run. |
| |
| -v is incompatible with -g and does not compare test output files. |
| |
| -r randomizes test execution order. You can use --randseed=int to provide a |
| int seed value for the randomizer; this is useful for reproducing troublesome |
| test orders. |
| |
| -T turns on code coverage tracing with the trace module. |
| |
| -D specifies the directory where coverage files are put. |
| |
| -N Put coverage files alongside modules. |
| |
| -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 |
| too many resources to run the full regression test non-stop). The |
| file /tmp/pynexttest is read to find the next test to run. If this |
| file is missing, the first test_*.py file in testdir or on the command |
| line is used. (actually tempfile.gettempdir() is used instead of |
| /tmp). |
| |
| -S is used to continue running tests after an aborted run. It will |
| maintain the order a standard run (ie, this assumes -r is not used). |
| This is useful after the tests have prematurely stopped for some external |
| reason and you want to start running from where you left off rather |
| than starting from the beginning. |
| |
| -f reads the names of tests from the file given as f's argument, one |
| or more test names per line. Whitespace is ignored. Blank lines and |
| lines beginning with '#' are ignored. This is especially useful for |
| whittling down failures involving interactions among tests. |
| |
| -L causes the leaks(1) command to be run just before exit if it exists. |
| leaks(1) is available on Mac OS X and presumably on some other |
| FreeBSD-derived systems. |
| |
| -R runs each test several times and examines sys.gettotalrefcount() to |
| see if the test appears to be leaking references. The argument should |
| be of the form stab:run:fname where 'stab' is the number of times the |
| test is run to let gettotalrefcount settle down, 'run' is the number |
| of times further it is run and 'fname' is the name of the file the |
| reports are written to. These parameters all have defaults (5, 4 and |
| "reflog.txt" respectively), and the minimal invocation is '-R :'. |
| |
| -M runs tests that require an exorbitant amount of memory. These tests |
| typically try to ascertain containers keep working when containing more than |
| 2 billion objects, which only works on 64-bit systems. There are also some |
| tests that try to exhaust the address space of the process, which only makes |
| sense on 32-bit systems with at least 2Gb of memory. The passed-in memlimit, |
| which is a string in the form of '2.5Gb', determines howmuch memory the |
| tests will limit themselves to (but they may go slightly over.) The number |
| shouldn't be more memory than the machine has (including swap memory). You |
| should also keep in mind that swap memory is generally much, much slower |
| than RAM, and setting memlimit to all available RAM or higher will heavily |
| tax the machine. On the other hand, it is no use running these tests with a |
| limit of less than 2.5Gb, and many require more than 20Gb. Tests that expect |
| to use more than memlimit memory will be skipped. The big-memory tests |
| generally run very, very long. |
| |
| -u is used to specify which special resource intensive tests to run, |
| such as those requiring large file support or network connectivity. |
| The argument is a comma-separated list of words indicating the |
| resources to test. Currently only the following are defined: |
| |
| all - Enable all special resources. |
| |
| audio - Tests that use the audio device. (There are known |
| cases of broken audio drivers that can crash Python or |
| even the Linux kernel.) |
| |
| curses - Tests that use curses and will modify the terminal's |
| state and output modes. |
| |
| largefile - It is okay to run some test that may create huge |
| files. These tests can take a long time and may |
| consume >2GB of disk space temporarily. |
| |
| network - It is okay to run tests that use external network |
| resource, e.g. testing SSL support for sockets. |
| |
| decimal - Test the decimal module against a large suite that |
| verifies compliance with standards. |
| |
| compiler - Allow test_tokenize to verify round-trip lexing on |
| every file in the test library. |
| |
| subprocess Run all tests for the subprocess module. |
| |
| urlfetch - It is okay to download files required on testing. |
| |
| gui - Run tests that require a running GUI. |
| |
| To enable all resources except one, use '-uall,-<resource>'. For |
| example, to run all the tests except for the gui tests, give the |
| option '-uall,-gui'. |
| """ |
| |
| import getopt |
| import os |
| import random |
| import re |
| import io |
| import sys |
| import time |
| import traceback |
| import warnings |
| import unittest |
| from inspect import isabstract |
| |
| # I see no other way to suppress these warnings; |
| # putting them in test_grammar.py has no effect: |
| warnings.filterwarnings("ignore", "hex/oct constants", FutureWarning, |
| ".*test.test_grammar$") |
| if sys.maxsize > 0x7fffffff: |
| # Also suppress them in <string>, because for 64-bit platforms, |
| # that's where test_grammar.py hides them. |
| warnings.filterwarnings("ignore", "hex/oct constants", FutureWarning, |
| "<string>") |
| |
| # Ignore ImportWarnings that only occur in the source tree, |
| # (because of modules with the same name as source-directories in Modules/) |
| for mod in ("ctypes", "gzip", "zipfile", "tarfile", "encodings.zlib_codec", |
| "test.test_zipimport", "test.test_zlib", "test.test_zipfile", |
| "test.test_codecs", "test.string_tests"): |
| warnings.filterwarnings(module=".*%s$" % (mod,), |
| action="ignore", category=ImportWarning) |
| |
| # MacOSX (a.k.a. Darwin) has a default stack size that is too small |
| # for deeply recursive regular expressions. We see this as crashes in |
| # the Python test suite when running test_re.py and test_sre.py. The |
| # fix is to set the stack limit to 2048. |
| # This approach may also be useful for other Unixy platforms that |
| # suffer from small default stack limits. |
| if sys.platform == 'darwin': |
| try: |
| import resource |
| except ImportError: |
| pass |
| else: |
| soft, hard = resource.getrlimit(resource.RLIMIT_STACK) |
| newsoft = min(hard, max(soft, 1024*2048)) |
| resource.setrlimit(resource.RLIMIT_STACK, (newsoft, hard)) |
| |
| from test import support |
| |
| RESOURCE_NAMES = ('audio', 'curses', 'largefile', 'network', |
| 'decimal', 'compiler', 'subprocess', 'urlfetch', 'gui') |
| |
| |
| def usage(msg): |
| print(msg, file=sys.stderr) |
| print("Use --help for usage", file=sys.stderr) |
| sys.exit(2) |
| |
| |
| def main(tests=None, testdir=None, verbose=0, quiet=False, generate=False, |
| exclude=False, single=False, randomize=False, fromfile=None, |
| findleaks=False, use_resources=None, trace=False, coverdir='coverage', |
| runleaks=False, huntrleaks=False, verbose2=False, print_slow=False, |
| random_seed=None): |
| """Execute a test suite. |
| |
| This also parses command-line options and modifies its behavior |
| accordingly. |
| |
| tests -- a list of strings containing test names (optional) |
| testdir -- the directory in which to look for tests (optional) |
| |
| Users other than the Python test suite will certainly want to |
| specify testdir; if it's omitted, the directory containing the |
| Python test suite is searched for. |
| |
| If the tests argument is omitted, the tests listed on the |
| command-line will be used. If that's empty, too, then all *.py |
| files beginning with test_ will be used. |
| |
| The other default arguments (verbose, quiet, generate, exclude, |
| single, randomize, findleaks, use_resources, trace, coverdir, |
| print_slow, and random_seed) allow programmers calling main() |
| directly to set the values that would normally be set by flags |
| on the command line. |
| """ |
| |
| replace_stdout() |
| |
| support.record_original_stdout(sys.stdout) |
| try: |
| opts, args = getopt.getopt(sys.argv[1:], 'hvgqxsSrf:lu:t:TD:NLR:wM:n', |
| ['help', 'verbose', 'quiet', 'exclude', |
| 'single', 'slow', 'random', 'fromfile', |
| 'findleaks', 'use=', 'threshold=', 'trace', |
| 'coverdir=', 'nocoverdir', 'runleaks', |
| 'huntrleaks=', 'verbose2', 'memlimit=', |
| 'debug', 'start=', 'nowindows', |
| 'randseed=', |
| ]) |
| except getopt.error as msg: |
| usage(msg) |
| |
| # Defaults |
| if random_seed is None: |
| random_seed = random.randrange(10000000) |
| if use_resources is None: |
| use_resources = [] |
| debug = False |
| start = None |
| for o, a in opts: |
| if o in ('-h', '--help'): |
| print(__doc__) |
| return |
| elif o in ('-v', '--verbose'): |
| verbose += 1 |
| elif o in ('-w', '--verbose2'): |
| verbose2 = True |
| elif o in ('-d', '--debug'): |
| debug = True |
| elif o in ('-q', '--quiet'): |
| quiet = True; |
| verbose = 0 |
| elif o in ('-x', '--exclude'): |
| exclude = True |
| elif o in ('-S', '--start'): |
| start = a |
| elif o in ('-s', '--single'): |
| single = True |
| elif o in ('-S', '--slow'): |
| print_slow = True |
| elif o in ('-r', '--randomize'): |
| randomize = True |
| elif o == '--randseed': |
| random_seed = int(a) |
| elif o in ('-f', '--fromfile'): |
| fromfile = a |
| elif o in ('-l', '--findleaks'): |
| findleaks = True |
| elif o in ('-L', '--runleaks'): |
| runleaks = True |
| elif o in ('-t', '--threshold'): |
| import gc |
| gc.set_threshold(int(a)) |
| elif o in ('-T', '--coverage'): |
| trace = True |
| elif o in ('-D', '--coverdir'): |
| coverdir = os.path.join(os.getcwd(), a) |
| elif o in ('-N', '--nocoverdir'): |
| coverdir = None |
| elif o in ('-R', '--huntrleaks'): |
| huntrleaks = a.split(':') |
| if len(huntrleaks) not in (2, 3): |
| print(a, huntrleaks) |
| usage('-R takes 2 or 3 colon-separated arguments') |
| if not huntrleaks[0]: |
| huntrleaks[0] = 5 |
| else: |
| huntrleaks[0] = int(huntrleaks[0]) |
| if not huntrleaks[1]: |
| huntrleaks[1] = 4 |
| else: |
| huntrleaks[1] = int(huntrleaks[1]) |
| if len(huntrleaks) == 2 or not huntrleaks[2]: |
| huntrleaks[2:] = ["reflog.txt"] |
| # Avoid false positives due to the character cache in |
| # stringobject.c filling slowly with random data |
| warm_char_cache() |
| elif o in ('-M', '--memlimit'): |
| support.set_memlimit(a) |
| elif o in ('-u', '--use'): |
| u = [x.lower() for x in a.split(',')] |
| for r in u: |
| if r == 'all': |
| use_resources[:] = RESOURCE_NAMES |
| continue |
| remove = False |
| if r[0] == '-': |
| remove = True |
| r = r[1:] |
| if r not in RESOURCE_NAMES: |
| usage('Invalid -u/--use option: ' + a) |
| if remove: |
| if r in use_resources: |
| use_resources.remove(r) |
| elif r not in use_resources: |
| use_resources.append(r) |
| elif o in ('-n', '--nowindows'): |
| import msvcrt |
| msvcrt.SetErrorMode(msvcrt.SEM_FAILCRITICALERRORS| |
| msvcrt.SEM_NOALIGNMENTFAULTEXCEPT| |
| msvcrt.SEM_NOGPFAULTERRORBOX| |
| msvcrt.SEM_NOOPENFILEERRORBOX) |
| try: |
| msvcrt.CrtSetReportMode |
| except AttributeError: |
| # release build |
| pass |
| else: |
| for m in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, msvcrt.CRT_ASSERT]: |
| msvcrt.CrtSetReportMode(m, msvcrt.CRTDBG_MODE_FILE) |
| msvcrt.CrtSetReportFile(m, msvcrt.CRTDBG_FILE_STDERR) |
| else: |
| print(("No handler for option {}. Please report this as a bug " |
| "at http://bugs.python.org.").format(o), file=sys.stderr) |
| sys.exit(1) |
| if generate and verbose: |
| usage("-g and -v don't go together!") |
| if single and fromfile: |
| usage("-s and -f don't go together!") |
| |
| good = [] |
| bad = [] |
| skipped = [] |
| resource_denieds = [] |
| |
| if findleaks: |
| try: |
| import gc |
| except ImportError: |
| print('No GC available, disabling findleaks.') |
| findleaks = False |
| else: |
| # Uncomment the line below to report garbage that is not |
| # freeable by reference counting alone. By default only |
| # garbage that is not collectable by the GC is reported. |
| #gc.set_debug(gc.DEBUG_SAVEALL) |
| found_garbage = [] |
| |
| if single: |
| from tempfile import gettempdir |
| filename = os.path.join(gettempdir(), 'pynexttest') |
| try: |
| fp = open(filename, 'r') |
| next = fp.read().strip() |
| tests = [next] |
| fp.close() |
| except IOError: |
| pass |
| |
| if fromfile: |
| tests = [] |
| fp = open(fromfile) |
| for line in fp: |
| guts = line.split() # assuming no test has whitespace in its name |
| if guts and not guts[0].startswith('#'): |
| tests.extend(guts) |
| fp.close() |
| |
| # Strip .py extensions. |
| if args: |
| args = list(map(removepy, args)) |
| if tests: |
| tests = list(map(removepy, tests)) |
| |
| stdtests = STDTESTS[:] |
| nottests = NOTTESTS.copy() |
| if exclude: |
| for arg in args: |
| if arg in stdtests: |
| stdtests.remove(arg) |
| nottests.add(arg) |
| args = [] |
| tests = tests or args or findtests(testdir, stdtests, nottests) |
| if single: |
| tests = tests[:1] |
| # Remove all the tests that precede start if it's set. |
| if start: |
| try: |
| del tests[:tests.index(start)] |
| except ValueError: |
| print("Couldn't find starting test (%s), using all tests" % start) |
| if randomize: |
| random.seed(random_seed) |
| print("Using random seed", random_seed) |
| random.shuffle(tests) |
| if trace: |
| import trace, tempfile |
| tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix, |
| tempfile.gettempdir()], |
| trace=False, count=True) |
| test_times = [] |
| support.verbose = verbose # Tell tests to be moderately quiet |
| support.use_resources = use_resources |
| save_modules = sys.modules.keys() |
| for test in tests: |
| if not quiet: |
| print(test) |
| 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,' |
| ' test_times, testdir)', |
| globals=globals(), locals=vars()) |
| else: |
| try: |
| ok = runtest(test, generate, verbose, quiet, test_times, |
| testdir, huntrleaks) |
| except KeyboardInterrupt: |
| # print a newline separate from the ^C |
| print() |
| break |
| except: |
| raise |
| if ok > 0: |
| good.append(test) |
| elif ok == 0: |
| bad.append(test) |
| else: |
| skipped.append(test) |
| if ok == -2: |
| resource_denieds.append(test) |
| if findleaks: |
| gc.collect() |
| if gc.garbage: |
| print("Warning: test created", len(gc.garbage), end=' ') |
| print("uncollectable object(s).") |
| # move the uncollectable objects somewhere so we don't see |
| # them again |
| found_garbage.extend(gc.garbage) |
| del gc.garbage[:] |
| # Unload the newly imported modules (best effort finalization) |
| for module in sys.modules.keys(): |
| if module not in save_modules and module.startswith("test."): |
| support.unload(module) |
| |
| if good and not quiet: |
| if not bad and not skipped and len(good) > 1: |
| print("All", end=' ') |
| print(count(len(good), "test"), "OK.") |
| if verbose: |
| print("CAUTION: stdout isn't compared in verbose mode:") |
| print("a test that passes in verbose mode may fail without it.") |
| if print_slow: |
| test_times.sort(reverse=True) |
| print("10 slowest tests:") |
| for time, test in test_times[:10]: |
| print("%s: %.1fs" % (test, time)) |
| if bad: |
| print(count(len(bad), "test"), "failed:") |
| printlist(bad) |
| if skipped and not quiet: |
| print(count(len(skipped), "test"), "skipped:") |
| printlist(skipped) |
| |
| e = _ExpectedSkips() |
| plat = sys.platform |
| if e.isvalid(): |
| surprise = set(skipped) - e.getexpected() - set(resource_denieds) |
| if surprise: |
| print(count(len(surprise), "skip"), \ |
| "unexpected on", plat + ":") |
| printlist(surprise) |
| else: |
| print("Those skips are all expected on", plat + ".") |
| else: |
| print("Ask someone to teach regrtest.py about which tests are") |
| print("expected to get skipped on", plat + ".") |
| |
| if verbose2 and bad: |
| print("Re-running failed tests in verbose mode") |
| for test in bad: |
| print("Re-running test %r in verbose mode" % test) |
| sys.stdout.flush() |
| try: |
| support.verbose = True |
| ok = runtest(test, generate, True, quiet, test_times, testdir, |
| huntrleaks, debug) |
| except KeyboardInterrupt: |
| # print a newline separate from the ^C |
| print() |
| break |
| except: |
| raise |
| |
| if single: |
| alltests = findtests(testdir, stdtests, nottests) |
| for i in range(len(alltests)): |
| if tests[0] == alltests[i]: |
| if i == len(alltests) - 1: |
| os.unlink(filename) |
| else: |
| fp = open(filename, 'w') |
| fp.write(alltests[i+1] + '\n') |
| fp.close() |
| break |
| else: |
| os.unlink(filename) |
| |
| if trace: |
| r = tracer.results() |
| r.write_results(show_missing=True, summary=True, coverdir=coverdir) |
| |
| if runleaks: |
| os.system("leaks %d" % os.getpid()) |
| |
| sys.exit(len(bad) > 0) |
| |
| |
| STDTESTS = [ |
| 'test_grammar', |
| 'test_opcodes', |
| 'test_dict', |
| 'test_builtin', |
| 'test_exceptions', |
| 'test_types', |
| 'test_unittest', |
| 'test_doctest', |
| 'test_doctest2', |
| ] |
| |
| NOTTESTS = { |
| 'test_future1', |
| 'test_future2', |
| } |
| |
| def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS): |
| """Return a list of all applicable test modules.""" |
| if not testdir: testdir = findtestdir() |
| names = os.listdir(testdir) |
| tests = [] |
| for name in names: |
| if name[:5] == "test_" and name[-3:] == ".py": |
| modname = name[:-3] |
| if modname not in stdtests and modname not in nottests: |
| tests.append(modname) |
| tests.sort() |
| return stdtests + tests |
| |
| def replace_stdout(): |
| """Set stdout encoder error handler to backslashreplace (as stderr error |
| handler) to avoid UnicodeEncodeError when printing a traceback""" |
| if os.name == "nt": |
| # Replace sys.stdout breaks the stdout newlines on Windows: issue #8533 |
| return |
| stdout = sys.stdout |
| sys.stdout = open(stdout.fileno(), 'w', |
| encoding=stdout.encoding, |
| errors="backslashreplace") |
| |
| def runtest(test, generate, verbose, quiet, test_times, |
| testdir=None, huntrleaks=False, debug=False): |
| """Run a single test. |
| |
| test -- the name of the test |
| verbose -- if true, print more messages |
| quiet -- if true, don't print 'skipped' messages (probably redundant) |
| test_times -- a list of (time, test_name) pairs |
| testdir -- test directory |
| huntrleaks -- run multiple times to test for leaks; requires a debug |
| build; a triple corresponding to -R's three arguments |
| debug -- if true, print tracebacks for failed tests regardless of |
| verbose setting |
| Return: |
| -2 test skipped because resource denied |
| -1 test skipped for some other reason |
| 0 test failed |
| 1 test passed |
| """ |
| |
| try: |
| return runtest_inner(test, generate, verbose, quiet, test_times, |
| testdir, huntrleaks) |
| finally: |
| cleanup_test_droppings(test, verbose) |
| |
| def runtest_inner(test, generate, verbose, quiet, test_times, |
| testdir=None, huntrleaks=False, debug=False): |
| support.unload(test) |
| if not testdir: |
| testdir = findtestdir() |
| if verbose: |
| cfp = None |
| else: |
| cfp = io.StringIO() # XXX Should use io.StringIO() |
| |
| refleak = False # True if the test leaked references. |
| try: |
| save_stdout = sys.stdout |
| # Save various things that tests may mess up so we can restore |
| # them afterward. |
| save_environ = dict(os.environ) |
| save_argv = sys.argv[:] |
| try: |
| if cfp: |
| sys.stdout = cfp |
| print(test) # Output file starts with test name |
| if test.startswith('test.'): |
| abstest = test |
| else: |
| # Always import it from the test package |
| abstest = 'test.' + test |
| start_time = time.time() |
| the_package = __import__(abstest, globals(), locals(), []) |
| the_module = getattr(the_package, test) |
| # Old tests run to completion simply as a side-effect of |
| # being imported. For tests based on unittest or doctest, |
| # explicitly invoke their test_main() function (if it exists). |
| indirect_test = getattr(the_module, "test_main", None) |
| if indirect_test is not None: |
| indirect_test() |
| if huntrleaks: |
| refleak = dash_R(the_module, test, indirect_test, huntrleaks) |
| test_time = time.time() - start_time |
| test_times.append((test_time, test)) |
| finally: |
| sys.stdout = save_stdout |
| # Restore what we saved if needed, but also complain if the test |
| # changed it so that the test may eventually get fixed. |
| if not os.environ == save_environ: |
| if not quiet: |
| print("Warning: os.environ was modified by", test) |
| os.environ.clear() |
| os.environ.update(save_environ) |
| if not sys.argv == save_argv: |
| if not quiet: |
| print("Warning: argv was modified by", test) |
| sys.argv[:] = save_argv |
| except support.ResourceDenied as msg: |
| if not quiet: |
| print(test, "skipped --", msg) |
| sys.stdout.flush() |
| return -2 |
| except unittest.SkipTest as msg: |
| if not quiet: |
| print(test, "skipped --", msg) |
| sys.stdout.flush() |
| return -1 |
| except KeyboardInterrupt: |
| raise |
| except support.TestFailed as msg: |
| print("test", test, "failed --", msg) |
| sys.stdout.flush() |
| return 0 |
| except: |
| type, value = sys.exc_info()[:2] |
| print("test", test, "crashed --", str(type) + ":", value) |
| sys.stdout.flush() |
| if verbose or debug: |
| traceback.print_exc(file=sys.stdout) |
| sys.stdout.flush() |
| return 0 |
| else: |
| if refleak: |
| return 0 |
| if not cfp: |
| return 1 |
| output = cfp.getvalue() |
| expected = test + "\n" |
| if output == expected or huntrleaks: |
| return 1 |
| print("test", test, "produced unexpected output:") |
| sys.stdout.flush() |
| reportdiff(expected, output) |
| sys.stdout.flush() |
| return 0 |
| |
| def cleanup_test_droppings(testname, verbose): |
| import shutil |
| import stat |
| |
| # Try to clean up junk commonly left behind. While tests shouldn't leave |
| # any files or directories behind, when a test fails that can be tedious |
| # for it to arrange. The consequences can be especially nasty on Windows, |
| # since if a test leaves a file open, it cannot be deleted by name (while |
| # there's nothing we can do about that here either, we can display the |
| # name of the offending test, which is a real help). |
| for name in (support.TESTFN, |
| "db_home", |
| ): |
| if not os.path.exists(name): |
| continue |
| |
| if os.path.isdir(name): |
| kind, nuker = "directory", shutil.rmtree |
| elif os.path.isfile(name): |
| kind, nuker = "file", os.unlink |
| else: |
| raise SystemError("os.path says %r exists but is neither " |
| "directory nor file" % name) |
| |
| if verbose: |
| print("%r left behind %s %r" % (testname, kind, name)) |
| try: |
| # if we have chmod, fix possible permissions problems |
| # that might prevent cleanup |
| if (hasattr(os, 'chmod')): |
| os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) |
| nuker(name) |
| except Exception as msg: |
| print(("%r left behind %s %r and it couldn't be " |
| "removed: %s" % (testname, kind, name, msg)), file=sys.stderr) |
| |
| def dash_R(the_module, test, indirect_test, huntrleaks): |
| """Run a test multiple times, looking for reference leaks. |
| |
| Returns: |
| False if the test didn't leak references; True if we detected refleaks. |
| """ |
| # This code is hackish and inelegant, but it seems to do the job. |
| import copyreg, _abcoll |
| |
| if not hasattr(sys, 'gettotalrefcount'): |
| raise Exception("Tracking reference leaks requires a debug build " |
| "of Python") |
| |
| # Save current values for dash_R_cleanup() to restore. |
| fs = warnings.filters[:] |
| ps = copyreg.dispatch_table.copy() |
| pic = sys.path_importer_cache.copy() |
| abcs = {} |
| for abc in [getattr(_abcoll, a) for a in _abcoll.__all__]: |
| if not isabstract(abc): |
| continue |
| for obj in abc.__subclasses__() + [abc]: |
| abcs[obj] = obj._abc_registry.copy() |
| |
| if indirect_test: |
| def run_the_test(): |
| indirect_test() |
| else: |
| def run_the_test(): |
| del sys.modules[the_module.__name__] |
| exec('import ' + the_module.__name__) |
| |
| deltas = [] |
| nwarmup, ntracked, fname = huntrleaks |
| repcount = nwarmup + ntracked |
| print("beginning", repcount, "repetitions", file=sys.stderr) |
| print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr) |
| dash_R_cleanup(fs, ps, pic, abcs) |
| for i in range(repcount): |
| rc = sys.gettotalrefcount() |
| run_the_test() |
| sys.stderr.write('.') |
| sys.stderr.flush() |
| dash_R_cleanup(fs, ps, pic, abcs) |
| if i >= nwarmup: |
| deltas.append(sys.gettotalrefcount() - rc - 2) |
| print(file=sys.stderr) |
| if any(deltas): |
| msg = '%s leaked %s references, sum=%s' % (test, deltas, sum(deltas)) |
| print(msg, file=sys.stderr) |
| refrep = open(fname, "a") |
| print(msg, file=refrep) |
| refrep.close() |
| return True |
| return False |
| |
| def dash_R_cleanup(fs, ps, pic, abcs): |
| import gc, copyreg |
| import _strptime, linecache |
| import urllib.parse, urllib.request, mimetypes, doctest |
| import struct, filecmp, _abcoll |
| from distutils.dir_util import _path_created |
| from weakref import WeakSet |
| |
| # Clear the warnings registry, so they can be displayed again |
| for mod in sys.modules.values(): |
| if hasattr(mod, '__warningregistry__'): |
| del mod.__warningregistry__ |
| |
| # Restore some original values. |
| warnings.filters[:] = fs |
| copyreg.dispatch_table.clear() |
| copyreg.dispatch_table.update(ps) |
| sys.path_importer_cache.clear() |
| sys.path_importer_cache.update(pic) |
| |
| # clear type cache |
| sys._clear_type_cache() |
| |
| # Clear ABC registries, restoring previously saved ABC registries. |
| for abc in [getattr(_abcoll, a) for a in _abcoll.__all__]: |
| if not isabstract(abc): |
| continue |
| for obj in abc.__subclasses__() + [abc]: |
| obj._abc_registry = abcs.get(obj, WeakSet()).copy() |
| obj._abc_cache.clear() |
| obj._abc_negative_cache.clear() |
| |
| # Clear assorted module caches. |
| _path_created.clear() |
| re.purge() |
| _strptime._regex_cache.clear() |
| urllib.parse.clear_cache() |
| urllib.request.urlcleanup() |
| linecache.clearcache() |
| mimetypes._default_mime_types() |
| filecmp._cache.clear() |
| struct._clearcache() |
| doctest.master = None |
| |
| # Collect cyclic trash. |
| gc.collect() |
| |
| def warm_char_cache(): |
| s = bytes(range(256)) |
| for i in range(256): |
| s[i:i+1] |
| |
| def reportdiff(expected, output): |
| import difflib |
| print("*" * 70) |
| a = expected.splitlines(1) |
| b = output.splitlines(1) |
| sm = difflib.SequenceMatcher(a=a, b=b) |
| tuples = sm.get_opcodes() |
| |
| def pair(x0, x1): |
| # x0:x1 are 0-based slice indices; convert to 1-based line indices. |
| x0 += 1 |
| if x0 >= x1: |
| return "line " + str(x0) |
| else: |
| return "lines %d-%d" % (x0, x1) |
| |
| for op, a0, a1, b0, b1 in tuples: |
| if op == 'equal': |
| pass |
| |
| elif op == 'delete': |
| print("***", pair(a0, a1), "of expected output missing:") |
| for line in a[a0:a1]: |
| print("-", line, end='') |
| |
| elif op == 'replace': |
| print("*** mismatch between", pair(a0, a1), "of expected", \ |
| "output and", pair(b0, b1), "of actual output:") |
| for line in difflib.ndiff(a[a0:a1], b[b0:b1]): |
| print(line, end='') |
| |
| elif op == 'insert': |
| print("***", pair(b0, b1), "of actual output doesn't appear", \ |
| "in expected output after line", str(a1)+":") |
| for line in b[b0:b1]: |
| print("+", line, end='') |
| |
| else: |
| print("get_opcodes() returned bad tuple?!?!", (op, a0, a1, b0, b1)) |
| |
| print("*" * 70) |
| |
| def findtestdir(): |
| if __name__ == '__main__': |
| file = sys.argv[0] |
| else: |
| file = __file__ |
| testdir = os.path.dirname(file) or os.curdir |
| return testdir |
| |
| def removepy(name): |
| if name.endswith(".py"): |
| name = name[:-3] |
| return name |
| |
| def count(n, word): |
| if n == 1: |
| return "%d %s" % (n, word) |
| else: |
| return "%d %ss" % (n, word) |
| |
| def printlist(x, width=70, indent=4): |
| """Print the elements of iterable x to stdout. |
| |
| Optional arg width (default 70) is the maximum line length. |
| Optional arg indent (default 4) is the number of blanks with which to |
| begin each line. |
| """ |
| |
| from textwrap import fill |
| blanks = ' ' * indent |
| # Print the sorted list: 'x' may be a '--random' list or a set() |
| print(fill(' '.join(str(elt) for elt in sorted(x)), width, |
| initial_indent=blanks, subsequent_indent=blanks)) |
| |
| # Map sys.platform to a string containing the basenames of tests |
| # expected to be skipped on that platform. |
| # |
| # Special cases: |
| # test_pep277 |
| # The _ExpectedSkips constructor adds this to the set of expected |
| # skips if not os.path.supports_unicode_filenames. |
| # test_timeout |
| # Controlled by test_timeout.skip_expected. Requires the network |
| # resource and a socket module. |
| # |
| # Tests that are expected to be skipped everywhere except on one platform |
| # are also handled separately. |
| |
| _expectations = { |
| 'win32': |
| """ |
| test__locale |
| test_crypt |
| test_curses |
| test_dbm |
| test_fcntl |
| test_fork1 |
| test_epoll |
| test_dbm_gnu |
| test_dbm_ndbm |
| test_grp |
| test_ioctl |
| test_largefile |
| test_kqueue |
| test_openpty |
| test_ossaudiodev |
| test_pipes |
| test_poll |
| test_posix |
| test_pty |
| test_pwd |
| test_resource |
| test_signal |
| test_syslog |
| test_threadsignals |
| test_wait3 |
| test_wait4 |
| """, |
| 'linux2': |
| """ |
| test_curses |
| test_largefile |
| test_kqueue |
| test_ossaudiodev |
| """, |
| 'mac': |
| """ |
| test_atexit |
| test_bz2 |
| test_crypt |
| test_curses |
| test_dbm |
| test_fcntl |
| test_fork1 |
| test_epoll |
| test_grp |
| test_ioctl |
| test_largefile |
| test_locale |
| test_kqueue |
| test_mmap |
| test_openpty |
| test_ossaudiodev |
| test_poll |
| test_popen |
| test_posix |
| test_pty |
| test_pwd |
| test_resource |
| test_signal |
| test_sundry |
| test_tarfile |
| """, |
| 'unixware7': |
| """ |
| test_epoll |
| test_largefile |
| test_kqueue |
| test_minidom |
| test_openpty |
| test_pyexpat |
| test_sax |
| test_sundry |
| """, |
| 'openunix8': |
| """ |
| test_epoll |
| test_largefile |
| test_kqueue |
| test_minidom |
| test_openpty |
| test_pyexpat |
| test_sax |
| test_sundry |
| """, |
| 'sco_sv3': |
| """ |
| test_asynchat |
| test_fork1 |
| test_epoll |
| test_gettext |
| test_largefile |
| test_locale |
| test_kqueue |
| test_minidom |
| test_openpty |
| test_pyexpat |
| test_queue |
| test_sax |
| test_sundry |
| test_thread |
| test_threaded_import |
| test_threadedtempfile |
| test_threading |
| """, |
| 'darwin': |
| """ |
| test__locale |
| test_curses |
| test_epoll |
| test_dbm_gnu |
| test_largefile |
| test_locale |
| test_minidom |
| test_ossaudiodev |
| test_poll |
| """, |
| 'sunos5': |
| """ |
| test_curses |
| test_dbm |
| test_epoll |
| test_kqueue |
| test_dbm_gnu |
| test_gzip |
| test_openpty |
| test_zipfile |
| test_zlib |
| """, |
| 'hp-ux11': |
| """ |
| test_curses |
| test_epoll |
| test_dbm_gnu |
| test_gzip |
| test_largefile |
| test_locale |
| test_kqueue |
| test_minidom |
| test_openpty |
| test_pyexpat |
| test_sax |
| test_zipfile |
| test_zlib |
| """, |
| 'atheos': |
| """ |
| test_curses |
| test_dbm_gnu |
| test_epoll |
| test_largefile |
| test_locale |
| test_kqueue |
| test_mhlib |
| test_mmap |
| test_poll |
| test_resource |
| """, |
| 'cygwin': |
| """ |
| test_curses |
| test_dbm |
| test_epoll |
| test_ioctl |
| test_kqueue |
| test_largefile |
| test_locale |
| test_ossaudiodev |
| test_socketserver |
| """, |
| 'os2emx': |
| """ |
| test_audioop |
| test_curses |
| test_epoll |
| test_kqueue |
| test_largefile |
| test_mmap |
| test_openpty |
| test_ossaudiodev |
| test_pty |
| test_resource |
| test_signal |
| """, |
| 'freebsd4': |
| """ |
| test_epoll |
| test_dbm_gnu |
| test_locale |
| test_ossaudiodev |
| test_pep277 |
| test_pty |
| test_socketserver |
| test_tcl |
| test_tk |
| test_ttk_guionly |
| test_ttk_textonly |
| test_timeout |
| test_urllibnet |
| test_multiprocessing |
| """, |
| 'aix5': |
| """ |
| test_bz2 |
| test_epoll |
| test_dbm_gnu |
| test_gzip |
| test_kqueue |
| test_ossaudiodev |
| test_tcl |
| test_tk |
| test_ttk_guionly |
| test_ttk_textonly |
| test_zipimport |
| test_zlib |
| """, |
| 'openbsd3': |
| """ |
| test_ctypes |
| test_epoll |
| test_dbm_gnu |
| test_locale |
| test_normalization |
| test_ossaudiodev |
| test_pep277 |
| test_tcl |
| test_tk |
| test_ttk_guionly |
| test_ttk_textonly |
| test_multiprocessing |
| """, |
| 'netbsd3': |
| """ |
| test_ctypes |
| test_curses |
| test_epoll |
| test_dbm_gnu |
| test_locale |
| test_ossaudiodev |
| test_pep277 |
| test_tcl |
| test_tk |
| test_ttk_guionly |
| test_ttk_textonly |
| test_multiprocessing |
| """, |
| } |
| _expectations['freebsd5'] = _expectations['freebsd4'] |
| _expectations['freebsd6'] = _expectations['freebsd4'] |
| _expectations['freebsd7'] = _expectations['freebsd4'] |
| _expectations['freebsd8'] = _expectations['freebsd4'] |
| |
| class _ExpectedSkips: |
| def __init__(self): |
| import os.path |
| from test import test_timeout |
| |
| self.valid = False |
| if sys.platform in _expectations: |
| s = _expectations[sys.platform] |
| self.expected = set(s.split()) |
| |
| # These are broken tests, for now skipped on every platform. |
| # XXX Fix these! |
| self.expected.add('test_nis') |
| |
| # expected to be skipped on every platform, even Linux |
| if not os.path.supports_unicode_filenames: |
| self.expected.add('test_pep277') |
| |
| # doctest, profile and cProfile tests fail when the codec for the |
| # fs encoding isn't built in because PyUnicode_Decode() adds two |
| # calls into Python. |
| encs = ("utf-8", "latin-1", "ascii", "mbcs", "utf-16", "utf-32") |
| if sys.getfilesystemencoding().lower() not in encs: |
| self.expected.add('test_profile') |
| self.expected.add('test_cProfile') |
| self.expected.add('test_doctest') |
| |
| if test_timeout.skip_expected: |
| self.expected.add('test_timeout') |
| |
| if sys.platform != "win32": |
| # test_sqlite is only reliable on Windows where the library |
| # is distributed with Python |
| WIN_ONLY = ["test_unicode_file", "test_winreg", |
| "test_winsound", "test_startfile", |
| "test_sqlite"] |
| for skip in WIN_ONLY: |
| self.expected.add(skip) |
| |
| if sys.platform != 'sunos5': |
| self.expected.add('test_nis') |
| |
| self.valid = True |
| |
| def isvalid(self): |
| "Return true iff _ExpectedSkips knows about the current platform." |
| return self.valid |
| |
| def getexpected(self): |
| """Return set of test names we expect to skip on current platform. |
| |
| self.isvalid() must be true. |
| """ |
| |
| assert self.isvalid() |
| return self.expected |
| |
| if __name__ == '__main__': |
| # Remove regrtest.py's own directory from the module search path. This |
| # prevents relative imports from working, and relative imports will screw |
| # up the testing framework. E.g. if both test.support and |
| # support are imported, they will not contain the same globals, and |
| # much of the testing framework relies on the globals in the |
| # test.support module. |
| mydir = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0]))) |
| i = len(sys.path) |
| while i >= 0: |
| i -= 1 |
| if os.path.abspath(os.path.normpath(sys.path[i])) == mydir: |
| del sys.path[i] |
| main() |