ia64/xen-unstable

changeset 7244:cd228621e1fd

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@xensource.com>
author emellor@ewan
date Thu Oct 06 15:12:31 2005 +0100 (2005-10-06)
parents bd37123974b2
children 4083eb31def0 f5320ac7ed31
files Makefile tools/python/Makefile tools/python/README tools/python/ZPL-2.0 tools/python/setup.py tools/python/test.py tools/python/xen/xend/tests/__init__.py tools/python/xen/xend/tests/test_sxp.py
line diff
     1.1 --- a/Makefile	Thu Oct 06 11:12:55 2005 +0100
     1.2 +++ b/Makefile	Thu Oct 06 15:12:31 2005 +0100
     1.3 @@ -36,6 +36,12 @@ build: kernels
     1.4  	$(MAKE) -C tools build
     1.5  	$(MAKE) -C docs build
     1.6  
     1.7 +# The test target is for unit tests that can run without an installation.  Of
     1.8 +# course, many tests require a machine running Xen itself, and these are
     1.9 +# handled elsewhere.
    1.10 +test:
    1.11 +	$(MAKE) -C tools/python test
    1.12 +
    1.13  # build and install everything into local dist directory
    1.14  dist: DESTDIR=$(DISTDIR)/install
    1.15  dist: dist-xen dist-kernels dist-tools dist-docs
     2.1 --- a/tools/python/Makefile	Thu Oct 06 11:12:55 2005 +0100
     2.2 +++ b/tools/python/Makefile	Thu Oct 06 15:12:31 2005 +0100
     2.3 @@ -15,5 +15,8 @@ install: all
     2.4  	CFLAGS="$(CFLAGS)" python setup.py install --root="$(DESTDIR)"
     2.5  endif
     2.6  
     2.7 +test:
     2.8 +	export LD_LIBRARY_PATH=$$(readlink -f ../libxc):$$(readlink -f ../xenstore); python test.py -b -u
     2.9 +
    2.10  clean:
    2.11  	rm -rf build *.pyc *.pyo *.o *.a *~
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/tools/python/README	Thu Oct 06 15:12:31 2005 +0100
     3.3 @@ -0,0 +1,3 @@
     3.4 +The file test.py here is from the Zope project, and is Copyright (c) 2001,
     3.5 +2002 Zope Corporation and Contributors.  This file is released under the Zope
     3.6 +Public License, version 2.0, a copy of which is in the file ZPL-2.0.
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/tools/python/ZPL-2.0	Thu Oct 06 15:12:31 2005 +0100
     4.3 @@ -0,0 +1,59 @@
     4.4 +Zope Public License (ZPL) Version 2.0
     4.5 +-----------------------------------------------
     4.6 +
     4.7 +This software is Copyright (c) Zope Corporation (tm) and
     4.8 +Contributors. All rights reserved.
     4.9 +
    4.10 +This license has been certified as open source. It has also
    4.11 +been designated as GPL compatible by the Free Software
    4.12 +Foundation (FSF).
    4.13 +
    4.14 +Redistribution and use in source and binary forms, with or
    4.15 +without modification, are permitted provided that the
    4.16 +following conditions are met:
    4.17 +
    4.18 +1. Redistributions in source code must retain the above
    4.19 +   copyright notice, this list of conditions, and the following
    4.20 +   disclaimer.
    4.21 +
    4.22 +2. Redistributions in binary form must reproduce the above
    4.23 +   copyright notice, this list of conditions, and the following
    4.24 +   disclaimer in the documentation and/or other materials
    4.25 +   provided with the distribution.
    4.26 +
    4.27 +3. The name Zope Corporation (tm) must not be used to
    4.28 +   endorse or promote products derived from this software
    4.29 +   without prior written permission from Zope Corporation.
    4.30 +
    4.31 +4. The right to distribute this software or to use it for
    4.32 +   any purpose does not give you the right to use Servicemarks
    4.33 +   (sm) or Trademarks (tm) of Zope Corporation. Use of them is
    4.34 +   covered in a separate agreement (see
    4.35 +   http://www.zope.com/Marks).
    4.36 +
    4.37 +5. If any files are modified, you must cause the modified
    4.38 +   files to carry prominent notices stating that you changed
    4.39 +   the files and the date of any change.
    4.40 +
    4.41 +Disclaimer
    4.42 +
    4.43 +  THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
    4.44 +  AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
    4.45 +  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
    4.46 +  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
    4.47 +  NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE
    4.48 +  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    4.49 +  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    4.50 +  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    4.51 +  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    4.52 +  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    4.53 +  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
    4.54 +  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    4.55 +  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
    4.56 +  DAMAGE.
    4.57 +
    4.58 +
    4.59 +This software consists of contributions made by Zope
    4.60 +Corporation and many individuals on behalf of Zope
    4.61 +Corporation.  Specific attributions are listed in the
    4.62 +accompanying credits file.
    4.63 \ No newline at end of file
     5.1 --- a/tools/python/setup.py	Thu Oct 06 11:12:55 2005 +0100
     5.2 +++ b/tools/python/setup.py	Thu Oct 06 15:12:31 2005 +0100
     5.3 @@ -42,7 +42,9 @@ setup(name            = 'xen',
     5.4                           'xen.xend.xenstore',
     5.5                           'xen.xm',
     5.6                           'xen.web',
     5.7 -                         'xen.sv'
     5.8 +                         'xen.sv',
     5.9 +
    5.10 +                         'xen.xend.tests'
    5.11                           ],
    5.12        ext_package = "xen.lowlevel",
    5.13        ext_modules = [ xc, xs ]
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/tools/python/test.py	Thu Oct 06 15:12:31 2005 +0100
     6.3 @@ -0,0 +1,1094 @@
     6.4 +#! /usr/bin/env python2.3
     6.5 +##############################################################################
     6.6 +#
     6.7 +# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
     6.8 +# All Rights Reserved.
     6.9 +#
    6.10 +# This software is subject to the provisions of the Zope Public License,
    6.11 +# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
    6.12 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
    6.13 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    6.14 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    6.15 +# FOR A PARTICULAR PURPOSE.
    6.16 +#
    6.17 +##############################################################################
    6.18 +"""
    6.19 +test.py [-abBcdDfFgGhklLmMPprstTuUv] [modfilter [testfilter]]
    6.20 +
    6.21 +Find and run tests written using the unittest module.
    6.22 +
    6.23 +The test runner searches for Python modules that contain test suites.
    6.24 +It collects those suites, and runs the tests.  There are many options
    6.25 +for controlling how the tests are run.  There are options for using
    6.26 +the debugger, reporting code coverage, and checking for refcount problems.
    6.27 +
    6.28 +The test runner uses the following rules for finding tests to run.  It
    6.29 +searches for packages and modules that contain "tests" as a component
    6.30 +of the name, e.g. "frob.tests.nitz" matches this rule because tests is
    6.31 +a sub-package of frob.  Within each "tests" package, it looks for
    6.32 +modules that begin with the name "test."  For each test module, it
    6.33 +imports the module and calls the module's test_suite() function, which must
    6.34 +return a unittest TestSuite object.
    6.35 +
    6.36 +Options can be specified as command line arguments (see below). However,
    6.37 +options may also be specified in a file named 'test.config', a Python
    6.38 +script which, if found, will be executed before the command line
    6.39 +arguments are processed.
    6.40 +
    6.41 +The test.config script should specify options by setting zero or more of the
    6.42 +global variables: LEVEL, BUILD, and other capitalized variable names found in
    6.43 +the test runner script (see the list of global variables in process_args().).
    6.44 +
    6.45 +
    6.46 +-a level
    6.47 +--at-level level
    6.48 +--all
    6.49 +    Run the tests at the given level.  Any test at a level at or below
    6.50 +    this is run, any test at a level above this is not run.  Level 0
    6.51 +    runs all tests.  The default is to run tests at level 1.  --all is
    6.52 +    a shortcut for -a 0.
    6.53 +
    6.54 +-b
    6.55 +--build
    6.56 +    Run "python setup.py build" before running tests, where "python"
    6.57 +    is the version of python used to run test.py.  Highly recommended.
    6.58 +    Tests will be run from the build directory.
    6.59 +
    6.60 +-B
    6.61 +--build-inplace
    6.62 +    Run "python setup.py build_ext -i" before running tests.  Tests will be
    6.63 +    run from the source directory.
    6.64 +
    6.65 +-c
    6.66 +--pychecker
    6.67 +    use pychecker
    6.68 +
    6.69 +-d
    6.70 +--debug
    6.71 +    Instead of the normal test harness, run a debug version which
    6.72 +    doesn't catch any exceptions.  This is occasionally handy when the
    6.73 +    unittest code catching the exception doesn't work right.
    6.74 +    Unfortunately, the debug harness doesn't print the name of the
    6.75 +    test, so Use With Care.
    6.76 +
    6.77 +-D
    6.78 +--debug-inplace
    6.79 +    Works like -d, except that it loads pdb when an exception occurs.
    6.80 +
    6.81 +--dir directory
    6.82 +-s directory
    6.83 +    Option to limit where tests are searched for. This is important
    6.84 +    when you *really* want to limit the code that gets run.  This can
    6.85 +    be specified more than once to run tests in two different parts of
    6.86 +    the source tree.
    6.87 +    For example, if refactoring interfaces, you don't want to see the way
    6.88 +    you have broken setups for tests in other packages. You *just* want to
    6.89 +    run the interface tests.
    6.90 +
    6.91 +-f
    6.92 +--skip-unit
    6.93 +    Run functional tests but not unit tests.
    6.94 +    Note that functional tests will be skipped if the module
    6.95 +    zope.app.tests.functional cannot be imported.
    6.96 +    Functional tests also expect to find the file ftesting.zcml,
    6.97 +    which is used to configure the functional-test run.
    6.98 +
    6.99 +-F
   6.100 +    DEPRECATED. Run both unit and functional tests.
   6.101 +    This option is deprecated, because this is the new default mode.
   6.102 +    Note that functional tests will be skipped if the module
   6.103 +    zope.app.tests.functional cannot be imported.
   6.104 +
   6.105 +-g threshold
   6.106 +--gc-threshold threshold
   6.107 +    Set the garbage collector generation0 threshold.  This can be used
   6.108 +    to stress memory and gc correctness.  Some crashes are only
   6.109 +    reproducible when the threshold is set to 1 (agressive garbage
   6.110 +    collection).  Do "-g 0" to disable garbage collection altogether.
   6.111 +
   6.112 +-G gc_option
   6.113 +--gc-option gc_option
   6.114 +    Set the garbage collection debugging flags.  The argument must be one
   6.115 +    of the DEBUG_ flags defined bythe Python gc module.  Multiple options
   6.116 +    can be specified by using "-G OPTION1 -G OPTION2."
   6.117 +
   6.118 +-k
   6.119 +--keepbytecode
   6.120 +    Do not delete all stale bytecode before running tests
   6.121 +
   6.122 +-l test_root
   6.123 +--libdir test_root
   6.124 +    Search for tests starting in the specified start directory
   6.125 +    (useful for testing components being developed outside the main
   6.126 +    "src" or "build" trees).
   6.127 +
   6.128 +-L
   6.129 +--loop
   6.130 +    Keep running the selected tests in a loop.  You may experience
   6.131 +    memory leakage.
   6.132 +
   6.133 +-m
   6.134 +-M  minimal GUI. See -U.
   6.135 +
   6.136 +-P
   6.137 +--profile
   6.138 +    Run the tests under hotshot and display the top 50 stats, sorted by
   6.139 +    cumulative time and number of calls.
   6.140 +
   6.141 +-p
   6.142 +--progress
   6.143 +    Show running progress.  It can be combined with -v or -vv.
   6.144 +
   6.145 +-r
   6.146 +--refcount
   6.147 +    Look for refcount problems.
   6.148 +    This requires that Python was built --with-pydebug.
   6.149 +
   6.150 +-t
   6.151 +--top-fifty
   6.152 +    Time the individual tests and print a list of the top 50, sorted from
   6.153 +    longest to shortest.
   6.154 +
   6.155 +--times n
   6.156 +--times outfile
   6.157 +    With an integer argument, time the tests and print a list of the top <n>
   6.158 +    tests, sorted from longest to shortest.
   6.159 +    With a non-integer argument, specifies a file to which timing information
   6.160 +    is to be printed.
   6.161 +
   6.162 +-T
   6.163 +--trace
   6.164 +    Use the trace module from Python for code coverage.  The current
   6.165 +    utility writes coverage files to a directory named `coverage' that
   6.166 +    is parallel to `build'.  It also prints a summary to stdout.
   6.167 +
   6.168 +-u
   6.169 +--skip-functional
   6.170 +    CHANGED. Run unit tests but not functional tests.
   6.171 +    Note that the meaning of -u is changed from its former meaning,
   6.172 +    which is now specified by -U or --gui.
   6.173 +
   6.174 +-U
   6.175 +--gui
   6.176 +    Use the PyUnit GUI instead of output to the command line.  The GUI
   6.177 +    imports tests on its own, taking care to reload all dependencies
   6.178 +    on each run.  The debug (-d), verbose (-v), progress (-p), and
   6.179 +    Loop (-L) options will be ignored.  The testfilter filter is also
   6.180 +    not applied.
   6.181 +
   6.182 +-m
   6.183 +-M
   6.184 +--minimal-gui
   6.185 +    Note: -m is DEPRECATED in favour of -M or --minimal-gui.
   6.186 +    -m starts the gui minimized.  Double-clicking the progress bar
   6.187 +    will start the import and run all tests.
   6.188 +
   6.189 +
   6.190 +-v
   6.191 +--verbose
   6.192 +    Verbose output.  With one -v, unittest prints a dot (".") for each
   6.193 +    test run.  With -vv, unittest prints the name of each test (for
   6.194 +    some definition of "name" ...).  With no -v, unittest is silent
   6.195 +    until the end of the run, except when errors occur.
   6.196 +
   6.197 +    When -p is also specified, the meaning of -v is slightly
   6.198 +    different.  With -p and no -v only the percent indicator is
   6.199 +    displayed.  With -p and -v the test name of the current test is
   6.200 +    shown to the right of the percent indicator.  With -p and -vv the
   6.201 +    test name is not truncated to fit into 80 columns and it is not
   6.202 +    cleared after the test finishes.
   6.203 +
   6.204 +
   6.205 +modfilter
   6.206 +testfilter
   6.207 +    Case-sensitive regexps to limit which tests are run, used in search
   6.208 +    (not match) mode.
   6.209 +    In an extension of Python regexp notation, a leading "!" is stripped
   6.210 +    and causes the sense of the remaining regexp to be negated (so "!bc"
   6.211 +    matches any string that does not match "bc", and vice versa).
   6.212 +    By default these act like ".", i.e. nothing is excluded.
   6.213 +
   6.214 +    modfilter is applied to a test file's path, starting at "build" and
   6.215 +    including (OS-dependent) path separators.
   6.216 +
   6.217 +    testfilter is applied to the (method) name of the unittest methods
   6.218 +    contained in the test files whose paths modfilter matched.
   6.219 +
   6.220 +Extreme (yet useful) examples:
   6.221 +
   6.222 +    test.py -vvb . "^testWriteClient$"
   6.223 +
   6.224 +    Builds the project silently, then runs unittest in verbose mode on all
   6.225 +    tests whose names are precisely "testWriteClient".  Useful when
   6.226 +    debugging a specific test.
   6.227 +
   6.228 +    test.py -vvb . "!^testWriteClient$"
   6.229 +
   6.230 +    As before, but runs all tests whose names aren't precisely
   6.231 +    "testWriteClient".  Useful to avoid a specific failing test you don't
   6.232 +    want to deal with just yet.
   6.233 +
   6.234 +    test.py -M . "!^testWriteClient$"
   6.235 +
   6.236 +    As before, but now opens up a minimized PyUnit GUI window (only showing
   6.237 +    the progress bar).  Useful for refactoring runs where you continually want
   6.238 +    to make sure all tests still pass.
   6.239 +"""
   6.240 +
   6.241 +import gc
   6.242 +import hotshot, hotshot.stats
   6.243 +import os
   6.244 +import re
   6.245 +import pdb
   6.246 +import sys
   6.247 +import threading    # just to get at Thread objects created by tests
   6.248 +import time
   6.249 +import traceback
   6.250 +import unittest
   6.251 +import warnings
   6.252 +
   6.253 +def set_trace_doctest(stdin=sys.stdin, stdout=sys.stdout, trace=pdb.set_trace):
   6.254 +    sys.stdin = stdin
   6.255 +    sys.stdout = stdout
   6.256 +    trace()
   6.257 +
   6.258 +pdb.set_trace_doctest = set_trace_doctest
   6.259 +
   6.260 +from distutils.util import get_platform
   6.261 +
   6.262 +PLAT_SPEC = "%s-%s" % (get_platform(), sys.version[0:3])
   6.263 +
   6.264 +class ImmediateTestResult(unittest._TextTestResult):
   6.265 +
   6.266 +    __super_init = unittest._TextTestResult.__init__
   6.267 +    __super_startTest = unittest._TextTestResult.startTest
   6.268 +    __super_printErrors = unittest._TextTestResult.printErrors
   6.269 +
   6.270 +    def __init__(self, stream, descriptions, verbosity, debug=False,
   6.271 +                 count=None, progress=False):
   6.272 +        self.__super_init(stream, descriptions, verbosity)
   6.273 +        self._debug = debug
   6.274 +        self._progress = progress
   6.275 +        self._progressWithNames = False
   6.276 +        self.count = count
   6.277 +        self._testtimes = {}
   6.278 +        if progress and verbosity == 1:
   6.279 +            self.dots = False
   6.280 +            self._progressWithNames = True
   6.281 +            self._lastWidth = 0
   6.282 +            self._maxWidth = 80
   6.283 +            try:
   6.284 +                import curses
   6.285 +            except ImportError:
   6.286 +                pass
   6.287 +            else:
   6.288 +                curses.setupterm()
   6.289 +                self._maxWidth = curses.tigetnum('cols')
   6.290 +            self._maxWidth -= len("xxxx/xxxx (xxx.x%): ") + 1
   6.291 +
   6.292 +    def stopTest(self, test):
   6.293 +        self._testtimes[test] = time.time() - self._testtimes[test]
   6.294 +        if gc.garbage:
   6.295 +            print "The following test left garbage:"
   6.296 +            print test
   6.297 +            print gc.garbage
   6.298 +            # XXX Perhaps eat the garbage here, so that the garbage isn't
   6.299 +            #     printed for every subsequent test.
   6.300 +
   6.301 +        # Did the test leave any new threads behind?
   6.302 +        new_threads = [t for t in threading.enumerate()
   6.303 +                         if (t.isAlive()
   6.304 +                             and
   6.305 +                             t not in self._threads)]
   6.306 +        if new_threads:
   6.307 +            print "The following test left new threads behind:"
   6.308 +            print test
   6.309 +            print "New thread(s):", new_threads
   6.310 +
   6.311 +    def print_times(self, stream, count=None):
   6.312 +        results = self._testtimes.items()
   6.313 +        results.sort(lambda x, y: cmp(y[1], x[1]))
   6.314 +        if count:
   6.315 +            n = min(count, len(results))
   6.316 +            if n:
   6.317 +                print >>stream, "Top %d longest tests:" % n
   6.318 +        else:
   6.319 +            n = len(results)
   6.320 +        if not n:
   6.321 +            return
   6.322 +        for i in range(n):
   6.323 +            print >>stream, "%6dms" % int(results[i][1] * 1000), results[i][0]
   6.324 +
   6.325 +    def _print_traceback(self, msg, err, test, errlist):
   6.326 +        if self.showAll or self.dots or self._progress:
   6.327 +            self.stream.writeln("\n")
   6.328 +            self._lastWidth = 0
   6.329 +
   6.330 +        tb = "".join(traceback.format_exception(*err))
   6.331 +        self.stream.writeln(msg)
   6.332 +        self.stream.writeln(tb)
   6.333 +        errlist.append((test, tb))
   6.334 +
   6.335 +    def startTest(self, test):
   6.336 +        if self._progress:
   6.337 +            self.stream.write("\r%4d" % (self.testsRun + 1))
   6.338 +            if self.count:
   6.339 +                self.stream.write("/%d (%5.1f%%)" % (self.count,
   6.340 +                                  (self.testsRun + 1) * 100.0 / self.count))
   6.341 +            if self.showAll:
   6.342 +                self.stream.write(": ")
   6.343 +            elif self._progressWithNames:
   6.344 +                # XXX will break with multibyte strings
   6.345 +                name = self.getShortDescription(test)
   6.346 +                width = len(name)
   6.347 +                if width < self._lastWidth:
   6.348 +                    name += " " * (self._lastWidth - width)
   6.349 +                self.stream.write(": %s" % name)
   6.350 +                self._lastWidth = width
   6.351 +            self.stream.flush()
   6.352 +        self._threads = threading.enumerate()
   6.353 +        self.__super_startTest(test)
   6.354 +        self._testtimes[test] = time.time()
   6.355 +
   6.356 +    def getShortDescription(self, test):
   6.357 +        s = self.getDescription(test)
   6.358 +        if len(s) > self._maxWidth:
   6.359 +            pos = s.find(" (")
   6.360 +            if pos >= 0:
   6.361 +                w = self._maxWidth - (pos + 5)
   6.362 +                if w < 1:
   6.363 +                    # first portion (test method name) is too long
   6.364 +                    s = s[:self._maxWidth-3] + "..."
   6.365 +                else:
   6.366 +                    pre = s[:pos+2]
   6.367 +                    post = s[-w:]
   6.368 +                    s = "%s...%s" % (pre, post)
   6.369 +        return s[:self._maxWidth]
   6.370 +
   6.371 +    def addError(self, test, err):
   6.372 +        if self._progress:
   6.373 +            self.stream.write("\r")
   6.374 +        if self._debug:
   6.375 +            raise err[0], err[1], err[2]
   6.376 +        self._print_traceback("Error in test %s" % test, err,
   6.377 +                              test, self.errors)
   6.378 +
   6.379 +    def addFailure(self, test, err):
   6.380 +        if self._progress:
   6.381 +            self.stream.write("\r")
   6.382 +        if self._debug:
   6.383 +            raise err[0], err[1], err[2]
   6.384 +        self._print_traceback("Failure in test %s" % test, err,
   6.385 +                              test, self.failures)
   6.386 +
   6.387 +    def printErrors(self):
   6.388 +        if self._progress and not (self.dots or self.showAll):
   6.389 +            self.stream.writeln()
   6.390 +        self.__super_printErrors()
   6.391 +
   6.392 +    def printErrorList(self, flavor, errors):
   6.393 +        for test, err in errors:
   6.394 +            self.stream.writeln(self.separator1)
   6.395 +            self.stream.writeln("%s: %s" % (flavor, self.getDescription(test)))
   6.396 +            self.stream.writeln(self.separator2)
   6.397 +            self.stream.writeln(err)
   6.398 +
   6.399 +
   6.400 +class ImmediateTestRunner(unittest.TextTestRunner):
   6.401 +
   6.402 +    __super_init = unittest.TextTestRunner.__init__
   6.403 +
   6.404 +    def __init__(self, **kwarg):
   6.405 +        debug = kwarg.get("debug")
   6.406 +        if debug is not None:
   6.407 +            del kwarg["debug"]
   6.408 +        progress = kwarg.get("progress")
   6.409 +        if progress is not None:
   6.410 +            del kwarg["progress"]
   6.411 +        profile = kwarg.get("profile")
   6.412 +        if profile is not None:
   6.413 +            del kwarg["profile"]
   6.414 +        self.__super_init(**kwarg)
   6.415 +        self._debug = debug
   6.416 +        self._progress = progress
   6.417 +        self._profile = profile
   6.418 +        # Create the test result here, so that we can add errors if
   6.419 +        # the test suite search process has problems.  The count
   6.420 +        # attribute must be set in run(), because we won't know the
   6.421 +        # count until all test suites have been found.
   6.422 +        self.result = ImmediateTestResult(
   6.423 +            self.stream, self.descriptions, self.verbosity, debug=self._debug,
   6.424 +            progress=self._progress)
   6.425 +
   6.426 +    def _makeResult(self):
   6.427 +        # Needed base class run method.
   6.428 +        return self.result
   6.429 +
   6.430 +    def run(self, test):
   6.431 +        self.result.count = test.countTestCases()
   6.432 +        if self._debug:
   6.433 +            club_debug(test)
   6.434 +        if self._profile:
   6.435 +            prof = hotshot.Profile("tests_profile.prof")
   6.436 +            args = (self, test)
   6.437 +            r = prof.runcall(unittest.TextTestRunner.run, *args)
   6.438 +            prof.close()
   6.439 +            stats = hotshot.stats.load("tests_profile.prof")
   6.440 +            stats.sort_stats('cumulative', 'calls')
   6.441 +            stats.print_stats(50)
   6.442 +            return r
   6.443 +        return unittest.TextTestRunner.run(self, test)
   6.444 +
   6.445 +def club_debug(test):
   6.446 +    # Beat a debug flag into debug-aware test cases
   6.447 +    setDebugModeOn = getattr(test, 'setDebugModeOn', None)
   6.448 +    if setDebugModeOn is not None:
   6.449 +        setDebugModeOn()
   6.450 +
   6.451 +    for subtest in getattr(test, '_tests', ()):
   6.452 +        club_debug(subtest)
   6.453 +
   6.454 +# setup list of directories to put on the path
   6.455 +class PathInit:
   6.456 +    def __init__(self, build, build_inplace, libdir=None):
   6.457 +        self.inplace = None
   6.458 +        # Figure out if we should test in-place or test in-build.  If the -b
   6.459 +        # or -B option was given, test in the place we were told to build in.
   6.460 +        # Otherwise, we'll look for a build directory and if we find one,
   6.461 +        # we'll test there, otherwise we'll test in-place.
   6.462 +        if build:
   6.463 +            self.inplace = build_inplace
   6.464 +        if self.inplace is None:
   6.465 +            # Need to figure it out
   6.466 +            if os.path.isdir(os.path.join("build", "lib.%s" % PLAT_SPEC)):
   6.467 +                self.inplace = False
   6.468 +            else:
   6.469 +                self.inplace = True
   6.470 +        # Calculate which directories we're going to add to sys.path, and cd
   6.471 +        # to the appropriate working directory
   6.472 +        self.org_cwd = os.getcwd()
   6.473 +        if self.inplace:
   6.474 +            self.libdir = "src"
   6.475 +        else:
   6.476 +            self.libdir = "lib.%s" % PLAT_SPEC
   6.477 +            os.chdir("build")
   6.478 +        # Hack sys.path
   6.479 +        self.cwd = os.getcwd()
   6.480 +        sys.path.insert(0, os.path.join(self.cwd, self.libdir))
   6.481 +        # Hack again for external products.
   6.482 +        global functional
   6.483 +        kind = functional and "FUNCTIONAL" or "UNIT"
   6.484 +        if libdir:
   6.485 +            extra = os.path.join(self.org_cwd, libdir)
   6.486 +            print "Running %s tests from %s" % (kind, extra)
   6.487 +            self.libdir = extra
   6.488 +            sys.path.insert(0, extra)
   6.489 +        else:
   6.490 +            print "Running %s tests from %s" % (kind, self.cwd)
   6.491 +        # Make sure functional tests find ftesting.zcml
   6.492 +        if functional:
   6.493 +            config_file = 'ftesting.zcml'
   6.494 +            if not self.inplace:
   6.495 +                # We chdired into build, so ftesting.zcml is in the
   6.496 +                # parent directory
   6.497 +                config_file = os.path.join('..', 'ftesting.zcml')
   6.498 +            print "Parsing %s" % config_file
   6.499 +            from zope.app.tests.functional import FunctionalTestSetup
   6.500 +            FunctionalTestSetup(config_file)
   6.501 +
   6.502 +def match(rx, s):
   6.503 +    if not rx:
   6.504 +        return True
   6.505 +    if rx[0] == "!":
   6.506 +        return re.search(rx[1:], s) is None
   6.507 +    else:
   6.508 +        return re.search(rx, s) is not None
   6.509 +
   6.510 +class TestFileFinder:
   6.511 +    def __init__(self, prefix):
   6.512 +        self.files = []
   6.513 +        self._plen = len(prefix)
   6.514 +        if not prefix.endswith(os.sep):
   6.515 +            self._plen += 1
   6.516 +        global functional
   6.517 +        if functional:
   6.518 +            self.dirname = "ftests"
   6.519 +        else:
   6.520 +            self.dirname = "tests"
   6.521 +
   6.522 +    def visit(self, rx, dir, files):
   6.523 +        if os.path.split(dir)[1] != self.dirname:
   6.524 +            # Allow tests/ftests module rather than package.
   6.525 +            modfname = self.dirname + '.py'
   6.526 +            if modfname in files:
   6.527 +                path = os.path.join(dir, modfname)
   6.528 +                if match(rx, path):
   6.529 +                    self.files.append(path)
   6.530 +                    return
   6.531 +            return
   6.532 +        # ignore tests that aren't in packages
   6.533 +        if not "__init__.py" in files:
   6.534 +            if not files or files == ["CVS"]:
   6.535 +                return
   6.536 +            print "not a package", dir
   6.537 +            return
   6.538 +
   6.539 +        # Put matching files in matches.  If matches is non-empty,
   6.540 +        # then make sure that the package is importable.
   6.541 +        matches = []
   6.542 +        for file in files:
   6.543 +            if file.startswith('test') and os.path.splitext(file)[-1] == '.py':
   6.544 +                path = os.path.join(dir, file)
   6.545 +                if match(rx, path):
   6.546 +                    matches.append(path)
   6.547 +
   6.548 +        # ignore tests when the package can't be imported, possibly due to
   6.549 +        # dependency failures.
   6.550 +        pkg = dir[self._plen:].replace(os.sep, '.')
   6.551 +        try:
   6.552 +            __import__(pkg)
   6.553 +        # We specifically do not want to catch ImportError since that's useful
   6.554 +        # information to know when running the tests.
   6.555 +        except RuntimeError, e:
   6.556 +            if VERBOSE:
   6.557 +                print "skipping %s because: %s" % (pkg, e)
   6.558 +            return
   6.559 +        else:
   6.560 +            self.files.extend(matches)
   6.561 +
   6.562 +    def module_from_path(self, path):
   6.563 +        """Return the Python package name indicated by the filesystem path."""
   6.564 +        assert path.endswith(".py")
   6.565 +        path = path[self._plen:-3]
   6.566 +        mod = path.replace(os.sep, ".")
   6.567 +        return mod
   6.568 +
   6.569 +def walk_with_symlinks(top, func, arg):
   6.570 +    """Like os.path.walk, but follows symlinks on POSIX systems.
   6.571 +
   6.572 +    This could theoreticaly result in an infinite loop, if you create symlink
   6.573 +    cycles in your Zope sandbox, so don't do that.
   6.574 +    """
   6.575 +    try:
   6.576 +        names = os.listdir(top)
   6.577 +    except os.error:
   6.578 +        return
   6.579 +    func(arg, top, names)
   6.580 +    exceptions = ('.', '..')
   6.581 +    for name in names:
   6.582 +        if name not in exceptions:
   6.583 +            name = os.path.join(top, name)
   6.584 +            if os.path.isdir(name):
   6.585 +                walk_with_symlinks(name, func, arg)
   6.586 +
   6.587 +def find_test_dir(dir):
   6.588 +    if os.path.exists(dir):
   6.589 +        return dir
   6.590 +    d = os.path.join(pathinit.libdir, dir)
   6.591 +    if os.path.exists(d):
   6.592 +        if os.path.isdir(d):
   6.593 +            return d
   6.594 +        raise ValueError("%s does not exist and %s is not a directory"
   6.595 +                         % (dir, d))
   6.596 +    raise ValueError("%s does not exist!" % dir)
   6.597 +
   6.598 +def find_tests(rx):
   6.599 +    global finder
   6.600 +    finder = TestFileFinder(pathinit.libdir)
   6.601 +
   6.602 +    if TEST_DIRS:
   6.603 +        for d in TEST_DIRS:
   6.604 +            d = find_test_dir(d)
   6.605 +            walk_with_symlinks(d, finder.visit, rx)
   6.606 +    else:
   6.607 +        walk_with_symlinks(pathinit.libdir, finder.visit, rx)
   6.608 +    return finder.files
   6.609 +
   6.610 +def package_import(modname):
   6.611 +    mod = __import__(modname)
   6.612 +    for part in modname.split(".")[1:]:
   6.613 +        mod = getattr(mod, part)
   6.614 +    return mod
   6.615 +
   6.616 +class PseudoTestCase:
   6.617 +    """Minimal test case objects to create error reports.
   6.618 +
   6.619 +    If test.py finds something that looks like it should be a test but
   6.620 +    can't load it or find its test suite, it will report an error
   6.621 +    using a PseudoTestCase.
   6.622 +    """
   6.623 +
   6.624 +    def __init__(self, name, descr=None):
   6.625 +        self.name = name
   6.626 +        self.descr = descr
   6.627 +
   6.628 +    def shortDescription(self):
   6.629 +        return self.descr
   6.630 +
   6.631 +    def __str__(self):
   6.632 +        return "Invalid Test (%s)" % self.name
   6.633 +
   6.634 +def get_suite(file, result):
   6.635 +    modname = finder.module_from_path(file)
   6.636 +    try:
   6.637 +        mod = package_import(modname)
   6.638 +        return mod.test_suite()
   6.639 +    except:
   6.640 +        result.addError(PseudoTestCase(modname), sys.exc_info())
   6.641 +        return None
   6.642 +
   6.643 +def filter_testcases(s, rx):
   6.644 +    new = unittest.TestSuite()
   6.645 +    for test in s._tests:
   6.646 +        # See if the levels match
   6.647 +        dolevel = (LEVEL == 0) or LEVEL >= getattr(test, "level", 0)
   6.648 +        if not dolevel:
   6.649 +            continue
   6.650 +        if isinstance(test, unittest.TestCase):
   6.651 +            name = test.id() # Full test name: package.module.class.method
   6.652 +            name = name[1 + name.rfind("."):] # extract method name
   6.653 +            if not rx or match(rx, name):
   6.654 +                new.addTest(test)
   6.655 +        else:
   6.656 +            filtered = filter_testcases(test, rx)
   6.657 +            if filtered:
   6.658 +                new.addTest(filtered)
   6.659 +    return new
   6.660 +
   6.661 +def gui_runner(files, test_filter):
   6.662 +    if BUILD_INPLACE:
   6.663 +        utildir = os.path.join(os.getcwd(), "utilities")
   6.664 +    else:
   6.665 +        utildir = os.path.join(os.getcwd(), "..", "utilities")
   6.666 +    sys.path.append(utildir)
   6.667 +    import unittestgui
   6.668 +    suites = []
   6.669 +    for file in files:
   6.670 +        suites.append(finder.module_from_path(file) + ".test_suite")
   6.671 +
   6.672 +    suites = ", ".join(suites)
   6.673 +    minimal = (GUI == "minimal")
   6.674 +    unittestgui.main(suites, minimal)
   6.675 +
   6.676 +class TrackRefs:
   6.677 +    """Object to track reference counts across test runs."""
   6.678 +
   6.679 +    def __init__(self):
   6.680 +        self.type2count = {}
   6.681 +        self.type2all = {}
   6.682 +
   6.683 +    def update(self):
   6.684 +        obs = sys.getobjects(0)
   6.685 +        type2count = {}
   6.686 +        type2all = {}
   6.687 +        for o in obs:
   6.688 +            all = sys.getrefcount(o)
   6.689 +
   6.690 +            if type(o) is str and o == '<dummy key>':
   6.691 +                # avoid dictionary madness
   6.692 +                continue
   6.693 +            t = type(o)
   6.694 +            if t in type2count:
   6.695 +                type2count[t] += 1
   6.696 +                type2all[t] += all
   6.697 +            else:
   6.698 +                type2count[t] = 1
   6.699 +                type2all[t] = all
   6.700 +
   6.701 +        ct = [(type2count[t] - self.type2count.get(t, 0),
   6.702 +               type2all[t] - self.type2all.get(t, 0),
   6.703 +               t)
   6.704 +              for t in type2count.iterkeys()]
   6.705 +        ct.sort()
   6.706 +        ct.reverse()
   6.707 +        printed = False
   6.708 +        for delta1, delta2, t in ct:
   6.709 +            if delta1 or delta2:
   6.710 +                if not printed:
   6.711 +                    print "%-55s %8s %8s" % ('', 'insts', 'refs')
   6.712 +                    printed = True
   6.713 +                print "%-55s %8d %8d" % (t, delta1, delta2)
   6.714 +
   6.715 +        self.type2count = type2count
   6.716 +        self.type2all = type2all
   6.717 +
   6.718 +def runner(files, test_filter, debug):
   6.719 +    runner = ImmediateTestRunner(verbosity=VERBOSE, debug=DEBUG,
   6.720 +                                 progress=PROGRESS, profile=PROFILE,
   6.721 +                                 descriptions=False)
   6.722 +    suite = unittest.TestSuite()
   6.723 +    for file in files:
   6.724 +        s = get_suite(file, runner.result)
   6.725 +        # See if the levels match
   6.726 +        dolevel = (LEVEL == 0) or LEVEL >= getattr(s, "level", 0)
   6.727 +        if s is not None and dolevel:
   6.728 +            s = filter_testcases(s, test_filter)
   6.729 +            suite.addTest(s)
   6.730 +    try:
   6.731 +        r = runner.run(suite)
   6.732 +        if TIMESFN:
   6.733 +            r.print_times(open(TIMESFN, "w"))
   6.734 +            if VERBOSE:
   6.735 +                print "Wrote timing data to", TIMESFN
   6.736 +        if TIMETESTS:
   6.737 +            r.print_times(sys.stdout, TIMETESTS)
   6.738 +    except:
   6.739 +        if DEBUGGER:
   6.740 +            print "%s:" % (sys.exc_info()[0], )
   6.741 +            print sys.exc_info()[1]
   6.742 +            pdb.post_mortem(sys.exc_info()[2])
   6.743 +        else:
   6.744 +            raise
   6.745 +
   6.746 +def remove_stale_bytecode(arg, dirname, names):
   6.747 +    names = map(os.path.normcase, names)
   6.748 +    for name in names:
   6.749 +        if name.endswith(".pyc") or name.endswith(".pyo"):
   6.750 +            srcname = name[:-1]
   6.751 +            if srcname not in names:
   6.752 +                fullname = os.path.join(dirname, name)
   6.753 +                print "Removing stale bytecode file", fullname
   6.754 +                os.unlink(fullname)
   6.755 +
   6.756 +def main(module_filter, test_filter, libdir):
   6.757 +    if not KEEP_STALE_BYTECODE:
   6.758 +        os.path.walk(os.curdir, remove_stale_bytecode, None)
   6.759 +
   6.760 +    configure_logging()
   6.761 +
   6.762 +    # Initialize the path and cwd
   6.763 +    global pathinit
   6.764 +    pathinit = PathInit(BUILD, BUILD_INPLACE, libdir)
   6.765 +
   6.766 +    files = find_tests(module_filter)
   6.767 +    files.sort()
   6.768 +
   6.769 +    if GUI:
   6.770 +        gui_runner(files, test_filter)
   6.771 +    elif LOOP:
   6.772 +        if REFCOUNT:
   6.773 +            rc = sys.gettotalrefcount()
   6.774 +            track = TrackRefs()
   6.775 +        while True:
   6.776 +            runner(files, test_filter, DEBUG)
   6.777 +            gc.collect()
   6.778 +            if gc.garbage:
   6.779 +                print "GARBAGE:", len(gc.garbage), gc.garbage
   6.780 +                return
   6.781 +            if REFCOUNT:
   6.782 +                prev = rc
   6.783 +                rc = sys.gettotalrefcount()
   6.784 +                print "totalrefcount=%-8d change=%-6d" % (rc, rc - prev)
   6.785 +                track.update()
   6.786 +    else:
   6.787 +        runner(files, test_filter, DEBUG)
   6.788 +
   6.789 +    os.chdir(pathinit.org_cwd)
   6.790 +
   6.791 +
   6.792 +def configure_logging():
   6.793 +    """Initialize the logging module."""
   6.794 +    import logging.config
   6.795 +
   6.796 +    # Get the log.ini file from the current directory instead of possibly
   6.797 +    # buried in the build directory.  XXX This isn't perfect because if
   6.798 +    # log.ini specifies a log file, it'll be relative to the build directory.
   6.799 +    # Hmm...
   6.800 +    logini = os.path.abspath("log.ini")
   6.801 +
   6.802 +    if os.path.exists(logini):
   6.803 +        logging.config.fileConfig(logini)
   6.804 +    else:
   6.805 +        logging.basicConfig()
   6.806 +
   6.807 +    if os.environ.has_key("LOGGING"):
   6.808 +        level = int(os.environ["LOGGING"])
   6.809 +        logging.getLogger().setLevel(level)
   6.810 +
   6.811 +
   6.812 +def process_args(argv=None):
   6.813 +    import getopt
   6.814 +    global MODULE_FILTER
   6.815 +    global TEST_FILTER
   6.816 +    global VERBOSE
   6.817 +    global LOOP
   6.818 +    global GUI
   6.819 +    global TRACE
   6.820 +    global REFCOUNT
   6.821 +    global DEBUG
   6.822 +    global DEBUGGER
   6.823 +    global BUILD
   6.824 +    global LEVEL
   6.825 +    global LIBDIR
   6.826 +    global TIMESFN
   6.827 +    global TIMETESTS
   6.828 +    global PROGRESS
   6.829 +    global BUILD_INPLACE
   6.830 +    global KEEP_STALE_BYTECODE
   6.831 +    global TEST_DIRS
   6.832 +    global PROFILE
   6.833 +    global GC_THRESHOLD
   6.834 +    global GC_FLAGS
   6.835 +    global RUN_UNIT
   6.836 +    global RUN_FUNCTIONAL
   6.837 +    global PYCHECKER
   6.838 +
   6.839 +    if argv is None:
   6.840 +        argv = sys.argv
   6.841 +
   6.842 +    MODULE_FILTER = None
   6.843 +    TEST_FILTER = None
   6.844 +    VERBOSE = 0
   6.845 +    LOOP = False
   6.846 +    GUI = False
   6.847 +    TRACE = False
   6.848 +    REFCOUNT = False
   6.849 +    DEBUG = False # Don't collect test results; simply let tests crash
   6.850 +    DEBUGGER = False
   6.851 +    BUILD = False
   6.852 +    BUILD_INPLACE = False
   6.853 +    GC_THRESHOLD = None
   6.854 +    gcdebug = 0
   6.855 +    GC_FLAGS = []
   6.856 +    LEVEL = 1
   6.857 +    LIBDIR = None
   6.858 +    PROGRESS = False
   6.859 +    TIMESFN = None
   6.860 +    TIMETESTS = 0
   6.861 +    KEEP_STALE_BYTECODE = 0
   6.862 +    RUN_UNIT = True
   6.863 +    RUN_FUNCTIONAL = True
   6.864 +    TEST_DIRS = []
   6.865 +    PROFILE = False
   6.866 +    PYCHECKER = False
   6.867 +    config_filename = 'test.config'
   6.868 +
   6.869 +    # import the config file
   6.870 +    if os.path.isfile(config_filename):
   6.871 +        print 'Configuration file found.'
   6.872 +        execfile(config_filename, globals())
   6.873 +
   6.874 +
   6.875 +    try:
   6.876 +        opts, args = getopt.getopt(argv[1:], "a:bBcdDfFg:G:hkl:LmMPprs:tTuUv",
   6.877 +                                   ["all", "help", "libdir=", "times=",
   6.878 +                                    "keepbytecode", "dir=", "build",
   6.879 +                                    "build-inplace",
   6.880 +                                    "at-level=",
   6.881 +                                    "pychecker", "debug", "pdebug",
   6.882 +                                    "gc-threshold=", "gc-option=",
   6.883 +                                    "loop", "gui", "minimal-gui",
   6.884 +                                    "profile", "progress", "refcount", "trace",
   6.885 +                                    "top-fifty", "verbose",
   6.886 +                                    ])
   6.887 +    # fixme: add the long names
   6.888 +    # fixme: add the extra documentation
   6.889 +    # fixme: test for functional first!
   6.890 +    except getopt.error, msg:
   6.891 +        print msg
   6.892 +        print "Try `python %s -h' for more information." % argv[0]
   6.893 +        sys.exit(2)
   6.894 +
   6.895 +    for k, v in opts:
   6.896 +        if k in ("-a", "--at-level"):
   6.897 +            LEVEL = int(v)
   6.898 +        elif k == "--all":
   6.899 +            LEVEL = 0
   6.900 +            os.environ["COMPLAIN_IF_TESTS_MISSED"]='1'
   6.901 +        elif k in ("-b", "--build"):
   6.902 +            BUILD = True
   6.903 +        elif k in ("-B", "--build-inplace"):
   6.904 +            BUILD = BUILD_INPLACE = True
   6.905 +        elif k in("-c", "--pychecker"):
   6.906 +            PYCHECKER = True
   6.907 +        elif k in ("-d", "--debug"):
   6.908 +            DEBUG = True
   6.909 +        elif k in ("-D", "--pdebug"):
   6.910 +            DEBUG = True
   6.911 +            DEBUGGER = True
   6.912 +        elif k in ("-f", "--skip-unit"):
   6.913 +            RUN_UNIT = False
   6.914 +        elif k in ("-u", "--skip-functional"):
   6.915 +            RUN_FUNCTIONAL = False
   6.916 +        elif k == "-F":
   6.917 +            message = 'Unit plus functional is the default behaviour.'
   6.918 +            warnings.warn(message, DeprecationWarning)
   6.919 +            RUN_UNIT = True
   6.920 +            RUN_FUNCTIONAL = True
   6.921 +        elif k in ("-h", "--help"):
   6.922 +            print __doc__
   6.923 +            sys.exit(0)
   6.924 +        elif k in ("-g", "--gc-threshold"):
   6.925 +            GC_THRESHOLD = int(v)
   6.926 +        elif k in ("-G", "--gc-option"):
   6.927 +            if not v.startswith("DEBUG_"):
   6.928 +                print "-G argument must be DEBUG_ flag, not", repr(v)
   6.929 +                sys.exit(1)
   6.930 +            GC_FLAGS.append(v)
   6.931 +        elif k in ('-k', '--keepbytecode'):
   6.932 +            KEEP_STALE_BYTECODE = 1
   6.933 +        elif k in ('-l', '--libdir'):
   6.934 +            LIBDIR = v
   6.935 +        elif k in ("-L", "--loop"):
   6.936 +            LOOP = 1
   6.937 +        elif k == "-m":
   6.938 +            GUI = "minimal"
   6.939 +            msg = "Use -M or --minimal-gui instead of -m."
   6.940 +            warnings.warn(msg, DeprecationWarning)
   6.941 +        elif k in ("-M", "--minimal-gui"):
   6.942 +            GUI = "minimal"
   6.943 +        elif k in ("-P", "--profile"):
   6.944 +            PROFILE = True
   6.945 +        elif k in ("-p", "--progress"):
   6.946 +            PROGRESS = True
   6.947 +        elif k in ("-r", "--refcount"):
   6.948 +                REFCOUNT = True
   6.949 +        elif k in ("-T", "--trace"):
   6.950 +            TRACE = True
   6.951 +        elif k in ("-t", "--top-fifty"):
   6.952 +            if not TIMETESTS:
   6.953 +                TIMETESTS = 50
   6.954 +        elif k in ("-u", "--gui"):
   6.955 +            GUI = 1
   6.956 +        elif k in ("-v", "--verbose"):
   6.957 +            VERBOSE += 1
   6.958 +        elif k == "--times":
   6.959 +            try:
   6.960 +                TIMETESTS = int(v)
   6.961 +            except ValueError:
   6.962 +                # must be a filename to write
   6.963 +                TIMESFN = v
   6.964 +        elif k in ('-s', '--dir'):
   6.965 +            TEST_DIRS.append(v)
   6.966 +
   6.967 +    if PYCHECKER:
   6.968 +        # make sure you have a recent version of pychecker
   6.969 +        if not os.environ.get("PYCHECKER"):
   6.970 +            os.environ["PYCHECKER"] = "-q"
   6.971 +        import pychecker.checker
   6.972 +
   6.973 +    if REFCOUNT and not hasattr(sys, "gettotalrefcount"):
   6.974 +        print "-r ignored, because it needs a debug build of Python"
   6.975 +        REFCOUNT = False
   6.976 +
   6.977 +    if sys.version_info < ( 2,3,2 ):
   6.978 +        print """\
   6.979 +        ERROR: Your python version is not supported by Zope3.
   6.980 +        Zope3 needs Python 2.3.2 or greater. You are running:""" + sys.version
   6.981 +        sys.exit(1)
   6.982 +
   6.983 +    if GC_THRESHOLD is not None:
   6.984 +        if GC_THRESHOLD == 0:
   6.985 +            gc.disable()
   6.986 +            print "gc disabled"
   6.987 +        else:
   6.988 +            gc.set_threshold(GC_THRESHOLD)
   6.989 +            print "gc threshold:", gc.get_threshold()
   6.990 +
   6.991 +    if GC_FLAGS:
   6.992 +        val = 0
   6.993 +        for flag in GC_FLAGS:
   6.994 +            v = getattr(gc, flag, None)
   6.995 +            if v is None:
   6.996 +                print "Unknown gc flag", repr(flag)
   6.997 +                print gc.set_debug.__doc__
   6.998 +                sys.exit(1)
   6.999 +            val |= v
  6.1000 +        gcdebug |= v
  6.1001 +
  6.1002 +    if gcdebug:
  6.1003 +        gc.set_debug(gcdebug)
  6.1004 +
  6.1005 +    if BUILD:
  6.1006 +        # Python 2.3 is more sane in its non -q output
  6.1007 +        if sys.hexversion >= 0x02030000:
  6.1008 +            qflag = ""
  6.1009 +        else:
  6.1010 +            qflag = "-q"
  6.1011 +        cmd = sys.executable + " setup.py " + qflag + " build"
  6.1012 +        if BUILD_INPLACE:
  6.1013 +            cmd += "_ext -i"
  6.1014 +        if VERBOSE:
  6.1015 +            print cmd
  6.1016 +        sts = os.system(cmd)
  6.1017 +        if sts:
  6.1018 +            print "Build failed", hex(sts)
  6.1019 +            sys.exit(1)
  6.1020 +
  6.1021 +    k = []
  6.1022 +    if RUN_UNIT:
  6.1023 +        k.append(False)
  6.1024 +    if RUN_FUNCTIONAL:
  6.1025 +        k.append(True)
  6.1026 +
  6.1027 +    global functional
  6.1028 +    for functional in k:
  6.1029 +
  6.1030 +        if VERBOSE:
  6.1031 +            kind = functional and "FUNCTIONAL" or "UNIT"
  6.1032 +            if LEVEL == 0:
  6.1033 +                print "Running %s tests at all levels" % kind
  6.1034 +            else:
  6.1035 +                print "Running %s tests at level %d" % (kind, LEVEL)
  6.1036 +
  6.1037 +# This was to avoid functional tests outside of z3, but this doesn't really
  6.1038 +# work right.
  6.1039 +##         if functional:
  6.1040 +##             try:
  6.1041 +##                 from zope.app.tests.functional import FunctionalTestSetup
  6.1042 +##             except ImportError:
  6.1043 +##                 raise
  6.1044 +##                 print ('Skipping functional tests: could not import '
  6.1045 +##                        'zope.app.tests.functional')
  6.1046 +##                 continue
  6.1047 +
  6.1048 +        # XXX We want to change *visible* warnings into errors.  The next
  6.1049 +        # line changes all warnings into errors, including warnings we
  6.1050 +        # normally never see.  In particular, test_datetime does some
  6.1051 +        # short-integer arithmetic that overflows to long ints, and, by
  6.1052 +        # default, Python doesn't display the overflow warning that can
  6.1053 +        # be enabled when this happens.  The next line turns that into an
  6.1054 +        # error instead.  Guido suggests that a better to get what we're
  6.1055 +        # after is to replace warnings.showwarning() with our own thing
  6.1056 +        # that raises an error.
  6.1057 +        ## warnings.filterwarnings("error")
  6.1058 +        warnings.filterwarnings("ignore", module="logging")
  6.1059 +
  6.1060 +        if args:
  6.1061 +            if len(args) > 1:
  6.1062 +                TEST_FILTER = args[1]
  6.1063 +            MODULE_FILTER = args[0]
  6.1064 +        try:
  6.1065 +            if TRACE:
  6.1066 +                # if the trace module is used, then we don't exit with
  6.1067 +                # status if on a false return value from main.
  6.1068 +                coverdir = os.path.join(os.getcwd(), "coverage")
  6.1069 +                import trace
  6.1070 +                ignoremods = ["os", "posixpath", "stat"]
  6.1071 +                tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix],
  6.1072 +                                     ignoremods=ignoremods,
  6.1073 +                                     trace=False, count=True)
  6.1074 +
  6.1075 +                tracer.runctx("main(MODULE_FILTER, TEST_FILTER, LIBDIR)",
  6.1076 +                              globals=globals(), locals=vars())
  6.1077 +                r = tracer.results()
  6.1078 +                path = "/tmp/trace.%s" % os.getpid()
  6.1079 +                import cPickle
  6.1080 +                f = open(path, "wb")
  6.1081 +                cPickle.dump(r, f)
  6.1082 +                f.close()
  6.1083 +                print path
  6.1084 +                r.write_results(show_missing=True,
  6.1085 +                                summary=True, coverdir=coverdir)
  6.1086 +            else:
  6.1087 +                bad = main(MODULE_FILTER, TEST_FILTER, LIBDIR)
  6.1088 +                if bad:
  6.1089 +                    sys.exit(1)
  6.1090 +        except ImportError, err:
  6.1091 +            print err
  6.1092 +            print sys.path
  6.1093 +            raise
  6.1094 +
  6.1095 +
  6.1096 +if __name__ == "__main__":
  6.1097 +    process_args()
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/tools/python/xen/xend/tests/test_sxp.py	Thu Oct 06 15:12:31 2005 +0100
     7.3 @@ -0,0 +1,18 @@
     7.4 +import unittest
     7.5 +
     7.6 +import xen.xend.sxp
     7.7 +
     7.8 +
     7.9 +class test_sxp(unittest.TestCase):
    7.10 +
    7.11 +    def testAllFromString(self):
    7.12 +        def t(input, expected):
    7.13 +            self.assertEqual(xen.xend.sxp.all_from_string(input), expected)
    7.14 +
    7.15 +        t('String',           ['String'])
    7.16 +        t('(String Thing)',   [['String', 'Thing']])
    7.17 +        t('(String) (Thing)', [['String'], ['Thing']])
    7.18 +
    7.19 +
    7.20 +def test_suite():
    7.21 +    return unittest.makeSuite(test_sxp)