[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] Added Zope's test.py, and a unit test to sxp. This test itself isn't very
# HG changeset patch # User emellor@ewan # Node ID cd228621e1fd4bc0f404b80e6c618ae02d0c1173 # Parent bd37123974b293eafe307a36f1a9669c8ec2221b Added Zope's test.py, and a unit test to sxp. This test itself isn't very exciting, but it's there to encourage the creation of more interesting ones. A test target has been added to the main Makefile, and the one in tools/python. Signed-off-by: Ewan Mellor <ewan@xxxxxxxxxxxxx> diff -r bd37123974b2 -r cd228621e1fd Makefile --- a/Makefile Thu Oct 6 10:12:55 2005 +++ b/Makefile Thu Oct 6 14:12:31 2005 @@ -35,6 +35,12 @@ $(MAKE) -C xen build $(MAKE) -C tools build $(MAKE) -C docs build + +# The test target is for unit tests that can run without an installation. Of +# course, many tests require a machine running Xen itself, and these are +# handled elsewhere. +test: + $(MAKE) -C tools/python test # build and install everything into local dist directory dist: DESTDIR=$(DISTDIR)/install diff -r bd37123974b2 -r cd228621e1fd tools/python/Makefile --- a/tools/python/Makefile Thu Oct 6 10:12:55 2005 +++ b/tools/python/Makefile Thu Oct 6 14:12:31 2005 @@ -15,5 +15,8 @@ CFLAGS="$(CFLAGS)" python setup.py install --root="$(DESTDIR)" endif +test: + export LD_LIBRARY_PATH=$$(readlink -f ../libxc):$$(readlink -f ../xenstore); python test.py -b -u + clean: rm -rf build *.pyc *.pyo *.o *.a *~ diff -r bd37123974b2 -r cd228621e1fd tools/python/setup.py --- a/tools/python/setup.py Thu Oct 6 10:12:55 2005 +++ b/tools/python/setup.py Thu Oct 6 14:12:31 2005 @@ -42,7 +42,9 @@ 'xen.xend.xenstore', 'xen.xm', 'xen.web', - 'xen.sv' + 'xen.sv', + + 'xen.xend.tests' ], ext_package = "xen.lowlevel", ext_modules = [ xc, xs ] diff -r bd37123974b2 -r cd228621e1fd tools/python/README --- /dev/null Thu Oct 6 10:12:55 2005 +++ b/tools/python/README Thu Oct 6 14:12:31 2005 @@ -0,0 +1,3 @@ +The file test.py here is from the Zope project, and is Copyright (c) 2001, +2002 Zope Corporation and Contributors. This file is released under the Zope +Public License, version 2.0, a copy of which is in the file ZPL-2.0. diff -r bd37123974b2 -r cd228621e1fd tools/python/ZPL-2.0 --- /dev/null Thu Oct 6 10:12:55 2005 +++ b/tools/python/ZPL-2.0 Thu Oct 6 14:12:31 2005 @@ -0,0 +1,59 @@ +Zope Public License (ZPL) Version 2.0 +----------------------------------------------- + +This software is Copyright (c) Zope Corporation (tm) and +Contributors. All rights reserved. + +This license has been certified as open source. It has also +been designated as GPL compatible by the Free Software +Foundation (FSF). + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the +following conditions are met: + +1. Redistributions in source code must retain the above + copyright notice, this list of conditions, and the following + disclaimer. + +2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions, and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + +3. The name Zope Corporation (tm) must not be used to + endorse or promote products derived from this software + without prior written permission from Zope Corporation. + +4. The right to distribute this software or to use it for + any purpose does not give you the right to use Servicemarks + (sm) or Trademarks (tm) of Zope Corporation. Use of them is + covered in a separate agreement (see + http://www.zope.com/Marks). + +5. If any files are modified, you must cause the modified + files to carry prominent notices stating that you changed + the files and the date of any change. + +Disclaimer + + THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS'' + AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT + NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + + +This software consists of contributions made by Zope +Corporation and many individuals on behalf of Zope +Corporation. Specific attributions are listed in the +accompanying credits file. \ No newline at end of file diff -r bd37123974b2 -r cd228621e1fd tools/python/test.py --- /dev/null Thu Oct 6 10:12:55 2005 +++ b/tools/python/test.py Thu Oct 6 14:12:31 2005 @@ -0,0 +1,1094 @@ +#! /usr/bin/env python2.3 +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +""" +test.py [-abBcdDfFgGhklLmMPprstTuUv] [modfilter [testfilter]] + +Find and run tests written using the unittest module. + +The test runner searches for Python modules that contain test suites. +It collects those suites, and runs the tests. There are many options +for controlling how the tests are run. There are options for using +the debugger, reporting code coverage, and checking for refcount problems. + +The test runner uses the following rules for finding tests to run. It +searches for packages and modules that contain "tests" as a component +of the name, e.g. "frob.tests.nitz" matches this rule because tests is +a sub-package of frob. Within each "tests" package, it looks for +modules that begin with the name "test." For each test module, it +imports the module and calls the module's test_suite() function, which must +return a unittest TestSuite object. + +Options can be specified as command line arguments (see below). However, +options may also be specified in a file named 'test.config', a Python +script which, if found, will be executed before the command line +arguments are processed. + +The test.config script should specify options by setting zero or more of the +global variables: LEVEL, BUILD, and other capitalized variable names found in +the test runner script (see the list of global variables in process_args().). + + +-a level +--at-level level +--all + Run the tests at the given level. Any test at a level at or below + this is run, any test at a level above this is not run. Level 0 + runs all tests. The default is to run tests at level 1. --all is + a shortcut for -a 0. + +-b +--build + Run "python setup.py build" before running tests, where "python" + is the version of python used to run test.py. Highly recommended. + Tests will be run from the build directory. + +-B +--build-inplace + Run "python setup.py build_ext -i" before running tests. Tests will be + run from the source directory. + +-c +--pychecker + use pychecker + +-d +--debug + Instead of the normal test harness, run a debug version which + doesn't catch any exceptions. This is occasionally handy when the + unittest code catching the exception doesn't work right. + Unfortunately, the debug harness doesn't print the name of the + test, so Use With Care. + +-D +--debug-inplace + Works like -d, except that it loads pdb when an exception occurs. + +--dir directory +-s directory + Option to limit where tests are searched for. This is important + when you *really* want to limit the code that gets run. This can + be specified more than once to run tests in two different parts of + the source tree. + For example, if refactoring interfaces, you don't want to see the way + you have broken setups for tests in other packages. You *just* want to + run the interface tests. + +-f +--skip-unit + Run functional tests but not unit tests. + Note that functional tests will be skipped if the module + zope.app.tests.functional cannot be imported. + Functional tests also expect to find the file ftesting.zcml, + which is used to configure the functional-test run. + +-F + DEPRECATED. Run both unit and functional tests. + This option is deprecated, because this is the new default mode. + Note that functional tests will be skipped if the module + zope.app.tests.functional cannot be imported. + +-g threshold +--gc-threshold threshold + Set the garbage collector generation0 threshold. This can be used + to stress memory and gc correctness. Some crashes are only + reproducible when the threshold is set to 1 (agressive garbage + collection). Do "-g 0" to disable garbage collection altogether. + +-G gc_option +--gc-option gc_option + Set the garbage collection debugging flags. The argument must be one + of the DEBUG_ flags defined bythe Python gc module. Multiple options + can be specified by using "-G OPTION1 -G OPTION2." + +-k +--keepbytecode + Do not delete all stale bytecode before running tests + +-l test_root +--libdir test_root + Search for tests starting in the specified start directory + (useful for testing components being developed outside the main + "src" or "build" trees). + +-L +--loop + Keep running the selected tests in a loop. You may experience + memory leakage. + +-m +-M minimal GUI. See -U. + +-P +--profile + Run the tests under hotshot and display the top 50 stats, sorted by + cumulative time and number of calls. + +-p +--progress + Show running progress. It can be combined with -v or -vv. + +-r +--refcount + Look for refcount problems. + This requires that Python was built --with-pydebug. + +-t +--top-fifty + Time the individual tests and print a list of the top 50, sorted from + longest to shortest. + +--times n +--times outfile + With an integer argument, time the tests and print a list of the top <n> + tests, sorted from longest to shortest. + With a non-integer argument, specifies a file to which timing information + is to be printed. + +-T +--trace + Use the trace module from Python for code coverage. The current + utility writes coverage files to a directory named `coverage' that + is parallel to `build'. It also prints a summary to stdout. + +-u +--skip-functional + CHANGED. Run unit tests but not functional tests. + Note that the meaning of -u is changed from its former meaning, + which is now specified by -U or --gui. + +-U +--gui + Use the PyUnit GUI instead of output to the command line. The GUI + imports tests on its own, taking care to reload all dependencies + on each run. The debug (-d), verbose (-v), progress (-p), and + Loop (-L) options will be ignored. The testfilter filter is also + not applied. + +-m +-M +--minimal-gui + Note: -m is DEPRECATED in favour of -M or --minimal-gui. + -m starts the gui minimized. Double-clicking the progress bar + will start the import and run all tests. + + +-v +--verbose + Verbose output. With one -v, unittest prints a dot (".") for each + test run. With -vv, unittest prints the name of each test (for + some definition of "name" ...). With no -v, unittest is silent + until the end of the run, except when errors occur. + + When -p is also specified, the meaning of -v is slightly + different. With -p and no -v only the percent indicator is + displayed. With -p and -v the test name of the current test is + shown to the right of the percent indicator. With -p and -vv the + test name is not truncated to fit into 80 columns and it is not + cleared after the test finishes. + + +modfilter +testfilter + Case-sensitive regexps to limit which tests are run, used in search + (not match) mode. + In an extension of Python regexp notation, a leading "!" is stripped + and causes the sense of the remaining regexp to be negated (so "!bc" + matches any string that does not match "bc", and vice versa). + By default these act like ".", i.e. nothing is excluded. + + modfilter is applied to a test file's path, starting at "build" and + including (OS-dependent) path separators. + + testfilter is applied to the (method) name of the unittest methods + contained in the test files whose paths modfilter matched. + +Extreme (yet useful) examples: + + test.py -vvb . "^testWriteClient$" + + Builds the project silently, then runs unittest in verbose mode on all + tests whose names are precisely "testWriteClient". Useful when + debugging a specific test. + + test.py -vvb . "!^testWriteClient$" + + As before, but runs all tests whose names aren't precisely + "testWriteClient". Useful to avoid a specific failing test you don't + want to deal with just yet. + + test.py -M . "!^testWriteClient$" + + As before, but now opens up a minimized PyUnit GUI window (only showing + the progress bar). Useful for refactoring runs where you continually want + to make sure all tests still pass. +""" + +import gc +import hotshot, hotshot.stats +import os +import re +import pdb +import sys +import threading # just to get at Thread objects created by tests +import time +import traceback +import unittest +import warnings + +def set_trace_doctest(stdin=sys.stdin, stdout=sys.stdout, trace=pdb.set_trace): + sys.stdin = stdin + sys.stdout = stdout + trace() + +pdb.set_trace_doctest = set_trace_doctest + +from distutils.util import get_platform + +PLAT_SPEC = "%s-%s" % (get_platform(), sys.version[0:3]) + +class ImmediateTestResult(unittest._TextTestResult): + + __super_init = unittest._TextTestResult.__init__ + __super_startTest = unittest._TextTestResult.startTest + __super_printErrors = unittest._TextTestResult.printErrors + + def __init__(self, stream, descriptions, verbosity, debug=False, + count=None, progress=False): + self.__super_init(stream, descriptions, verbosity) + self._debug = debug + self._progress = progress + self._progressWithNames = False + self.count = count + self._testtimes = {} + if progress and verbosity == 1: + self.dots = False + self._progressWithNames = True + self._lastWidth = 0 + self._maxWidth = 80 + try: + import curses + except ImportError: + pass + else: + curses.setupterm() + self._maxWidth = curses.tigetnum('cols') + self._maxWidth -= len("xxxx/xxxx (xxx.x%): ") + 1 + + def stopTest(self, test): + self._testtimes[test] = time.time() - self._testtimes[test] + if gc.garbage: + print "The following test left garbage:" + print test + print gc.garbage + # XXX Perhaps eat the garbage here, so that the garbage isn't + # printed for every subsequent test. + + # Did the test leave any new threads behind? + new_threads = [t for t in threading.enumerate() + if (t.isAlive() + and + t not in self._threads)] + if new_threads: + print "The following test left new threads behind:" + print test + print "New thread(s):", new_threads + + def print_times(self, stream, count=None): + results = self._testtimes.items() + results.sort(lambda x, y: cmp(y[1], x[1])) + if count: + n = min(count, len(results)) + if n: + print >>stream, "Top %d longest tests:" % n + else: + n = len(results) + if not n: + return + for i in range(n): + print >>stream, "%6dms" % int(results[i][1] * 1000), results[i][0] + + def _print_traceback(self, msg, err, test, errlist): + if self.showAll or self.dots or self._progress: + self.stream.writeln("\n") + self._lastWidth = 0 + + tb = "".join(traceback.format_exception(*err)) + self.stream.writeln(msg) + self.stream.writeln(tb) + errlist.append((test, tb)) + + def startTest(self, test): + if self._progress: + self.stream.write("\r%4d" % (self.testsRun + 1)) + if self.count: + self.stream.write("/%d (%5.1f%%)" % (self.count, + (self.testsRun + 1) * 100.0 / self.count)) + if self.showAll: + self.stream.write(": ") + elif self._progressWithNames: + # XXX will break with multibyte strings + name = self.getShortDescription(test) + width = len(name) + if width < self._lastWidth: + name += " " * (self._lastWidth - width) + self.stream.write(": %s" % name) + self._lastWidth = width + self.stream.flush() + self._threads = threading.enumerate() + self.__super_startTest(test) + self._testtimes[test] = time.time() + + def getShortDescription(self, test): + s = self.getDescription(test) + if len(s) > self._maxWidth: + pos = s.find(" (") + if pos >= 0: + w = self._maxWidth - (pos + 5) + if w < 1: + # first portion (test method name) is too long + s = s[:self._maxWidth-3] + "..." + else: + pre = s[:pos+2] + post = s[-w:] + s = "%s...%s" % (pre, post) + return s[:self._maxWidth] + + def addError(self, test, err): + if self._progress: + self.stream.write("\r") + if self._debug: + raise err[0], err[1], err[2] + self._print_traceback("Error in test %s" % test, err, + test, self.errors) + + def addFailure(self, test, err): + if self._progress: + self.stream.write("\r") + if self._debug: + raise err[0], err[1], err[2] + self._print_traceback("Failure in test %s" % test, err, + test, self.failures) + + def printErrors(self): + if self._progress and not (self.dots or self.showAll): + self.stream.writeln() + self.__super_printErrors() + + def printErrorList(self, flavor, errors): + for test, err in errors: + self.stream.writeln(self.separator1) + self.stream.writeln("%s: %s" % (flavor, self.getDescription(test))) + self.stream.writeln(self.separator2) + self.stream.writeln(err) + + +class ImmediateTestRunner(unittest.TextTestRunner): + + __super_init = unittest.TextTestRunner.__init__ + + def __init__(self, **kwarg): + debug = kwarg.get("debug") + if debug is not None: + del kwarg["debug"] + progress = kwarg.get("progress") + if progress is not None: + del kwarg["progress"] + profile = kwarg.get("profile") + if profile is not None: + del kwarg["profile"] + self.__super_init(**kwarg) + self._debug = debug + self._progress = progress + self._profile = profile + # Create the test result here, so that we can add errors if + # the test suite search process has problems. The count + # attribute must be set in run(), because we won't know the + # count until all test suites have been found. + self.result = ImmediateTestResult( + self.stream, self.descriptions, self.verbosity, debug=self._debug, + progress=self._progress) + + def _makeResult(self): + # Needed base class run method. + return self.result + + def run(self, test): + self.result.count = test.countTestCases() + if self._debug: + club_debug(test) + if self._profile: + prof = hotshot.Profile("tests_profile.prof") + args = (self, test) + r = prof.runcall(unittest.TextTestRunner.run, *args) + prof.close() + stats = hotshot.stats.load("tests_profile.prof") + stats.sort_stats('cumulative', 'calls') + stats.print_stats(50) + return r + return unittest.TextTestRunner.run(self, test) + +def club_debug(test): + # Beat a debug flag into debug-aware test cases + setDebugModeOn = getattr(test, 'setDebugModeOn', None) + if setDebugModeOn is not None: + setDebugModeOn() + + for subtest in getattr(test, '_tests', ()): + club_debug(subtest) + +# setup list of directories to put on the path +class PathInit: + def __init__(self, build, build_inplace, libdir=None): + self.inplace = None + # Figure out if we should test in-place or test in-build. If the -b + # or -B option was given, test in the place we were told to build in. + # Otherwise, we'll look for a build directory and if we find one, + # we'll test there, otherwise we'll test in-place. + if build: + self.inplace = build_inplace + if self.inplace is None: + # Need to figure it out + if os.path.isdir(os.path.join("build", "lib.%s" % PLAT_SPEC)): + self.inplace = False + else: + self.inplace = True + # Calculate which directories we're going to add to sys.path, and cd + # to the appropriate working directory + self.org_cwd = os.getcwd() + if self.inplace: + self.libdir = "src" + else: + self.libdir = "lib.%s" % PLAT_SPEC + os.chdir("build") + # Hack sys.path + self.cwd = os.getcwd() + sys.path.insert(0, os.path.join(self.cwd, self.libdir)) + # Hack again for external products. + global functional + kind = functional and "FUNCTIONAL" or "UNIT" + if libdir: + extra = os.path.join(self.org_cwd, libdir) + print "Running %s tests from %s" % (kind, extra) + self.libdir = extra + sys.path.insert(0, extra) + else: + print "Running %s tests from %s" % (kind, self.cwd) + # Make sure functional tests find ftesting.zcml + if functional: + config_file = 'ftesting.zcml' + if not self.inplace: + # We chdired into build, so ftesting.zcml is in the + # parent directory + config_file = os.path.join('..', 'ftesting.zcml') + print "Parsing %s" % config_file + from zope.app.tests.functional import FunctionalTestSetup + FunctionalTestSetup(config_file) + +def match(rx, s): + if not rx: + return True + if rx[0] == "!": + return re.search(rx[1:], s) is None + else: + return re.search(rx, s) is not None + +class TestFileFinder: + def __init__(self, prefix): + self.files = [] + self._plen = len(prefix) + if not prefix.endswith(os.sep): + self._plen += 1 + global functional + if functional: + self.dirname = "ftests" + else: + self.dirname = "tests" + + def visit(self, rx, dir, files): + if os.path.split(dir)[1] != self.dirname: + # Allow tests/ftests module rather than package. + modfname = self.dirname + '.py' + if modfname in files: + path = os.path.join(dir, modfname) + if match(rx, path): + self.files.append(path) + return + return + # ignore tests that aren't in packages + if not "__init__.py" in files: + if not files or files == ["CVS"]: + return + print "not a package", dir + return + + # Put matching files in matches. If matches is non-empty, + # then make sure that the package is importable. + matches = [] + for file in files: + if file.startswith('test') and os.path.splitext(file)[-1] == '.py': + path = os.path.join(dir, file) + if match(rx, path): + matches.append(path) + + # ignore tests when the package can't be imported, possibly due to + # dependency failures. + pkg = dir[self._plen:].replace(os.sep, '.') + try: + __import__(pkg) + # We specifically do not want to catch ImportError since that's useful + # information to know when running the tests. + except RuntimeError, e: + if VERBOSE: + print "skipping %s because: %s" % (pkg, e) + return + else: + self.files.extend(matches) + + def module_from_path(self, path): + """Return the Python package name indicated by the filesystem path.""" + assert path.endswith(".py") + path = path[self._plen:-3] + mod = path.replace(os.sep, ".") + return mod + +def walk_with_symlinks(top, func, arg): + """Like os.path.walk, but follows symlinks on POSIX systems. + + This could theoreticaly result in an infinite loop, if you create symlink + cycles in your Zope sandbox, so don't do that. + """ + try: + names = os.listdir(top) + except os.error: + return + func(arg, top, names) + exceptions = ('.', '..') + for name in names: + if name not in exceptions: + name = os.path.join(top, name) + if os.path.isdir(name): + walk_with_symlinks(name, func, arg) + +def find_test_dir(dir): + if os.path.exists(dir): + return dir + d = os.path.join(pathinit.libdir, dir) + if os.path.exists(d): + if os.path.isdir(d): + return d + raise ValueError("%s does not exist and %s is not a directory" + % (dir, d)) + raise ValueError("%s does not exist!" % dir) + +def find_tests(rx): + global finder + finder = TestFileFinder(pathinit.libdir) + + if TEST_DIRS: + for d in TEST_DIRS: + d = find_test_dir(d) + walk_with_symlinks(d, finder.visit, rx) + else: + walk_with_symlinks(pathinit.libdir, finder.visit, rx) + return finder.files + +def package_import(modname): + mod = __import__(modname) + for part in modname.split(".")[1:]: + mod = getattr(mod, part) + return mod + +class PseudoTestCase: + """Minimal test case objects to create error reports. + + If test.py finds something that looks like it should be a test but + can't load it or find its test suite, it will report an error + using a PseudoTestCase. + """ + + def __init__(self, name, descr=None): + self.name = name + self.descr = descr + + def shortDescription(self): + return self.descr + + def __str__(self): + return "Invalid Test (%s)" % self.name + +def get_suite(file, result): + modname = finder.module_from_path(file) + try: + mod = package_import(modname) + return mod.test_suite() + except: + result.addError(PseudoTestCase(modname), sys.exc_info()) + return None + +def filter_testcases(s, rx): + new = unittest.TestSuite() + for test in s._tests: + # See if the levels match + dolevel = (LEVEL == 0) or LEVEL >= getattr(test, "level", 0) + if not dolevel: + continue + if isinstance(test, unittest.TestCase): + name = test.id() # Full test name: package.module.class.method + name = name[1 + name.rfind("."):] # extract method name + if not rx or match(rx, name): + new.addTest(test) + else: + filtered = filter_testcases(test, rx) + if filtered: + new.addTest(filtered) + return new + +def gui_runner(files, test_filter): + if BUILD_INPLACE: + utildir = os.path.join(os.getcwd(), "utilities") + else: + utildir = os.path.join(os.getcwd(), "..", "utilities") + sys.path.append(utildir) + import unittestgui + suites = [] + for file in files: + suites.append(finder.module_from_path(file) + ".test_suite") + + suites = ", ".join(suites) + minimal = (GUI == "minimal") + unittestgui.main(suites, minimal) + +class TrackRefs: + """Object to track reference counts across test runs.""" + + def __init__(self): + self.type2count = {} + self.type2all = {} + + def update(self): + obs = sys.getobjects(0) + type2count = {} + type2all = {} + for o in obs: + all = sys.getrefcount(o) + + if type(o) is str and o == '<dummy key>': + # avoid dictionary madness + continue + t = type(o) + if t in type2count: + type2count[t] += 1 + type2all[t] += all + else: + type2count[t] = 1 + type2all[t] = all + + ct = [(type2count[t] - self.type2count.get(t, 0), + type2all[t] - self.type2all.get(t, 0), + t) + for t in type2count.iterkeys()] + ct.sort() + ct.reverse() + printed = False + for delta1, delta2, t in ct: + if delta1 or delta2: + if not printed: + print "%-55s %8s %8s" % ('', 'insts', 'refs') + printed = True + print "%-55s %8d %8d" % (t, delta1, delta2) + + self.type2count = type2count + self.type2all = type2all + +def runner(files, test_filter, debug): + runner = ImmediateTestRunner(verbosity=VERBOSE, debug=DEBUG, + progress=PROGRESS, profile=PROFILE, + descriptions=False) + suite = unittest.TestSuite() + for file in files: + s = get_suite(file, runner.result) + # See if the levels match + dolevel = (LEVEL == 0) or LEVEL >= getattr(s, "level", 0) + if s is not None and dolevel: + s = filter_testcases(s, test_filter) + suite.addTest(s) + try: + r = runner.run(suite) + if TIMESFN: + r.print_times(open(TIMESFN, "w")) + if VERBOSE: + print "Wrote timing data to", TIMESFN + if TIMETESTS: + r.print_times(sys.stdout, TIMETESTS) + except: + if DEBUGGER: + print "%s:" % (sys.exc_info()[0], ) + print sys.exc_info()[1] + pdb.post_mortem(sys.exc_info()[2]) + else: + raise + +def remove_stale_bytecode(arg, dirname, names): + names = map(os.path.normcase, names) + for name in names: + if name.endswith(".pyc") or name.endswith(".pyo"): + srcname = name[:-1] + if srcname not in names: + fullname = os.path.join(dirname, name) + print "Removing stale bytecode file", fullname + os.unlink(fullname) + +def main(module_filter, test_filter, libdir): + if not KEEP_STALE_BYTECODE: + os.path.walk(os.curdir, remove_stale_bytecode, None) + + configure_logging() + + # Initialize the path and cwd + global pathinit + pathinit = PathInit(BUILD, BUILD_INPLACE, libdir) + + files = find_tests(module_filter) + files.sort() + + if GUI: + gui_runner(files, test_filter) + elif LOOP: + if REFCOUNT: + rc = sys.gettotalrefcount() + track = TrackRefs() + while True: + runner(files, test_filter, DEBUG) + gc.collect() + if gc.garbage: + print "GARBAGE:", len(gc.garbage), gc.garbage + return + if REFCOUNT: + prev = rc + rc = sys.gettotalrefcount() + print "totalrefcount=%-8d change=%-6d" % (rc, rc - prev) + track.update() + else: + runner(files, test_filter, DEBUG) + + os.chdir(pathinit.org_cwd) + + +def configure_logging(): + """Initialize the logging module.""" + import logging.config + + # Get the log.ini file from the current directory instead of possibly + # buried in the build directory. XXX This isn't perfect because if + # log.ini specifies a log file, it'll be relative to the build directory. + # Hmm... + logini = os.path.abspath("log.ini") + + if os.path.exists(logini): + logging.config.fileConfig(logini) + else: + logging.basicConfig() + + if os.environ.has_key("LOGGING"): + level = int(os.environ["LOGGING"]) + logging.getLogger().setLevel(level) + + +def process_args(argv=None): + import getopt + global MODULE_FILTER + global TEST_FILTER + global VERBOSE + global LOOP + global GUI + global TRACE + global REFCOUNT + global DEBUG + global DEBUGGER + global BUILD + global LEVEL + global LIBDIR + global TIMESFN + global TIMETESTS + global PROGRESS + global BUILD_INPLACE + global KEEP_STALE_BYTECODE + global TEST_DIRS + global PROFILE + global GC_THRESHOLD + global GC_FLAGS + global RUN_UNIT + global RUN_FUNCTIONAL + global PYCHECKER + + if argv is None: + argv = sys.argv + + MODULE_FILTER = None + TEST_FILTER = None + VERBOSE = 0 + LOOP = False + GUI = False + TRACE = False + REFCOUNT = False + DEBUG = False # Don't collect test results; simply let tests crash + DEBUGGER = False + BUILD = False + BUILD_INPLACE = False + GC_THRESHOLD = None + gcdebug = 0 + GC_FLAGS = [] + LEVEL = 1 + LIBDIR = None + PROGRESS = False + TIMESFN = None + TIMETESTS = 0 + KEEP_STALE_BYTECODE = 0 + RUN_UNIT = True + RUN_FUNCTIONAL = True + TEST_DIRS = [] + PROFILE = False + PYCHECKER = False + config_filename = 'test.config' + + # import the config file + if os.path.isfile(config_filename): + print 'Configuration file found.' + execfile(config_filename, globals()) + + + try: + opts, args = getopt.getopt(argv[1:], "a:bBcdDfFg:G:hkl:LmMPprs:tTuUv", + ["all", "help", "libdir=", "times=", + "keepbytecode", "dir=", "build", + "build-inplace", + "at-level=", + "pychecker", "debug", "pdebug", + "gc-threshold=", "gc-option=", + "loop", "gui", "minimal-gui", + "profile", "progress", "refcount", "trace", + "top-fifty", "verbose", + ]) + # fixme: add the long names + # fixme: add the extra documentation + # fixme: test for functional first! + except getopt.error, msg: + print msg + print "Try `python %s -h' for more information." % argv[0] + sys.exit(2) + + for k, v in opts: + if k in ("-a", "--at-level"): + LEVEL = int(v) + elif k == "--all": + LEVEL = 0 + os.environ["COMPLAIN_IF_TESTS_MISSED"]='1' + elif k in ("-b", "--build"): + BUILD = True + elif k in ("-B", "--build-inplace"): + BUILD = BUILD_INPLACE = True + elif k in("-c", "--pychecker"): + PYCHECKER = True + elif k in ("-d", "--debug"): + DEBUG = True + elif k in ("-D", "--pdebug"): + DEBUG = True + DEBUGGER = True + elif k in ("-f", "--skip-unit"): + RUN_UNIT = False + elif k in ("-u", "--skip-functional"): + RUN_FUNCTIONAL = False + elif k == "-F": + message = 'Unit plus functional is the default behaviour.' + warnings.warn(message, DeprecationWarning) + RUN_UNIT = True + RUN_FUNCTIONAL = True + elif k in ("-h", "--help"): + print __doc__ + sys.exit(0) + elif k in ("-g", "--gc-threshold"): + GC_THRESHOLD = int(v) + elif k in ("-G", "--gc-option"): + if not v.startswith("DEBUG_"): + print "-G argument must be DEBUG_ flag, not", repr(v) + sys.exit(1) + GC_FLAGS.append(v) + elif k in ('-k', '--keepbytecode'): + KEEP_STALE_BYTECODE = 1 + elif k in ('-l', '--libdir'): + LIBDIR = v + elif k in ("-L", "--loop"): + LOOP = 1 + elif k == "-m": + GUI = "minimal" + msg = "Use -M or --minimal-gui instead of -m." + warnings.warn(msg, DeprecationWarning) + elif k in ("-M", "--minimal-gui"): + GUI = "minimal" + elif k in ("-P", "--profile"): + PROFILE = True + elif k in ("-p", "--progress"): + PROGRESS = True + elif k in ("-r", "--refcount"): + REFCOUNT = True + elif k in ("-T", "--trace"): + TRACE = True + elif k in ("-t", "--top-fifty"): + if not TIMETESTS: + TIMETESTS = 50 + elif k in ("-u", "--gui"): + GUI = 1 + elif k in ("-v", "--verbose"): + VERBOSE += 1 + elif k == "--times": + try: + TIMETESTS = int(v) + except ValueError: + # must be a filename to write + TIMESFN = v + elif k in ('-s', '--dir'): + TEST_DIRS.append(v) + + if PYCHECKER: + # make sure you have a recent version of pychecker + if not os.environ.get("PYCHECKER"): + os.environ["PYCHECKER"] = "-q" + import pychecker.checker + + if REFCOUNT and not hasattr(sys, "gettotalrefcount"): + print "-r ignored, because it needs a debug build of Python" + REFCOUNT = False + + if sys.version_info < ( 2,3,2 ): + print """\ + ERROR: Your python version is not supported by Zope3. + Zope3 needs Python 2.3.2 or greater. You are running:""" + sys.version + sys.exit(1) + + if GC_THRESHOLD is not None: + if GC_THRESHOLD == 0: + gc.disable() + print "gc disabled" + else: + gc.set_threshold(GC_THRESHOLD) + print "gc threshold:", gc.get_threshold() + + if GC_FLAGS: + val = 0 + for flag in GC_FLAGS: + v = getattr(gc, flag, None) + if v is None: + print "Unknown gc flag", repr(flag) + print gc.set_debug.__doc__ + sys.exit(1) + val |= v + gcdebug |= v + + if gcdebug: + gc.set_debug(gcdebug) + + if BUILD: + # Python 2.3 is more sane in its non -q output + if sys.hexversion >= 0x02030000: + qflag = "" + else: + qflag = "-q" + cmd = sys.executable + " setup.py " + qflag + " build" + if BUILD_INPLACE: + cmd += "_ext -i" + if VERBOSE: + print cmd + sts = os.system(cmd) + if sts: + print "Build failed", hex(sts) + sys.exit(1) + + k = [] + if RUN_UNIT: + k.append(False) + if RUN_FUNCTIONAL: + k.append(True) + + global functional + for functional in k: + + if VERBOSE: + kind = functional and "FUNCTIONAL" or "UNIT" + if LEVEL == 0: + print "Running %s tests at all levels" % kind + else: + print "Running %s tests at level %d" % (kind, LEVEL) + +# This was to avoid functional tests outside of z3, but this doesn't really +# work right. +## if functional: +## try: +## from zope.app.tests.functional import FunctionalTestSetup +## except ImportError: +## raise +## print ('Skipping functional tests: could not import ' +## 'zope.app.tests.functional') +## continue + + # XXX We want to change *visible* warnings into errors. The next + # line changes all warnings into errors, including warnings we + # normally never see. In particular, test_datetime does some + # short-integer arithmetic that overflows to long ints, and, by + # default, Python doesn't display the overflow warning that can + # be enabled when this happens. The next line turns that into an + # error instead. Guido suggests that a better to get what we're + # after is to replace warnings.showwarning() with our own thing + # that raises an error. + ## warnings.filterwarnings("error") + warnings.filterwarnings("ignore", module="logging") + + if args: + if len(args) > 1: + TEST_FILTER = args[1] + MODULE_FILTER = args[0] + try: + if TRACE: + # if the trace module is used, then we don't exit with + # status if on a false return value from main. + coverdir = os.path.join(os.getcwd(), "coverage") + import trace + ignoremods = ["os", "posixpath", "stat"] + tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix], + ignoremods=ignoremods, + trace=False, count=True) + + tracer.runctx("main(MODULE_FILTER, TEST_FILTER, LIBDIR)", + globals=globals(), locals=vars()) + r = tracer.results() + path = "/tmp/trace.%s" % os.getpid() + import cPickle + f = open(path, "wb") + cPickle.dump(r, f) + f.close() + print path + r.write_results(show_missing=True, + summary=True, coverdir=coverdir) + else: + bad = main(MODULE_FILTER, TEST_FILTER, LIBDIR) + if bad: + sys.exit(1) + except ImportError, err: + print err + print sys.path + raise + + +if __name__ == "__main__": + process_args() diff -r bd37123974b2 -r cd228621e1fd tools/python/xen/xend/tests/test_sxp.py --- /dev/null Thu Oct 6 10:12:55 2005 +++ b/tools/python/xen/xend/tests/test_sxp.py Thu Oct 6 14:12:31 2005 @@ -0,0 +1,18 @@ +import unittest + +import xen.xend.sxp + + +class test_sxp(unittest.TestCase): + + def testAllFromString(self): + def t(input, expected): + self.assertEqual(xen.xend.sxp.all_from_string(input), expected) + + t('String', ['String']) + t('(String Thing)', [['String', 'Thing']]) + t('(String) (Thing)', [['String'], ['Thing']]) + + +def test_suite(): + return unittest.makeSuite(test_sxp) _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |