bdd9b7a70a744a0231bac4d88014b74654171a1d
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=False,
180 count=None, progress=False):
181 self.__super_init(stream, descriptions, verbosity)
182 self._debug = debug
183 self._progress = progress
184 self._progressWithNames = False
185 self._count = count
186 self._testtimes = {}
187 if progress and verbosity == 1:
188 self.dots = False
189 self._progressWithNames = True
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 = False
348 else:
349 self.inplace = True
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 True
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 # Initialize the logging module.
630 import logging.config
631 logging.basicConfig()
633 level = os.getenv("LOGGING")
634 if level:
635 level = int(level)
636 else:
637 level = logging.CRITICAL
638 logging.root.setLevel(level)
640 if os.path.exists(logini):
641 logging.config.fileConfig(logini)
643 files = find_tests(module_filter)
644 files.sort()
646 if GUI:
647 gui_runner(files, test_filter)
648 elif LOOP:
649 if REFCOUNT:
650 rc = sys.gettotalrefcount()
651 track = TrackRefs()
652 while True:
653 runner(files, test_filter, debug)
654 gc.collect()
655 if gc.garbage:
656 print "GARBAGE:", len(gc.garbage), gc.garbage
657 return
658 if REFCOUNT:
659 prev = rc
660 rc = sys.gettotalrefcount()
661 print "totalrefcount=%-8d change=%-6d" % (rc, rc - prev)
662 track.update()
663 else:
664 runner(files, test_filter, debug)
667 def process_args(argv=None):
668 import getopt
669 global module_filter
670 global test_filter
671 global VERBOSE
672 global LOOP
673 global GUI
674 global TRACE
675 global REFCOUNT
676 global debug
677 global debugger
678 global build
679 global level
680 global libdir
681 global timesfn
682 global timetests
683 global progress
684 global build_inplace
685 global keepStaleBytecode
686 global functional
687 global test_dir
689 if argv is None:
690 argv = sys.argv
692 module_filter = None
693 test_filter = None
694 VERBOSE = 1
695 LOOP = False
696 GUI = False
697 TRACE = False
698 REFCOUNT = False
699 debug = False # Don't collect test results; simply let tests crash
700 debugger = False
701 build = False
702 build_inplace = False
703 gcthresh = None
704 gcdebug = 0
705 gcflags = []
706 level = 1
707 libdir = '.'
708 progress = False
709 timesfn = None
710 timetests = 0
711 keepStaleBytecode = 0
712 functional = False
713 test_dir = None
715 try:
716 opts, args = getopt.getopt(argv[1:], "a:bBcdDfg:G:hLmprtTuv",
717 ["all", "help", "libdir=", "times=",
718 "keepbytecode", "dir=", "build"])
719 except getopt.error, msg:
720 print msg
721 print "Try `python %s -h' for more information." % argv[0]
722 sys.exit(2)
724 for k, v in opts:
725 if k == "-a":
726 level = int(v)
727 elif k == "--all":
728 level = 0
729 os.environ["COMPLAIN_IF_TESTS_MISSED"]='1'
730 elif k in ("-b", "--build"):
731 build = True
732 elif k == "-B":
733 build = build_inplace = True
734 elif k == "-c":
735 # make sure you have a recent version of pychecker
736 if not os.environ.get("PYCHECKER"):
737 os.environ["PYCHECKER"] = "-q"
738 import pychecker.checker
739 elif k == "-d":
740 debug = True
741 elif k == "-D":
742 debug = True
743 debugger = True
744 elif k == "-f":
745 functional = True
746 elif k in ("-h", "--help"):
747 print __doc__
748 sys.exit(0)
749 elif k == "-g":
750 gcthresh = int(v)
751 elif k == "-G":
752 if not v.startswith("DEBUG_"):
753 print "-G argument must be DEBUG_ flag, not", repr(v)
754 sys.exit(1)
755 gcflags.append(v)
756 elif k == '--keepbytecode':
757 keepStaleBytecode = 1
758 elif k == '--libdir':
759 libdir = v
760 elif k == "-L":
761 LOOP = 1
762 elif k == "-m":
763 GUI = "minimal"
764 elif k == "-p":
765 progress = True
766 elif k == "-r":
767 if hasattr(sys, "gettotalrefcount"):
768 REFCOUNT = True
769 else:
770 print "-r ignored, because it needs a debug build of Python"
771 elif k == "-T":
772 TRACE = True
773 elif k == "-t":
774 if not timetests:
775 timetests = 50
776 elif k == "-u":
777 GUI = 1
778 elif k == "-v":
779 VERBOSE += 1
780 elif k == "--times":
781 try:
782 timetests = int(v)
783 except ValueError:
784 # must be a filename to write
785 timesfn = v
786 elif k == '--dir':
787 test_dir = v
789 if sys.version_info < ( 2,2,3 ):
790 print """\
791 ERROR: Your python version is not supported by Zope3.
792 Zope3 needs either Python2.3 or Python2.2.3 or greater.
793 In particular, Zope3 on Python2.2.2 is a recipe for
794 pain. You are running:""" + sys.version
795 sys.exit(1)
797 if gcthresh is not None:
798 if gcthresh == 0:
799 gc.disable()
800 print "gc disabled"
801 else:
802 gc.set_threshold(gcthresh)
803 print "gc threshold:", gc.get_threshold()
805 if gcflags:
806 val = 0
807 for flag in gcflags:
808 v = getattr(gc, flag, None)
809 if v is None:
810 print "Unknown gc flag", repr(flag)
811 print gc.set_debug.__doc__
812 sys.exit(1)
813 val |= v
814 gcdebug |= v
816 if gcdebug:
817 gc.set_debug(gcdebug)
819 if build:
820 # Python 2.3 is more sane in its non -q output
821 if sys.hexversion >= 0x02030000:
822 qflag = ""
823 else:
824 qflag = "-q"
825 cmd = sys.executable + " setup.py " + qflag + " build"
826 if build_inplace:
827 cmd += "_ext -i"
828 if VERBOSE:
829 print cmd
830 sts = os.system(cmd)
831 if sts:
832 print "Build failed", hex(sts)
833 sys.exit(1)
835 if VERBOSE:
836 kind = functional and "functional" or "unit"
837 if level == 0:
838 print "Running %s tests at all levels" % kind
839 else:
840 print "Running %s tests at level %d" % (kind, level)
842 # XXX We want to change *visible* warnings into errors. The next
843 # line changes all warnings into errors, including warnings we
844 # normally never see. In particular, test_datetime does some
845 # short-integer arithmetic that overflows to long ints, and, by
846 # default, Python doesn't display the overflow warning that can
847 # be enabled when this happens. The next line turns that into an
848 # error instead. Guido suggests that a better to get what we're
849 # after is to replace warnings.showwarning() with our own thing
850 # that raises an error.
851 ## warnings.filterwarnings("error")
852 warnings.filterwarnings("ignore", module="logging")
854 if args:
855 if len(args) > 1:
856 test_filter = args[1]
857 module_filter = args[0]
858 try:
859 if TRACE:
860 # if the trace module is used, then we don't exit with
861 # status if on a false return value from main.
862 coverdir = os.path.join(os.getcwd(), "coverage")
863 import trace
864 ignoremods = ["os", "posixpath", "stat"]
865 tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix],
866 ignoremods=ignoremods,
867 trace=False, count=True)
869 tracer.runctx("main(module_filter, test_filter, libdir)",
870 globals=globals(), locals=vars())
871 r = tracer.results()
872 path = "/tmp/trace.%s" % os.getpid()
873 import cPickle
874 f = open(path, "wb")
875 cPickle.dump(r, f)
876 f.close()
877 print path
878 r.write_results(show_missing=True, summary=True, coverdir=coverdir)
879 else:
880 bad = main(module_filter, test_filter, libdir)
881 if bad:
882 sys.exit(1)
883 except ImportError, err:
884 print err
885 print sys.path
886 raise
889 if __name__ == "__main__":
890 process_args()