Code

- Remove implementations of Class.getnode from back_anydbm and rdbms_common,
[roundup.git] / run_tests.py
1 #! /usr/bin/env python2.2
2 ##############################################################################
3 #
4 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
5 # All Rights Reserved.
6 #
7 # This software is subject to the provisions of the Zope Public License,
8 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
9 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
10 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
11 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
12 # FOR A PARTICULAR PURPOSE.
13 #
14 ##############################################################################
15 """
16 test.py [-aBbcdDfgGhLmprtTuv] [modfilter [testfilter]]
18 Test harness.
20 -a level
21 --all
22     Run the tests at the given level.  Any test at a level at or below this is
23     run, any test at a level above this is not run.  Level 0 runs all tests.
24     The default is to run tests at level 1.  --all is a shortcut for -a 0.
26 -b
27 --build
28     Run "python setup.py build" before running tests, where "python"
29     is the version of python used to run test.py.  Highly recommended.
30     Tests will be run from the build directory.  (Note: In Python < 2.3
31     the -q flag is added to the setup.py command line.)
33 -B
34     Run "python setup.py build_ext -i" before running tests.  Tests will be
35     run from the source directory.
37 -c  use pychecker
39 -d
40     Instead of the normal test harness, run a debug version which
41     doesn't catch any exceptions.  This is occasionally handy when the
42     unittest code catching the exception doesn't work right.
43     Unfortunately, the debug harness doesn't print the name of the
44     test, so Use With Care.
46 --dir directory
47     Option to limit where tests are searched for. This is
48     important when you *really* want to limit the code that gets run.
49     For example, if refactoring interfaces, you don't want to see the way
50     you have broken setups for tests in other packages. You *just* want to
51     run the interface tests.
53 -D
54     Works like -d, except that it loads pdb when an exception occurs.
56 -f
57     Run functional tests instead of unit tests.
59 -g threshold
60     Set the garbage collector generation0 threshold.  This can be used to
61     stress memory and gc correctness.  Some crashes are only reproducible when
62     the threshold is set to 1 (agressive garbage collection).  Do "-g 0" to
63     disable garbage collection altogether.
65 -G gc_option
66     Set the garbage collection debugging flags.  The argument must be one
67     of the DEBUG_ flags defined bythe Python gc module.  Multiple options
68     can be specified by using "-G OPTION1 -G OPTION2."
70 --libdir test_root
71     Search for tests starting in the specified start directory
72     (useful for testing components being developed outside the main
73     "src" or "build" trees).
75 --keepbytecode
76     Do not delete all stale bytecode before running tests
78 -L
79     Keep running the selected tests in a loop.  You may experience
80     memory leakage.
82 -t
83     Time the individual tests and print a list of the top 50, sorted from
84     longest to shortest.
86 -p
87     Show running progress.  It can be combined with -v or -vv.
89 -r
90     Look for refcount problems.
91     This requires that Python was built --with-pydebug.
93 -T
94     Use the trace module from Python for code coverage.  XXX This only works
95     if trace.py is explicitly added to PYTHONPATH.  The current utility writes
96     coverage files to a directory named `coverage' that is parallel to
97     `build'.  It also prints a summary to stdout.
99 -v
100     Verbose output.  With one -v, unittest prints a dot (".") for each test
101     run.  With -vv, unittest prints the name of each test (for some definition
102     of "name" ...).  With no -v, unittest is silent until the end of the run,
103     except when errors occur.
105     When -p is also specified, the meaning of -v is sligtly changed.  With
106     -p and no -v only the percent indicator is displayed.  With -p and -v
107     the test name of the current test is shown to the right of the percent
108     indicator.  With -p and -vv the test name is not truncated to fit into
109     80 columns and it is not cleared after the test finishes.
111 -u
112 -m
113     Use the PyUnit GUI instead of output to the command line.  The GUI imports
114     tests on its own, taking care to reload all dependencies on each run.  The
115     debug (-d), verbose (-v), progress (-p), and Loop (-L) options will be
116     ignored.  The testfilter filter is also not applied.
118     -m starts the gui minimized.  Double-clicking the progress bar will start
119     the import and run all tests.
122 modfilter
123 testfilter
124     Case-sensitive regexps to limit which tests are run, used in search
125     (not match) mode.
126     In an extension of Python regexp notation, a leading "!" is stripped
127     and causes the sense of the remaining regexp to be negated (so "!bc"
128     matches any string that does not match "bc", and vice versa).
129     By default these act like ".", i.e. nothing is excluded.
131     modfilter is applied to a test file's path, starting at "build" and
132     including (OS-dependent) path separators.
134     testfilter is applied to the (method) name of the unittest methods
135     contained in the test files whose paths modfilter matched.
137 Extreme (yet useful) examples:
139     test.py -vvb . "^testWriteClient$"
141     Builds the project silently, then runs unittest in verbose mode on all
142     tests whose names are precisely "testWriteClient".  Useful when
143     debugging a specific test.
145     test.py -vvb . "!^testWriteClient$"
147     As before, but runs all tests whose names aren't precisely
148     "testWriteClient".  Useful to avoid a specific failing test you don't
149     want to deal with just yet.
151     test.py -m . "!^testWriteClient$"
153     As before, but now opens up a minimized PyUnit GUI window (only showing
154     the progress bar).  Useful for refactoring runs where you continually want
155     to make sure all tests still pass.
156 """
158 import gc
159 import os
160 import re
161 import pdb
162 import sys
163 import threading    # just to get at Thread objects created by tests
164 import time
165 import traceback
166 import unittest
167 import warnings
169 from distutils.util import get_platform
171 PLAT_SPEC = "%s-%s" % (get_platform(), sys.version[0:3])
173 class ImmediateTestResult(unittest._TextTestResult):
175     __super_init = unittest._TextTestResult.__init__
176     __super_startTest = unittest._TextTestResult.startTest
177     __super_printErrors = unittest._TextTestResult.printErrors
179     def __init__(self, stream, descriptions, verbosity, debug=0,
180                  count=None, progress=0):
181         self.__super_init(stream, descriptions, verbosity)
182         self._debug = debug
183         self._progress = progress
184         self._progressWithNames = 0
185         self._count = count
186         self._testtimes = {}
187         if progress and verbosity == 1:
188             self.dots = 0
189             self._progressWithNames = 1
190             self._lastWidth = 0
191             self._maxWidth = 80
192             try:
193                 import curses
194             except ImportError:
195                 pass
196             else:
197                 curses.setupterm()
198                 self._maxWidth = curses.tigetnum('cols')
199             self._maxWidth -= len("xxxx/xxxx (xxx.x%): ") + 1
201     def stopTest(self, test):
202         self._testtimes[test] = time.time() - self._testtimes[test]
203         if gc.garbage:
204             print "The following test left garbage:"
205             print test
206             print gc.garbage
207             # eat the garbage here, so that the garbage isn't
208             # printed for every subsequent test.
209             gc.garbage[:] = []
211         # Did the test leave any new threads behind?
212         new_threads = [t for t in threading.enumerate()
213                          if (t.isAlive()
214                              and
215                              t not in self._threads)]
216         if new_threads:
217             print "The following test left new threads behind:"
218             print test
219             print "New thread(s):", new_threads
221     def print_times(self, stream, count=None):
222         results = self._testtimes.items()
223         results.sort(lambda x, y: cmp(y[1], x[1]))
224         if count:
225             n = min(count, len(results))
226             if n:
227                 print >>stream, "Top %d longest tests:" % n
228         else:
229             n = len(results)
230         if not n:
231             return
232         for i in range(n):
233             print >>stream, "%6dms" % int(results[i][1] * 1000), results[i][0]
235     def _print_traceback(self, msg, err, test, errlist):
236         if self.showAll or self.dots or self._progress:
237             self.stream.writeln("\n")
238             self._lastWidth = 0
240         tb = "".join(traceback.format_exception(*err))
241         self.stream.writeln(msg)
242         self.stream.writeln(tb)
243         errlist.append((test, tb))
245     def startTest(self, test):
246         if self._progress:
247             self.stream.write("\r%4d" % (self.testsRun + 1))
248             if self._count:
249                 self.stream.write("/%d (%5.1f%%)" % (self._count,
250                                   (self.testsRun + 1) * 100.0 / self._count))
251             if self.showAll:
252                 self.stream.write(": ")
253             elif self._progressWithNames:
254                 # XXX will break with multibyte strings
255                 name = self.getShortDescription(test)
256                 width = len(name)
257                 if width < self._lastWidth:
258                     name += " " * (self._lastWidth - width)
259                 self.stream.write(": %s" % name)
260                 self._lastWidth = width
261             self.stream.flush()
262         self._threads = threading.enumerate()
263         self.__super_startTest(test)
264         self._testtimes[test] = time.time()
266     def getShortDescription(self, test):
267         s = self.getDescription(test)
268         if len(s) > self._maxWidth:
269             pos = s.find(" (")
270             if pos >= 0:
271                 w = self._maxWidth - (pos + 5)
272                 if w < 1:
273                     # first portion (test method name) is too long
274                     s = s[:self._maxWidth-3] + "..."
275                 else:
276                     pre = s[:pos+2]
277                     post = s[-w:]
278                     s = "%s...%s" % (pre, post)
279         return s[:self._maxWidth]
281     def addError(self, test, err):
282         if self._progress:
283             self.stream.write("\r")
284         if self._debug:
285             raise err[0], err[1], err[2]
286         self._print_traceback("Error in test %s" % test, err,
287                               test, self.errors)
289     def addFailure(self, test, err):
290         if self._progress:
291             self.stream.write("\r")
292         if self._debug:
293             raise err[0], err[1], err[2]
294         self._print_traceback("Failure in test %s" % test, err,
295                               test, self.failures)
297     def printErrors(self):
298         if self._progress and not (self.dots or self.showAll):
299             self.stream.writeln()
300         self.__super_printErrors()
302     def printErrorList(self, flavor, errors):
303         for test, err in errors:
304             self.stream.writeln(self.separator1)
305             self.stream.writeln("%s: %s" % (flavor, self.getDescription(test)))
306             self.stream.writeln(self.separator2)
307             self.stream.writeln(err)
310 class ImmediateTestRunner(unittest.TextTestRunner):
312     __super_init = unittest.TextTestRunner.__init__
314     def __init__(self, **kwarg):
315         debug = kwarg.get("debug")
316         if debug is not None:
317             del kwarg["debug"]
318         progress = kwarg.get("progress")
319         if progress is not None:
320             del kwarg["progress"]
321         self.__super_init(**kwarg)
322         self._debug = debug
323         self._progress = progress
325     def _makeResult(self):
326         return ImmediateTestResult(self.stream, self.descriptions,
327                                    self.verbosity, debug=self._debug,
328                                    count=self._count, progress=self._progress)
330     def run(self, test):
331         self._count = test.countTestCases()
332         return unittest.TextTestRunner.run(self, test)
334 # setup list of directories to put on the path
335 class PathInit:
336     def __init__(self, build, build_inplace, libdir=None):
337         self.inplace = None
338         # Figure out if we should test in-place or test in-build.  If the -b
339         # or -B option was given, test in the place we were told to build in.
340         # Otherwise, we'll look for a build directory and if we find one,
341         # we'll test there, otherwise we'll test in-place.
342         if build:
343             self.inplace = build_inplace
344         if self.inplace is None:
345             # Need to figure it out
346             if os.path.isdir(os.path.join("build", "lib.%s" % PLAT_SPEC)):
347                 self.inplace = 0
348             else:
349                 self.inplace = 1
350         # Calculate which directories we're going to add to sys.path, and cd
351         # to the appropriate working directory
352         org_cwd = os.getcwd()
353         if self.inplace:
354             self.libdir = "src"
355         else:
356             self.libdir = "lib.%s" % PLAT_SPEC
357             os.chdir("build")
358         # Hack sys.path
359         self.cwd = os.getcwd()
360         sys.path.insert(0, os.path.join(self.cwd, self.libdir))
361         # Hack again for external products.
362         global functional
363         kind = functional and "functional" or "unit"
364         if libdir:
365             extra = os.path.join(org_cwd, libdir)
366             print "Running %s tests from %s" % (kind, extra)
367             self.libdir = extra
368             sys.path.insert(0, extra)
369         else:
370             print "Running %s tests from %s" % (kind, self.cwd)
371         # Make sure functional tests find ftesting.zcml
372         if functional:
373             config_file = 'ftesting.zcml'
374             if not self.inplace:
375                 # We chdired into build, so ftesting.zcml is in the
376                 # parent directory
377                 config_file = os.path.join('..', 'ftesting.zcml')
378             print "Parsing %s" % config_file
379             from zope.testing.functional import FunctionalTestSetup
380             FunctionalTestSetup(config_file)
382 def match(rx, s):
383     if not rx:
384         return 1
385     if rx[0] == "!":
386         return re.search(rx[1:], s) is None
387     else:
388         return re.search(rx, s) is not None
390 class TestFileFinder:
391     def __init__(self, prefix):
392         self.files = []
393         self._plen = len(prefix)
394         if not prefix.endswith(os.sep):
395             self._plen += 1
396         global functional
397         if functional:
398             self.dirname = "ftest"
399         else:
400             self.dirname = "test"
402     def visit(self, rx, dir, files):
403         if os.path.split(dir)[1] != self.dirname:
404             return
405         # ignore tests that aren't in packages
406         if not "__init__.py" in files:
407             if not files or files == ["CVS"]:
408                 return
409             print "not a package", dir
410             return
412         # Put matching files in matches.  If matches is non-empty,
413         # then make sure that the package is importable.
414         matches = []
415         for file in files:
416             if file.startswith('test') and os.path.splitext(file)[-1] == '.py':
417                 path = os.path.join(dir, file)
418                 if match(rx, path):
419                     matches.append(path)
421         # ignore tests when the package can't be imported, possibly due to
422         # dependency failures.
423         pkg = dir[self._plen:].replace(os.sep, '.')
424         try:
425             __import__(pkg)
426         # We specifically do not want to catch ImportError since that's useful
427         # information to know when running the tests.
428         except RuntimeError, e:
429             if VERBOSE:
430                 print "skipping %s because: %s" % (pkg, e)
431             return
432         else:
433             self.files.extend(matches)
435     def module_from_path(self, path):
436         """Return the Python package name indicated by the filesystem path."""
437         assert path.endswith(".py")
438         path = path[self._plen:-3]
439         mod = path.replace(os.sep, ".")
440         return mod
442 def walk_with_symlinks(top, func, arg):
443     """Like os.path.walk, but follows symlinks on POSIX systems.
445     This could theoreticaly result in an infinite loop, if you create symlink
446     cycles in your Zope sandbox, so don't do that.
447     """
448     try:
449         names = os.listdir(top)
450     except os.error:
451         return
452     func(arg, top, names)
453     exceptions = ('.', '..')
454     for name in names:
455         if name not in exceptions:
456             name = os.path.join(top, name)
457             if os.path.isdir(name):
458                 walk_with_symlinks(name, func, arg)
461 def check_test_dir():
462     global test_dir
463     if test_dir and not os.path.exists(test_dir):
464         d = pathinit.libdir
465         d = os.path.join(d, test_dir)
466         if os.path.exists(d):
467             if not os.path.isdir(d):
468                 raise ValueError(
469                     "%s does not exist and %s is not a directory"
470                     % (test_dir, d)
471                     )
472             test_dir = d
473         else:
474             raise ValueError("%s does not exist!" % test_dir)
477 def find_tests(rx):
478     global finder
479     finder = TestFileFinder(pathinit.libdir)
481     check_test_dir()
482     walkdir = test_dir or pathinit.libdir
483     walk_with_symlinks(walkdir, finder.visit, rx)
484     return finder.files
486 def package_import(modname):
487     mod = __import__(modname)
488     for part in modname.split(".")[1:]:
489         mod = getattr(mod, part)
490     return mod
492 def get_suite(file):
493     modname = finder.module_from_path(file)
494     try:
495         mod = package_import(modname)
496     except ImportError, err:
497         # print traceback
498         print "Error importing %s\n%s" % (modname, err)
499         traceback.print_exc()
500         if debug:
501             raise
502         return None
503     try:
504         suite_func = mod.test_suite
505     except AttributeError:
506         print "No test_suite() in %s" % file
507         return None
508     return suite_func()
510 def filter_testcases(s, rx):
511     new = unittest.TestSuite()
512     for test in s._tests:
513         # See if the levels match
514         dolevel = (level == 0) or level >= getattr(test, "level", 0)
515         if not dolevel:
516             continue
517         if isinstance(test, unittest.TestCase):
518             name = test.id() # Full test name: package.module.class.method
519             name = name[1 + name.rfind("."):] # extract method name
520             if not rx or match(rx, name):
521                 new.addTest(test)
522         else:
523             filtered = filter_testcases(test, rx)
524             if filtered:
525                 new.addTest(filtered)
526     return new
528 def gui_runner(files, test_filter):
529     if build_inplace:
530         utildir = os.path.join(os.getcwd(), "utilities")
531     else:
532         utildir = os.path.join(os.getcwd(), "..", "utilities")
533     sys.path.append(utildir)
534     import unittestgui
535     suites = []
536     for file in files:
537         suites.append(finder.module_from_path(file) + ".test_suite")
539     suites = ", ".join(suites)
540     minimal = (GUI == "minimal")
541     unittestgui.main(suites, minimal)
543 class TrackRefs:
544     """Object to track reference counts across test runs."""
546     def __init__(self):
547         self.type2count = {}
548         self.type2all = {}
550     def update(self):
551         obs = sys.getobjects(0)
552         type2count = {}
553         type2all = {}
554         for o in obs:
555             all = sys.getrefcount(o)
556             t = type(o)
557             if t in type2count:
558                 type2count[t] += 1
559                 type2all[t] += all
560             else:
561                 type2count[t] = 1
562                 type2all[t] = all
564         ct = [(type2count[t] - self.type2count.get(t, 0),
565                type2all[t] - self.type2all.get(t, 0),
566                t)
567               for t in type2count.iterkeys()]
568         ct.sort()
569         ct.reverse()
570         for delta1, delta2, t in ct:
571             if delta1 or delta2:
572                 print "%-55s %8d %8d" % (t, delta1, delta2)
574         self.type2count = type2count
575         self.type2all = type2all
577 def runner(files, test_filter, debug):
578     runner = ImmediateTestRunner(verbosity=VERBOSE, debug=debug,
579                                  progress=progress)
580     suite = unittest.TestSuite()
581     for file in files:
582         s = get_suite(file)
583         # See if the levels match
584         dolevel = (level == 0) or level >= getattr(s, "level", 0)
585         if s is not None and dolevel:
586             s = filter_testcases(s, test_filter)
587             suite.addTest(s)
588     try:
589         r = runner.run(suite)
590         if timesfn:
591             r.print_times(open(timesfn, "w"))
592             if VERBOSE:
593                 print "Wrote timing data to", timesfn
594         if timetests:
595             r.print_times(sys.stdout, timetests)
596     except:
597         if debugger:
598             print "%s:" % (sys.exc_info()[0], )
599             print sys.exc_info()[1]
600             pdb.post_mortem(sys.exc_info()[2])
601         else:
602             raise
604 def remove_stale_bytecode(arg, dirname, names):
605     names = map(os.path.normcase, names)
606     for name in names:
607         if name.endswith(".pyc") or name.endswith(".pyo"):
608             srcname = name[:-1]
609             if srcname not in names:
610                 fullname = os.path.join(dirname, name)
611                 print "Removing stale bytecode file", fullname
612                 os.unlink(fullname)
614 def main(module_filter, test_filter, libdir):
615     if not keepStaleBytecode:
616         os.path.walk(os.curdir, remove_stale_bytecode, None)
618     # Get the log.ini file from the current directory instead of possibly
619     # buried in the build directory.  XXX This isn't perfect because if
620     # log.ini specifies a log file, it'll be relative to the build directory.
621     # Hmm...
622     logini = os.path.abspath("log.ini")
624     # Initialize the path and cwd
625     global pathinit
626     pathinit = PathInit(build, build_inplace, libdir)
628 # No logging module in py 2.1
629 #    # Initialize the logging module.
631 #    import logging.config
632 #    logging.basicConfig()
634 #    level = os.getenv("LOGGING")
635 #    if level:
636 #        level = int(level)
637 #    else:
638 #        level = logging.CRITICAL
639 #    logging.root.setLevel(level)
641 #    if os.path.exists(logini):
642 #        logging.config.fileConfig(logini)
644     files = find_tests(module_filter)
645     files.sort()
647     if GUI:
648         gui_runner(files, test_filter)
649     elif LOOP:
650         if REFCOUNT:
651             rc = sys.gettotalrefcount()
652             track = TrackRefs()
653         while 1:
654             runner(files, test_filter, debug)
655             gc.collect()
656             if gc.garbage:
657                 print "GARBAGE:", len(gc.garbage), gc.garbage
658                 return
659             if REFCOUNT:
660                 prev = rc
661                 rc = sys.gettotalrefcount()
662                 print "totalrefcount=%-8d change=%-6d" % (rc, rc - prev)
663                 track.update()
664     else:
665         runner(files, test_filter, debug)
668 def process_args(argv=None):
669     import getopt
670     global module_filter
671     global test_filter
672     global VERBOSE
673     global LOOP
674     global GUI
675     global TRACE
676     global REFCOUNT
677     global debug
678     global debugger
679     global build
680     global level
681     global libdir
682     global timesfn
683     global timetests
684     global progress
685     global build_inplace
686     global keepStaleBytecode
687     global functional
688     global test_dir
690     if argv is None:
691         argv = sys.argv
693     module_filter = None
694     test_filter = None
695     VERBOSE = 1
696     LOOP = 0
697     GUI = 0
698     TRACE = 0
699     REFCOUNT = 0
700     debug = 0 # Don't collect test results; simply let tests crash
701     debugger = 0
702     build = 0
703     build_inplace = 0
704     gcthresh = None
705     gcdebug = 0
706     gcflags = []
707     level = 1
708     libdir = '.'
709     progress = 0
710     timesfn = None
711     timetests = 0
712     keepStaleBytecode = 0
713     functional = 0
714     test_dir = None
716     try:
717         opts, args = getopt.getopt(argv[1:], "a:bBcdDfg:G:hLmprtTuv",
718                                    ["all", "help", "libdir=", "times=",
719                                     "keepbytecode", "dir=", "build"])
720     except getopt.error, msg:
721         print msg
722         print "Try `python %s -h' for more information." % argv[0]
723         sys.exit(2)
725     for k, v in opts:
726         if k == "-a":
727             level = int(v)
728         elif k == "--all":
729             level = 0
730             os.environ["COMPLAIN_IF_TESTS_MISSED"]='1'
731         elif k in ("-b", "--build"):
732             build = 1
733         elif k == "-B":
734              build = build_inplace = 1
735         elif k == "-c":
736             # make sure you have a recent version of pychecker
737             if not os.environ.get("PYCHECKER"):
738                 os.environ["PYCHECKER"] = "-q"
739             import pychecker.checker
740         elif k == "-d":
741             debug = 1
742         elif k == "-D":
743             debug = 1
744             debugger = 1
745         elif k == "-f":
746             functional = 1
747         elif k in ("-h", "--help"):
748             print __doc__
749             sys.exit(0)
750         elif k == "-g":
751             gcthresh = int(v)
752         elif k == "-G":
753             if not v.startswith("DEBUG_"):
754                 print "-G argument must be DEBUG_ flag, not", repr(v)
755                 sys.exit(1)
756             gcflags.append(v)
757         elif k == '--keepbytecode':
758             keepStaleBytecode = 1
759         elif k == '--libdir':
760             libdir = v
761         elif k == "-L":
762             LOOP = 1
763         elif k == "-m":
764             GUI = "minimal"
765         elif k == "-p":
766             progress = 1
767         elif k == "-r":
768             if hasattr(sys, "gettotalrefcount"):
769                 REFCOUNT = 1
770             else:
771                 print "-r ignored, because it needs a debug build of Python"
772         elif k == "-T":
773             TRACE = 1
774         elif k == "-t":
775             if not timetests:
776                 timetests = 50
777         elif k == "-u":
778             GUI = 1
779         elif k == "-v":
780             VERBOSE += 1
781         elif k == "--times":
782             try:
783                 timetests = int(v)
784             except ValueError:
785                 # must be a filename to write
786                 timesfn = v
787         elif k == '--dir':
788             test_dir = v
790     if gcthresh is not None:
791         if gcthresh == 0:
792             gc.disable()
793             print "gc disabled"
794         else:
795             gc.set_threshold(gcthresh)
796             print "gc threshold:", gc.get_threshold()
798     if gcflags:
799         val = 0
800         for flag in gcflags:
801             v = getattr(gc, flag, None)
802             if v is None:
803                 print "Unknown gc flag", repr(flag)
804                 print gc.set_debug.__doc__
805                 sys.exit(1)
806             val |= v
807         gcdebug |= v
809     if gcdebug:
810         gc.set_debug(gcdebug)
812     if build:
813         # Python 2.3 is more sane in its non -q output
814         if sys.hexversion >= 0x02030000:
815             qflag = ""
816         else:
817             qflag = "-q"
818         cmd = sys.executable + " setup.py " + qflag + " build"
819         if build_inplace:
820             cmd += "_ext -i"
821         if VERBOSE:
822             print cmd
823         sts = os.system(cmd)
824         if sts:
825             print "Build failed", hex(sts)
826             sys.exit(1)
828     if VERBOSE:
829         kind = functional and "functional" or "unit"
830         if level == 0:
831             print "Running %s tests at all levels" % kind
832         else:
833             print "Running %s tests at level %d" % (kind, level)
835     # XXX We want to change *visible* warnings into errors.  The next
836     # line changes all warnings into errors, including warnings we
837     # normally never see.  In particular, test_datetime does some
838     # short-integer arithmetic that overflows to long ints, and, by
839     # default, Python doesn't display the overflow warning that can
840     # be enabled when this happens.  The next line turns that into an
841     # error instead.  Guido suggests that a better to get what we're
842     # after is to replace warnings.showwarning() with our own thing
843     # that raises an error.
844 ##    warnings.filterwarnings("error")
845     warnings.filterwarnings("ignore", module="logging")
847     if args:
848         if len(args) > 1:
849             test_filter = args[1]
850         module_filter = args[0]
851     try:
852         if TRACE:
853             # if the trace module is used, then we don't exit with
854             # status if on a false return value from main.
855             coverdir = os.path.join(os.getcwd(), "coverage")
856             import trace
857             ignoremods = ["os", "posixpath", "stat"]
858             tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix],
859                                  ignoremods=ignoremods,
860                                  trace=0, count=1)
862             tracer.runctx("main(module_filter, test_filter, libdir)",
863                           globals=globals(), locals=vars())
864             r = tracer.results()
865             path = "/tmp/trace.%s" % os.getpid()
866             import cPickle
867             f = open(path, "wb")
868             cPickle.dump(r, f)
869             f.close()
870             print path
871             r.write_results(show_missing=1, summary=1, coverdir=coverdir)
872         else:
873             bad = main(module_filter, test_filter, libdir)
874             if bad:
875                 sys.exit(1)
876     except ImportError, err:
877         print err
878         print sys.path
879         raise
882 if __name__ == "__main__":
883     process_args()