Code

Use assertRaises instead of introducing _nocheck machinery into assertRaisesMessage.
[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         print sys.path
362         # Hack again for external products.
363         global functional
364         kind = functional and "functional" or "unit"
365         if libdir:
366             extra = os.path.join(org_cwd, libdir)
367             print "Running %s tests from %s" % (kind, extra)
368             self.libdir = extra
369             sys.path.insert(0, extra)
370         else:
371             print "Running %s tests from %s" % (kind, self.cwd)
372         # Make sure functional tests find ftesting.zcml
373         if functional:
374             config_file = 'ftesting.zcml'
375             if not self.inplace:
376                 # We chdired into build, so ftesting.zcml is in the
377                 # parent directory
378                 config_file = os.path.join('..', 'ftesting.zcml')
379             print "Parsing %s" % config_file
380             from zope.testing.functional import FunctionalTestSetup
381             FunctionalTestSetup(config_file)
383 def match(rx, s):
384     if not rx:
385         return 1
386     if rx[0] == "!":
387         return re.search(rx[1:], s) is None
388     else:
389         return re.search(rx, s) is not None
391 class TestFileFinder:
392     def __init__(self, prefix):
393         self.files = []
394         self._plen = len(prefix)
395         if not prefix.endswith(os.sep):
396             self._plen += 1
397         global functional
398         if functional:
399             self.dirname = "ftest"
400         else:
401             self.dirname = "test"
403     def visit(self, rx, dir, files):
404         if os.path.split(dir)[1] != self.dirname:
405             return
406         # ignore tests that aren't in packages
407         if not "__init__.py" in files:
408             if not files or files == ["CVS"]:
409                 return
410             print "not a package", dir
411             return
413         # Put matching files in matches.  If matches is non-empty,
414         # then make sure that the package is importable.
415         matches = []
416         for file in files:
417             if file.startswith('test') and os.path.splitext(file)[-1] == '.py':
418                 path = os.path.join(dir, file)
419                 if match(rx, path):
420                     matches.append(path)
422         # ignore tests when the package can't be imported, possibly due to
423         # dependency failures.
424         pkg = dir[self._plen:].replace(os.sep, '.')
425         try:
426             __import__(pkg)
427         # We specifically do not want to catch ImportError since that's useful
428         # information to know when running the tests.
429         except RuntimeError, e:
430             if VERBOSE:
431                 print "skipping %s because: %s" % (pkg, e)
432             return
433         else:
434             self.files.extend(matches)
436     def module_from_path(self, path):
437         """Return the Python package name indicated by the filesystem path."""
438         assert path.endswith(".py")
439         path = path[self._plen:-3]
440         mod = path.replace(os.sep, ".")
441         return mod
443 def walk_with_symlinks(top, func, arg):
444     """Like os.path.walk, but follows symlinks on POSIX systems.
446     This could theoreticaly result in an infinite loop, if you create symlink
447     cycles in your Zope sandbox, so don't do that.
448     """
449     try:
450         names = os.listdir(top)
451     except os.error:
452         return
453     func(arg, top, names)
454     exceptions = ('.', '..')
455     for name in names:
456         if name not in exceptions:
457             name = os.path.join(top, name)
458             if os.path.isdir(name):
459                 walk_with_symlinks(name, func, arg)
462 def check_test_dir():
463     global test_dir
464     if test_dir and not os.path.exists(test_dir):
465         d = pathinit.libdir
466         d = os.path.join(d, test_dir)
467         if os.path.exists(d):
468             if not os.path.isdir(d):
469                 raise ValueError(
470                     "%s does not exist and %s is not a directory"
471                     % (test_dir, d)
472                     )
473             test_dir = d
474         else:
475             raise ValueError("%s does not exist!" % test_dir)
478 def find_tests(rx):
479     global finder
480     finder = TestFileFinder(pathinit.libdir)
482     check_test_dir()
483     walkdir = test_dir or pathinit.libdir
484     walk_with_symlinks(walkdir, finder.visit, rx)
485     return finder.files
487 def package_import(modname):
488     mod = __import__(modname)
489     for part in modname.split(".")[1:]:
490         mod = getattr(mod, part)
491     return mod
493 def get_suite(file):
494     modname = finder.module_from_path(file)
495     try:
496         mod = package_import(modname)
497     except ImportError, err:
498         # print traceback
499         print "Error importing %s\n%s" % (modname, err)
500         traceback.print_exc()
501         if debug:
502             raise
503         return None
504     try:
505         suite_func = mod.test_suite
506     except AttributeError:
507         print "No test_suite() in %s" % file
508         return None
509     return suite_func()
511 def filter_testcases(s, rx):
512     new = unittest.TestSuite()
513     for test in s._tests:
514         # See if the levels match
515         dolevel = (level == 0) or level >= getattr(test, "level", 0)
516         if not dolevel:
517             continue
518         if isinstance(test, unittest.TestCase):
519             name = test.id() # Full test name: package.module.class.method
520             name = name[1 + name.rfind("."):] # extract method name
521             if not rx or match(rx, name):
522                 new.addTest(test)
523         else:
524             filtered = filter_testcases(test, rx)
525             if filtered:
526                 new.addTest(filtered)
527     return new
529 def gui_runner(files, test_filter):
530     if build_inplace:
531         utildir = os.path.join(os.getcwd(), "utilities")
532     else:
533         utildir = os.path.join(os.getcwd(), "..", "utilities")
534     sys.path.append(utildir)
535     import unittestgui
536     suites = []
537     for file in files:
538         suites.append(finder.module_from_path(file) + ".test_suite")
540     suites = ", ".join(suites)
541     minimal = (GUI == "minimal")
542     # unittestgui apparently doesn't take the minimal flag anymore
543     unittestgui.main(suites)
545 class TrackRefs:
546     """Object to track reference counts across test runs."""
548     def __init__(self):
549         self.type2count = {}
550         self.type2all = {}
552     def update(self):
553         obs = sys.getobjects(0)
554         type2count = {}
555         type2all = {}
556         for o in obs:
557             all = sys.getrefcount(o)
558             t = type(o)
559             if t in type2count:
560                 type2count[t] += 1
561                 type2all[t] += all
562             else:
563                 type2count[t] = 1
564                 type2all[t] = all
566         ct = [(type2count[t] - self.type2count.get(t, 0),
567                type2all[t] - self.type2all.get(t, 0),
568                t)
569               for t in type2count.iterkeys()]
570         ct.sort()
571         ct.reverse()
572         for delta1, delta2, t in ct:
573             if delta1 or delta2:
574                 print "%-55s %8d %8d" % (t, delta1, delta2)
576         self.type2count = type2count
577         self.type2all = type2all
579 def runner(files, test_filter, debug):
580     runner = ImmediateTestRunner(verbosity=VERBOSE, debug=debug,
581                                  progress=progress)
582     suite = unittest.TestSuite()
583     for file in files:
584         s = get_suite(file)
585         # See if the levels match
586         dolevel = (level == 0) or level >= getattr(s, "level", 0)
587         if s is not None and dolevel:
588             s = filter_testcases(s, test_filter)
589             suite.addTest(s)
590     try:
591         r = runner.run(suite)
592         if timesfn:
593             r.print_times(open(timesfn, "w"))
594             if VERBOSE:
595                 print "Wrote timing data to", timesfn
596         if timetests:
597             r.print_times(sys.stdout, timetests)
598     except:
599         if debugger:
600             print "%s:" % (sys.exc_info()[0], )
601             print sys.exc_info()[1]
602             pdb.post_mortem(sys.exc_info()[2])
603         else:
604             raise
606 def remove_stale_bytecode(arg, dirname, names):
607     names = map(os.path.normcase, names)
608     for name in names:
609         if name.endswith(".pyc") or name.endswith(".pyo"):
610             srcname = name[:-1]
611             if srcname not in names:
612                 fullname = os.path.join(dirname, name)
613                 print "Removing stale bytecode file", fullname
614                 os.unlink(fullname)
616 def main(module_filter, test_filter, libdir):
617     if not keepStaleBytecode:
618         os.path.walk(os.curdir, remove_stale_bytecode, None)
620     # Get the log.ini file from the current directory instead of possibly
621     # buried in the build directory.  XXX This isn't perfect because if
622     # log.ini specifies a log file, it'll be relative to the build directory.
623     # Hmm...
624     logini = os.path.abspath("log.ini")
626     # Initialize the path and cwd
627     global pathinit
628     pathinit = PathInit(build, build_inplace, libdir)
630 # No logging module in py 2.1
631 #    # Initialize the logging module.
633 #    import logging.config
634 #    logging.basicConfig()
636 #    level = os.getenv("LOGGING")
637 #    if level:
638 #        level = int(level)
639 #    else:
640 #        level = logging.CRITICAL
641 #    logging.root.setLevel(level)
643 #    if os.path.exists(logini):
644 #        logging.config.fileConfig(logini)
646     files = find_tests(module_filter)
647     files.sort()
649     if GUI:
650         gui_runner(files, test_filter)
651     elif LOOP:
652         if REFCOUNT:
653             rc = sys.gettotalrefcount()
654             track = TrackRefs()
655         while 1:
656             runner(files, test_filter, debug)
657             gc.collect()
658             if gc.garbage:
659                 print "GARBAGE:", len(gc.garbage), gc.garbage
660                 return
661             if REFCOUNT:
662                 prev = rc
663                 rc = sys.gettotalrefcount()
664                 print "totalrefcount=%-8d change=%-6d" % (rc, rc - prev)
665                 track.update()
666     else:
667         runner(files, test_filter, debug)
670 def process_args(argv=None):
671     import getopt
672     global module_filter
673     global test_filter
674     global VERBOSE
675     global LOOP
676     global GUI
677     global TRACE
678     global REFCOUNT
679     global debug
680     global debugger
681     global build
682     global level
683     global libdir
684     global timesfn
685     global timetests
686     global progress
687     global build_inplace
688     global keepStaleBytecode
689     global functional
690     global test_dir
692     if argv is None:
693         argv = sys.argv
695     module_filter = None
696     test_filter = None
697     VERBOSE = 1
698     LOOP = 0
699     GUI = 0
700     TRACE = 0
701     REFCOUNT = 0
702     debug = 0 # Don't collect test results; simply let tests crash
703     debugger = 0
704     build = 0
705     build_inplace = 0
706     gcthresh = None
707     gcdebug = 0
708     gcflags = []
709     level = 1
710     libdir = '.'
711     progress = 0
712     timesfn = None
713     timetests = 0
714     keepStaleBytecode = 0
715     functional = 0
716     test_dir = None
718     try:
719         opts, args = getopt.getopt(argv[1:], "a:bBcdDfg:G:hLmprtTuv",
720                                    ["all", "help", "libdir=", "times=",
721                                     "keepbytecode", "dir=", "build"])
722     except getopt.error, msg:
723         print msg
724         print "Try `python %s -h' for more information." % argv[0]
725         sys.exit(2)
727     for k, v in opts:
728         if k == "-a":
729             level = int(v)
730         elif k == "--all":
731             level = 0
732             os.environ["COMPLAIN_IF_TESTS_MISSED"]='1'
733         elif k in ("-b", "--build"):
734             build = 1
735         elif k == "-B":
736              build = build_inplace = 1
737         elif k == "-c":
738             # make sure you have a recent version of pychecker
739             if not os.environ.get("PYCHECKER"):
740                 os.environ["PYCHECKER"] = "-q"
741             import pychecker.checker
742         elif k == "-d":
743             debug = 1
744         elif k == "-D":
745             debug = 1
746             debugger = 1
747         elif k == "-f":
748             functional = 1
749         elif k in ("-h", "--help"):
750             print __doc__
751             sys.exit(0)
752         elif k == "-g":
753             gcthresh = int(v)
754         elif k == "-G":
755             if not v.startswith("DEBUG_"):
756                 print "-G argument must be DEBUG_ flag, not", repr(v)
757                 sys.exit(1)
758             gcflags.append(v)
759         elif k == '--keepbytecode':
760             keepStaleBytecode = 1
761         elif k == '--libdir':
762             libdir = v
763         elif k == "-L":
764             LOOP = 1
765         elif k == "-m":
766             GUI = "minimal"
767         elif k == "-p":
768             progress = 1
769         elif k == "-r":
770             if hasattr(sys, "gettotalrefcount"):
771                 REFCOUNT = 1
772             else:
773                 print "-r ignored, because it needs a debug build of Python"
774         elif k == "-T":
775             TRACE = 1
776         elif k == "-t":
777             if not timetests:
778                 timetests = 50
779         elif k == "-u":
780             GUI = 1
781         elif k == "-v":
782             VERBOSE += 1
783         elif k == "--times":
784             try:
785                 timetests = int(v)
786             except ValueError:
787                 # must be a filename to write
788                 timesfn = v
789         elif k == '--dir':
790             test_dir = v
792     if gcthresh is not None:
793         if gcthresh == 0:
794             gc.disable()
795             print "gc disabled"
796         else:
797             gc.set_threshold(gcthresh)
798             print "gc threshold:", gc.get_threshold()
800     if gcflags:
801         val = 0
802         for flag in gcflags:
803             v = getattr(gc, flag, None)
804             if v is None:
805                 print "Unknown gc flag", repr(flag)
806                 print gc.set_debug.__doc__
807                 sys.exit(1)
808             val |= v
809         gcdebug |= v
811     if gcdebug:
812         gc.set_debug(gcdebug)
814     if build:
815         # Python 2.3 is more sane in its non -q output
816         if sys.hexversion >= 0x02030000:
817             qflag = ""
818         else:
819             qflag = "-q"
820         cmd = sys.executable + " setup.py " + qflag + " build"
821         if build_inplace:
822             cmd += "_ext -i"
823         if VERBOSE:
824             print cmd
825         sts = os.system(cmd)
826         if sts:
827             print "Build failed", hex(sts)
828             sys.exit(1)
830     if VERBOSE:
831         kind = functional and "functional" or "unit"
832         if level == 0:
833             print "Running %s tests at all levels" % kind
834         else:
835             print "Running %s tests at level %d" % (kind, level)
837     # XXX We want to change *visible* warnings into errors.  The next
838     # line changes all warnings into errors, including warnings we
839     # normally never see.  In particular, test_datetime does some
840     # short-integer arithmetic that overflows to long ints, and, by
841     # default, Python doesn't display the overflow warning that can
842     # be enabled when this happens.  The next line turns that into an
843     # error instead.  Guido suggests that a better to get what we're
844     # after is to replace warnings.showwarning() with our own thing
845     # that raises an error.
846 ##    warnings.filterwarnings("error")
847     warnings.filterwarnings("ignore", module="logging")
849     if args:
850         if len(args) > 1:
851             test_filter = args[1]
852         module_filter = args[0]
853     try:
854         if TRACE:
855             # if the trace module is used, then we don't exit with
856             # status if on a false return value from main.
857             coverdir = os.path.join(os.getcwd(), "coverage")
858             import trace
859             ignoremods = ["os", "posixpath", "stat"]
860             tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix],
861                                  ignoremods=ignoremods,
862                                  trace=0, count=1)
864             tracer.runctx("main(module_filter, test_filter, libdir)",
865                           globals=globals(), locals=vars())
866             r = tracer.results()
867             path = "/tmp/trace.%s" % os.getpid()
868             import cPickle
869             f = open(path, "wb")
870             cPickle.dump(r, f)
871             f.close()
872             print path
873             r.write_results(show_missing=1, summary=1, coverdir=coverdir)
874         else:
875             bad = main(module_filter, test_filter, libdir)
876             if bad:
877                 sys.exit(1)
878     except ImportError, err:
879         print err
880         print sys.path
881         raise
884 if __name__ == "__main__":
885     process_args()