c54375307b4a3a055751d0708a1ec52efc0b3009
1 #! /usr/bin/env python
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 # docstrings for tests don't override test-descriptions:
188 self.descriptions = False
189 if progress and verbosity == 1:
190 self.dots = 0
191 self._progressWithNames = 1
192 self._lastWidth = 0
193 self._maxWidth = 80
194 try:
195 import curses
196 except ImportError:
197 pass
198 else:
199 curses.setupterm()
200 self._maxWidth = curses.tigetnum('cols')
201 self._maxWidth -= len("xxxx/xxxx (xxx.x%): ") + 1
203 def stopTest(self, test):
204 self._testtimes[test] = time.time() - self._testtimes[test]
205 if gc.garbage:
206 print "The following test left garbage:"
207 print test
208 print gc.garbage
209 # eat the garbage here, so that the garbage isn't
210 # printed for every subsequent test.
211 gc.garbage[:] = []
213 # Did the test leave any new threads behind?
214 new_threads = [t for t in threading.enumerate()
215 if (t.isAlive()
216 and
217 t not in self._threads)]
218 if new_threads:
219 print "The following test left new threads behind:"
220 print test
221 print "New thread(s):", new_threads
223 def print_times(self, stream, count=None):
224 results = self._testtimes.items()
225 results.sort(lambda x, y: cmp(y[1], x[1]))
226 if count:
227 n = min(count, len(results))
228 if n:
229 print >>stream, "Top %d longest tests:" % n
230 else:
231 n = len(results)
232 if not n:
233 return
234 for i in range(n):
235 print >>stream, "%6dms" % int(results[i][1] * 1000), results[i][0]
237 def _print_traceback(self, msg, err, test, errlist):
238 if self.showAll or self.dots or self._progress:
239 self.stream.writeln("\n")
240 self._lastWidth = 0
242 tb = "".join(traceback.format_exception(*err))
243 self.stream.writeln(msg)
244 self.stream.writeln(tb)
245 errlist.append((test, tb))
247 def startTest(self, test):
248 if self._progress:
249 self.stream.write("\r%4d" % (self.testsRun + 1))
250 if self._count:
251 self.stream.write("/%d (%5.1f%%)" % (self._count,
252 (self.testsRun + 1) * 100.0 / self._count))
253 if self.showAll:
254 self.stream.write(": ")
255 elif self._progressWithNames:
256 # XXX will break with multibyte strings
257 name = self.getShortDescription(test)
258 width = len(name)
259 if width < self._lastWidth:
260 name += " " * (self._lastWidth - width)
261 self.stream.write(": %s" % name)
262 self._lastWidth = width
263 self.stream.flush()
264 self._threads = threading.enumerate()
265 self.__super_startTest(test)
266 self._testtimes[test] = time.time()
268 def getShortDescription(self, test):
269 s = self.getDescription(test)
270 if len(s) > self._maxWidth:
271 pos = s.find(" (")
272 if pos >= 0:
273 w = self._maxWidth - (pos + 5)
274 if w < 1:
275 # first portion (test method name) is too long
276 s = s[:self._maxWidth-3] + "..."
277 else:
278 pre = s[:pos+2]
279 post = s[-w:]
280 s = "%s...%s" % (pre, post)
281 return s[:self._maxWidth]
283 def addError(self, test, err):
284 if self._progress:
285 self.stream.write("\r")
286 if self._debug:
287 raise err[0], err[1], err[2]
288 self._print_traceback("Error in test %s" % test, err,
289 test, self.errors)
291 def addFailure(self, test, err):
292 if self._progress:
293 self.stream.write("\r")
294 if self._debug:
295 raise err[0], err[1], err[2]
296 self._print_traceback("Failure in test %s" % test, err,
297 test, self.failures)
299 def printErrors(self):
300 if self._progress and not (self.dots or self.showAll):
301 self.stream.writeln()
302 self.__super_printErrors()
304 def printErrorList(self, flavor, errors):
305 for test, err in errors:
306 self.stream.writeln(self.separator1)
307 self.stream.writeln("%s: %s" % (flavor, self.getDescription(test)))
308 self.stream.writeln(self.separator2)
309 self.stream.writeln(err)
312 class ImmediateTestRunner(unittest.TextTestRunner):
314 __super_init = unittest.TextTestRunner.__init__
316 def __init__(self, **kwarg):
317 debug = kwarg.get("debug")
318 if debug is not None:
319 del kwarg["debug"]
320 progress = kwarg.get("progress")
321 if progress is not None:
322 del kwarg["progress"]
323 self.__super_init(**kwarg)
324 self._debug = debug
325 self._progress = progress
327 def _makeResult(self):
328 return ImmediateTestResult(self.stream, self.descriptions,
329 self.verbosity, debug=self._debug,
330 count=self._count, progress=self._progress)
332 def run(self, test):
333 self._count = test.countTestCases()
334 return unittest.TextTestRunner.run(self, test)
336 # setup list of directories to put on the path
337 class PathInit:
338 def __init__(self, build, build_inplace, libdir=None):
339 self.inplace = None
340 # Figure out if we should test in-place or test in-build. If the -b
341 # or -B option was given, test in the place we were told to build in.
342 # Otherwise, we'll look for a build directory and if we find one,
343 # we'll test there, otherwise we'll test in-place.
344 if build:
345 self.inplace = build_inplace
346 if self.inplace is None:
347 # Need to figure it out
348 if os.path.isdir(os.path.join("build", "lib.%s" % PLAT_SPEC)):
349 self.inplace = 0
350 else:
351 self.inplace = 1
352 # Calculate which directories we're going to add to sys.path, and cd
353 # to the appropriate working directory
354 org_cwd = os.getcwd()
355 if self.inplace:
356 self.libdir = "src"
357 else:
358 self.libdir = "lib.%s" % PLAT_SPEC
359 os.chdir("build")
360 # Hack sys.path
361 self.cwd = os.getcwd()
362 sys.path.insert(0, os.path.join(self.cwd, self.libdir))
363 # Hack again for external products.
364 global functional
365 kind = functional and "functional" or "unit"
366 if libdir:
367 extra = os.path.join(org_cwd, libdir)
368 print "Running %s tests from %s" % (kind, extra)
369 self.libdir = extra
370 sys.path.insert(0, extra)
371 else:
372 print "Running %s tests from %s" % (kind, self.cwd)
373 # Make sure functional tests find ftesting.zcml
374 if functional:
375 config_file = 'ftesting.zcml'
376 if not self.inplace:
377 # We chdired into build, so ftesting.zcml is in the
378 # parent directory
379 config_file = os.path.join('..', 'ftesting.zcml')
380 print "Parsing %s" % config_file
381 from zope.testing.functional import FunctionalTestSetup
382 FunctionalTestSetup(config_file)
384 def match(rx, s):
385 if not rx:
386 return 1
387 if rx[0] == "!":
388 return re.search(rx[1:], s) is None
389 else:
390 return re.search(rx, s) is not None
392 class TestFileFinder:
393 def __init__(self, prefix):
394 self.files = []
395 self._plen = len(prefix)
396 if not prefix.endswith(os.sep):
397 self._plen += 1
398 global functional
399 if functional:
400 self.dirname = "ftest"
401 else:
402 self.dirname = "test"
404 def visit(self, rx, dir, files):
405 if os.path.split(dir)[1] != self.dirname:
406 return
407 # ignore tests that aren't in packages
408 if not "__init__.py" in files:
409 if not files or files == ["CVS"]:
410 return
411 print "not a package", dir
412 return
414 # Put matching files in matches. If matches is non-empty,
415 # then make sure that the package is importable.
416 matches = []
417 for file in files:
418 if file.startswith('test') and os.path.splitext(file)[-1] == '.py':
419 path = os.path.join(dir, file)
420 if match(rx, path):
421 matches.append(path)
423 # ignore tests when the package can't be imported, possibly due to
424 # dependency failures.
425 pkg = dir[self._plen:].replace(os.sep, '.')
426 try:
427 __import__(pkg)
428 # We specifically do not want to catch ImportError since that's useful
429 # information to know when running the tests.
430 except RuntimeError, e:
431 if VERBOSE:
432 print "skipping %s because: %s" % (pkg, e)
433 return
434 else:
435 self.files.extend(matches)
437 def module_from_path(self, path):
438 """Return the Python package name indicated by the filesystem path."""
439 assert path.endswith(".py")
440 path = path[self._plen:-3]
441 mod = path.replace(os.sep, ".")
442 return mod
444 def walk_with_symlinks(top, func, arg):
445 """Like os.path.walk, but follows symlinks on POSIX systems.
447 This could theoreticaly result in an infinite loop, if you create symlink
448 cycles in your Zope sandbox, so don't do that.
449 """
450 try:
451 names = os.listdir(top)
452 except os.error:
453 return
454 func(arg, top, names)
455 exceptions = ('.', '..')
456 for name in names:
457 if name not in exceptions:
458 name = os.path.join(top, name)
459 if os.path.isdir(name):
460 walk_with_symlinks(name, func, arg)
463 def check_test_dir():
464 global test_dir
465 if test_dir and not os.path.exists(test_dir):
466 d = pathinit.libdir
467 d = os.path.join(d, test_dir)
468 if os.path.exists(d):
469 if not os.path.isdir(d):
470 raise ValueError(
471 "%s does not exist and %s is not a directory"
472 % (test_dir, d)
473 )
474 test_dir = d
475 else:
476 raise ValueError("%s does not exist!" % test_dir)
479 def find_tests(rx):
480 global finder
481 finder = TestFileFinder(pathinit.libdir)
483 check_test_dir()
484 walkdir = test_dir or pathinit.libdir
485 walk_with_symlinks(walkdir, finder.visit, rx)
486 return finder.files
488 def package_import(modname):
489 mod = __import__(modname)
490 for part in modname.split(".")[1:]:
491 mod = getattr(mod, part)
492 return mod
494 def get_suite(file):
495 modname = finder.module_from_path(file)
496 try:
497 mod = package_import(modname)
498 except ImportError, err:
499 # print traceback
500 print "Error importing %s\n%s" % (modname, err)
501 traceback.print_exc()
502 if debug:
503 raise
504 return None
505 try:
506 suite_func = mod.test_suite
507 except AttributeError:
508 print "No test_suite() in %s" % file
509 return None
510 return suite_func()
512 def filter_testcases(s, rx):
513 new = unittest.TestSuite()
514 for test in s._tests:
515 # See if the levels match
516 dolevel = (level == 0) or level >= getattr(test, "level", 0)
517 if not dolevel:
518 continue
519 if isinstance(test, unittest.TestCase):
520 name = test.id() # Full test name: package.module.class.method
521 name = name[1 + name.rfind("."):] # extract method name
522 if not rx or match(rx, name):
523 new.addTest(test)
524 else:
525 filtered = filter_testcases(test, rx)
526 if filtered:
527 new.addTest(filtered)
528 return new
530 def gui_runner(files, test_filter):
531 if build_inplace:
532 utildir = os.path.join(os.getcwd(), "utilities")
533 else:
534 utildir = os.path.join(os.getcwd(), "..", "utilities")
535 sys.path.append(utildir)
536 import unittestgui
537 suites = []
538 for file in files:
539 suites.append(finder.module_from_path(file) + ".test_suite")
541 suites = ", ".join(suites)
542 minimal = (GUI == "minimal")
543 # unittestgui apparently doesn't take the minimal flag anymore
544 unittestgui.main(suites)
546 class TrackRefs:
547 """Object to track reference counts across test runs."""
549 def __init__(self):
550 self.type2count = {}
551 self.type2all = {}
553 def update(self):
554 obs = sys.getobjects(0)
555 type2count = {}
556 type2all = {}
557 for o in obs:
558 all = sys.getrefcount(o)
559 t = type(o)
560 if t in type2count:
561 type2count[t] += 1
562 type2all[t] += all
563 else:
564 type2count[t] = 1
565 type2all[t] = all
567 ct = [(type2count[t] - self.type2count.get(t, 0),
568 type2all[t] - self.type2all.get(t, 0),
569 t)
570 for t in type2count.iterkeys()]
571 ct.sort()
572 ct.reverse()
573 for delta1, delta2, t in ct:
574 if delta1 or delta2:
575 print "%-55s %8d %8d" % (t, delta1, delta2)
577 self.type2count = type2count
578 self.type2all = type2all
580 def runner(files, test_filter, debug):
581 runner = ImmediateTestRunner(verbosity=VERBOSE, debug=debug,
582 progress=progress)
583 suite = unittest.TestSuite()
584 for file in files:
585 s = get_suite(file)
586 # See if the levels match
587 dolevel = (level == 0) or level >= getattr(s, "level", 0)
588 if s is not None and dolevel:
589 s = filter_testcases(s, test_filter)
590 suite.addTest(s)
591 try:
592 r = runner.run(suite)
593 if timesfn:
594 r.print_times(open(timesfn, "w"))
595 if VERBOSE:
596 print "Wrote timing data to", timesfn
597 if timetests:
598 r.print_times(sys.stdout, timetests)
599 except:
600 if debugger:
601 print "%s:" % (sys.exc_info()[0], )
602 print sys.exc_info()[1]
603 pdb.post_mortem(sys.exc_info()[2])
604 else:
605 raise
607 def remove_stale_bytecode(arg, dirname, names):
608 names = map(os.path.normcase, names)
609 for name in names:
610 if name.endswith(".pyc") or name.endswith(".pyo"):
611 srcname = name[:-1]
612 if srcname not in names:
613 fullname = os.path.join(dirname, name)
614 print "Removing stale bytecode file", fullname
615 os.unlink(fullname)
617 def main(module_filter, test_filter, libdir):
618 if not keepStaleBytecode:
619 os.path.walk(os.curdir, remove_stale_bytecode, None)
621 # Get the log.ini file from the current directory instead of possibly
622 # buried in the build directory. XXX This isn't perfect because if
623 # log.ini specifies a log file, it'll be relative to the build directory.
624 # Hmm...
625 logini = os.path.abspath("log.ini")
627 from setup import check_manifest
628 check_manifest()
630 # Initialize the path and cwd
631 global pathinit
632 pathinit = PathInit(build, build_inplace, libdir)
634 # No logging module in py 2.1
635 # # Initialize the logging module.
637 # import logging.config
638 # logging.basicConfig()
640 # level = os.getenv("LOGGING")
641 # if level:
642 # level = int(level)
643 # else:
644 # level = logging.CRITICAL
645 # logging.root.setLevel(level)
647 # if os.path.exists(logini):
648 # logging.config.fileConfig(logini)
650 files = find_tests(module_filter)
651 files.sort()
653 if GUI:
654 gui_runner(files, test_filter)
655 elif LOOP:
656 if REFCOUNT:
657 rc = sys.gettotalrefcount()
658 track = TrackRefs()
659 while 1:
660 runner(files, test_filter, debug)
661 gc.collect()
662 if gc.garbage:
663 print "GARBAGE:", len(gc.garbage), gc.garbage
664 return
665 if REFCOUNT:
666 prev = rc
667 rc = sys.gettotalrefcount()
668 print "totalrefcount=%-8d change=%-6d" % (rc, rc - prev)
669 track.update()
670 else:
671 runner(files, test_filter, debug)
674 def process_args(argv=None):
675 import getopt
676 global module_filter
677 global test_filter
678 global VERBOSE
679 global LOOP
680 global GUI
681 global TRACE
682 global REFCOUNT
683 global debug
684 global debugger
685 global build
686 global level
687 global libdir
688 global timesfn
689 global timetests
690 global progress
691 global build_inplace
692 global keepStaleBytecode
693 global functional
694 global test_dir
696 if argv is None:
697 argv = sys.argv
699 module_filter = None
700 test_filter = None
701 VERBOSE = 2
702 LOOP = 0
703 GUI = 0
704 TRACE = 0
705 REFCOUNT = 0
706 debug = 0 # Don't collect test results; simply let tests crash
707 debugger = 0
708 build = 0
709 build_inplace = 0
710 gcthresh = None
711 gcdebug = 0
712 gcflags = []
713 level = 1
714 libdir = '.'
715 progress = 0
716 timesfn = None
717 timetests = 0
718 keepStaleBytecode = 0
719 functional = 0
720 test_dir = None
722 try:
723 opts, args = getopt.getopt(argv[1:], "a:bBcdDfg:G:hLmprtTuv",
724 ["all", "help", "libdir=", "times=",
725 "keepbytecode", "dir=", "build"])
726 except getopt.error, msg:
727 print msg
728 print "Try `python %s -h' for more information." % argv[0]
729 sys.exit(2)
731 for k, v in opts:
732 if k == "-a":
733 level = int(v)
734 elif k == "--all":
735 level = 0
736 os.environ["COMPLAIN_IF_TESTS_MISSED"]='1'
737 elif k in ("-b", "--build"):
738 build = 1
739 elif k == "-B":
740 build = build_inplace = 1
741 elif k == "-c":
742 # make sure you have a recent version of pychecker
743 if not os.environ.get("PYCHECKER"):
744 os.environ["PYCHECKER"] = "-q"
745 import pychecker.checker
746 elif k == "-d":
747 debug = 1
748 elif k == "-D":
749 debug = 1
750 debugger = 1
751 elif k == "-f":
752 functional = 1
753 elif k in ("-h", "--help"):
754 print __doc__
755 sys.exit(0)
756 elif k == "-g":
757 gcthresh = int(v)
758 elif k == "-G":
759 if not v.startswith("DEBUG_"):
760 print "-G argument must be DEBUG_ flag, not", repr(v)
761 sys.exit(1)
762 gcflags.append(v)
763 elif k == '--keepbytecode':
764 keepStaleBytecode = 1
765 elif k == '--libdir':
766 libdir = v
767 elif k == "-L":
768 LOOP = 1
769 elif k == "-m":
770 GUI = "minimal"
771 elif k == "-p":
772 progress = 1
773 elif k == "-r":
774 if hasattr(sys, "gettotalrefcount"):
775 REFCOUNT = 1
776 else:
777 print "-r ignored, because it needs a debug build of Python"
778 elif k == "-T":
779 TRACE = 1
780 elif k == "-t":
781 if not timetests:
782 timetests = 50
783 elif k == "-u":
784 GUI = 1
785 elif k == "-v":
786 VERBOSE += 1
787 elif k == "--times":
788 try:
789 timetests = int(v)
790 except ValueError:
791 # must be a filename to write
792 timesfn = v
793 elif k == '--dir':
794 test_dir = v
796 if gcthresh is not None:
797 if gcthresh == 0:
798 gc.disable()
799 print "gc disabled"
800 else:
801 gc.set_threshold(gcthresh)
802 print "gc threshold:", gc.get_threshold()
804 if gcflags:
805 val = 0
806 for flag in gcflags:
807 v = getattr(gc, flag, None)
808 if v is None:
809 print "Unknown gc flag", repr(flag)
810 print gc.set_debug.__doc__
811 sys.exit(1)
812 val |= v
813 gcdebug |= v
815 if gcdebug:
816 gc.set_debug(gcdebug)
818 if build:
819 # Python 2.3 is more sane in its non -q output
820 if sys.hexversion >= 0x02030000:
821 qflag = ""
822 else:
823 qflag = "-q"
824 cmd = sys.executable + " setup.py " + qflag + " build"
825 if build_inplace:
826 cmd += "_ext -i"
827 if VERBOSE:
828 print cmd
829 sts = os.system(cmd)
830 if sts:
831 print "Build failed", hex(sts)
832 sys.exit(1)
834 if VERBOSE:
835 kind = functional and "functional" or "unit"
836 if level == 0:
837 print "Running %s tests at all levels" % kind
838 else:
839 print "Running %s tests at level %d" % (kind, level)
841 # XXX We want to change *visible* warnings into errors. The next
842 # line changes all warnings into errors, including warnings we
843 # normally never see. In particular, test_datetime does some
844 # short-integer arithmetic that overflows to long ints, and, by
845 # default, Python doesn't display the overflow warning that can
846 # be enabled when this happens. The next line turns that into an
847 # error instead. Guido suggests that a better to get what we're
848 # after is to replace warnings.showwarning() with our own thing
849 # that raises an error.
850 ## warnings.filterwarnings("error")
851 warnings.filterwarnings("ignore", module="logging")
853 if args:
854 if len(args) > 1:
855 test_filter = args[1]
856 module_filter = args[0]
857 try:
858 if TRACE:
859 # if the trace module is used, then we don't exit with
860 # status if on a false return value from main.
861 coverdir = os.path.join(os.getcwd(), "coverage")
862 import trace
863 ignoremods = ["os", "posixpath", "stat"]
864 tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix],
865 ignoremods=ignoremods,
866 trace=0, count=1)
868 tracer.runctx("main(module_filter, test_filter, libdir)",
869 globals=globals(), locals=vars())
870 r = tracer.results()
871 path = "/tmp/trace.%s" % os.getpid()
872 import cPickle
873 f = open(path, "wb")
874 cPickle.dump(r, f)
875 f.close()
876 print path
877 r.write_results(show_missing=1, summary=1, coverdir=coverdir)
878 else:
879 bad = main(module_filter, test_filter, libdir)
880 if bad:
881 sys.exit(1)
882 except ImportError, err:
883 print err
884 print sys.path
885 raise
888 if __name__ == "__main__":
889 process_args()