SF.net SVN: jython:[7215] trunk/jython/Lib/test/test_support.py
[email protected] Sat, 12 Mar 2011 18:46:20 +0000
| Newsgroups | gmane.comp.lang.jython.cvs |
|---|---|
| Message-ID | <[email protected]> |
Revision: 7215
http://jython.svn.sourceforge.net/jython/?rev=7215&view=rev
Author: fwierzbicki
Date: 2011-03-12 18:46:20 +0000 (Sat, 12 Mar 2011)
Log Message:
-----------
from:
http://svn.python.org/projects/python/branches/release26-maint/Lib/test/test_support.py@88766
Modified Paths:
--------------
trunk/jython/Lib/test/test_support.py
Modified: trunk/jython/Lib/test/test_support.py
===================================================================
--- trunk/jython/Lib/test/test_support.py 2011-03-12 18:41:56 UTC (rev 7214)
+++ trunk/jython/Lib/test/test_support.py 2011-03-12 18:46:20 UTC (rev 7215)
@@ -1,11 +1,31 @@
"""Supporting definitions for the Python regression tests."""
if __name__ != 'test.test_support':
- raise ImportError, 'test_support must be imported from the test package'
+ raise ImportError('test_support must be imported from the test package')
+import contextlib
+import errno
+import socket
import sys
-import time
+import os
+import shutil
+import warnings
+import unittest
+import re
+__all__ = ["Error", "TestFailed", "TestSkipped", "ResourceDenied", "import_module",
+ "verbose", "use_resources", "max_memuse", "record_original_stdout",
+ "get_original_stdout", "unload", "unlink", "rmtree", "forget",
+ "is_resource_enabled", "requires", "find_unused_port", "bind_port",
+ "fcmp", "have_unicode", "is_jython", "TESTFN", "HOST", "FUZZ",
+ "findfile", "verify", "vereq", "sortdict", "check_syntax_error",
+ "open_urlresource", "check_warnings", "_check_py3k_warnings",
+ "CleanImport", "EnvironmentVarGuard", "captured_output",
+ "captured_stdout", "TransientResource", "transient_internet",
+ "run_with_locale", "set_memlimit", "bigmemtest", "bigaddrspacetest",
+ "BasicTestRunner", "run_unittest", "run_doctest", "threading_setup",
+ "threading_cleanup", "reap_children"]
+
class Error(Exception):
"""Base class for regression test exceptions."""
@@ -30,11 +50,25 @@
and unexpected skips.
"""
+def import_module(name, deprecated=False):
+ """Import the module to be tested, raising TestSkipped if it is not
+ available."""
+ with warnings.catch_warnings():
+ if deprecated:
+ warnings.filterwarnings("ignore", ".+ (module|package)",
+ DeprecationWarning)
+ try:
+ module = __import__(name, level=0)
+ except ImportError:
+ raise TestSkipped("No module named " + name)
+ else:
+ return module
+
verbose = 1 # Flag set to 0 by regrtest.py
use_resources = None # Flag set to [] by regrtest.py
-junit_xml_dir = None # Option set by regrtest.py
max_memuse = 0 # Disable bigmem tests (they will still be run with
# small sizes, to make sure they work.)
+real_max_memuse = 0
# _original_stdout is meant to hold stdout at the time regrtest began.
# This may be "the real" stdout, or IDLE's emulation of stdout, or whatever.
@@ -54,17 +88,23 @@
pass
def unlink(filename):
- import os
try:
os.unlink(filename)
except OSError:
pass
+def rmtree(path):
+ try:
+ shutil.rmtree(path)
+ except OSError, e:
+ # Unix returns ENOENT, Windows returns ESRCH.
+ if e.errno not in (errno.ENOENT, errno.ESRCH):
+ raise
+
def forget(modname):
'''"Forget" a module was ever imported by removing it from sys.modules and
deleting any .pyc and .pyo files.'''
unload(modname)
- import os
for dirname in sys.path:
unlink(os.path.join(dirname, modname + os.extsep + 'pyc'))
# Deleting the .pyo file cannot be within the 'try' for the .pyc since
@@ -91,80 +131,124 @@
msg = "Use of the `%s' resource not enabled" % resource
raise ResourceDenied(msg)
-def bind_port(sock, host='', preferred_port=54321):
- """Try to bind the sock to a port. If we are running multiple
- tests and we don't try multiple ports, the test can fails. This
- makes the test more robust."""
+HOST = 'localhost'
- import socket, errno
+def find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM):
+ """Returns an unused port that should be suitable for binding. This is
+ achieved by creating a temporary socket with the same family and type as
+ the 'sock' parameter (default is AF_INET, SOCK_STREAM), and binding it to
+ the specified host address (defaults to 0.0.0.0) with the port set to 0,
+ eliciting an unused ephemeral port from the OS. The temporary socket is
+ then closed and deleted, and the ephemeral port is returned.
- # Find some random ports that hopefully no one is listening on.
- # Ideally each test would clean up after itself and not continue listening
- # on any ports. However, this isn't the case. The last port (0) is
- # a stop-gap that asks the O/S to assign a port. Whenever the warning
- # message below is printed, the test that is listening on the port should
- # be fixed to close the socket at the end of the test.
- # Another reason why we can't use a port is another process (possibly
- # another instance of the test suite) is using the same port.
- for port in [preferred_port, 9907, 10243, 32999, 0]:
- try:
- sock.bind((host, port))
- if port == 0:
- port = sock.getsockname()[1]
- return port
- except socket.error, (err, msg):
- if err != errno.EADDRINUSE:
- raise
- print >>sys.__stderr__, \
- ' WARNING: failed to listen on port %d, trying another' % port
- raise TestFailed, 'unable to find port to listen on'
+ Either this method or bind_port() should be used for any tests where a
+ server socket needs to be bound to a particular port for the duration of
+ the test. Which one to use depends on whether the calling code is creating
+ a python socket, or if an unused port needs to be provided in a constructor
+ or passed to an external program (i.e. the -accept argument to openssl's
+ s_server mode). Always prefer bind_port() over find_unused_port() where
+ possible. Hard coded ports should *NEVER* be used. As soon as a server
+ socket is bound to a hard coded port, the ability to run multiple instances
+ of the test simultaneously on the same host is compromised, which makes the
+ test a ticking time bomb in a buildbot environment. On Unix buildbots, this
+ may simply manifest as a failed test, which can be recovered from without
+ intervention in most cases, but on Windows, the entire python process can
+ completely and utterly wedge, requiring someone to log in to the buildbot
+ and manually kill the affected process.
+ (This is easy to reproduce on Windows, unfortunately, and can be traced to
+ the SO_REUSEADDR socket option having different semantics on Windows versus
+ Unix/Linux. On Unix, you can't have two AF_INET SOCK_STREAM sockets bind,
+ listen and then accept connections on identical host/ports. An EADDRINUSE
+ socket.error will be raised at some point (depending on the platform and
+ the order bind and listen were called on each socket).
+
+ However, on Windows, if SO_REUSEADDR is set on the sockets, no EADDRINUSE
+ will ever be raised when attempting to bind two identical host/ports. When
+ accept() is called on each socket, the second caller's process will steal
+ the port from the first caller, leaving them both in an awkwardly wedged
+ state where they'll no longer respond to any signals or graceful kills, and
+ must be forcibly killed via OpenProcess()/TerminateProcess().
+
+ The solution on Windows is to use the SO_EXCLUSIVEADDRUSE socket option
+ instead of SO_REUSEADDR, which effectively affords the same semantics as
+ SO_REUSEADDR on Unix. Given the propensity of Unix developers in the Open
+ Source world compared to Windows ones, this is a common mistake. A quick
+ look over OpenSSL's 0.9.8g source shows that they use SO_REUSEADDR when
+ openssl.exe is called with the 's_server' option, for example. See
+ http://bugs.python.org/issue2550 for more info. The following site also
+ has a very thorough description about the implications of both REUSEADDR
+ and EXCLUSIVEADDRUSE on Windows:
+ http://msdn2.microsoft.com/en-us/library/ms740621(VS.85).aspx)
+
+ XXX: although this approach is a vast improvement on previous attempts to
+ elicit unused ports, it rests heavily on the assumption that the ephemeral
+ port returned to us by the OS won't immediately be dished back out to some
+ other process when we close and delete our temporary socket but before our
+ calling code has a chance to bind the returned port. We can deal with this
+ issue if/when we come across it."""
+ tempsock = socket.socket(family, socktype)
+ port = bind_port(tempsock)
+ tempsock.close()
+ del tempsock
+ return port
+
+def bind_port(sock, host=HOST):
+ """Bind the socket to a free port and return the port number. Relies on
+ ephemeral ports in order to ensure we are using an unbound port. This is
+ important as many tests may be running simultaneously, especially in a
+ buildbot environment. This method raises an exception if the sock.family
+ is AF_INET and sock.type is SOCK_STREAM, *and* the socket has SO_REUSEADDR
+ or SO_REUSEPORT set on it. Tests should *never* set these socket options
+ for TCP/IP sockets. The only case for setting these options is testing
+ multicasting via multiple UDP sockets.
+
+ Additionally, if the SO_EXCLUSIVEADDRUSE socket option is available (i.e.
+ on Windows), it will be set on the socket. This will prevent anyone else
+ from bind()'ing to our host/port for the duration of the test.
+ """
+ if sock.family == socket.AF_INET and sock.type == socket.SOCK_STREAM:
+ if hasattr(socket, 'SO_REUSEADDR'):
+ if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) == 1:
+ raise TestFailed("tests should never set the SO_REUSEADDR " \
+ "socket option on TCP/IP sockets!")
+ if hasattr(socket, 'SO_REUSEPORT'):
+ if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT) == 1:
+ raise TestFailed("tests should never set the SO_REUSEPORT " \
+ "socket option on TCP/IP sockets!")
+ if hasattr(socket, 'SO_EXCLUSIVEADDRUSE'):
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
+
+ sock.bind((host, 0))
+ port = sock.getsockname()[1]
+ return port
+
FUZZ = 1e-6
def fcmp(x, y): # fuzzy comparison function
- if type(x) == type(0.0) or type(y) == type(0.0):
+ if isinstance(x, float) or isinstance(y, float):
try:
- x, y = coerce(x, y)
fuzz = (abs(x) + abs(y)) * FUZZ
if abs(x-y) <= fuzz:
return 0
except:
pass
- elif type(x) == type(y) and type(x) in (type(()), type([])):
+ elif type(x) == type(y) and isinstance(x, (tuple, list)):
for i in range(min(len(x), len(y))):
outcome = fcmp(x[i], y[i])
if outcome != 0:
return outcome
- return cmp(len(x), len(y))
- return cmp(x, y)
+ return (len(x) > len(y)) - (len(x) < len(y))
+ return (x > y) - (x < y)
try:
unicode
- have_unicode = 1
+ have_unicode = True
except NameError:
- have_unicode = 0
+ have_unicode = False
is_jython = sys.platform.startswith('java')
-if is_jython:
- def make_jar_classloader(jar):
- import os
- from java.net import URL, URLClassLoader
- url = URL('jar:file:%s!/' % jar)
- if os._name == 'nt':
- # URLJarFiles keep a cached open file handle to the jar even
- # after this ClassLoader is GC'ed, disallowing Windows tests
- # from removing the jar file from disk when finished with it
- conn = url.openConnection()
- if conn.getDefaultUseCaches():
- # XXX: Globally turn off jar caching: this stupid
- # instance method actually toggles a static flag. Need a
- # better fix
- conn.setDefaultUseCaches(False)
-
- return URLClassLoader([url])
-
-import os
# Filename used for testing
if os.name == 'java':
# Jython disallows @ in module names
@@ -227,13 +311,12 @@
if fp is not None:
fp.close()
unlink(TESTFN)
-del os, fp
+del fp
def findfile(file, here=__file__):
"""Try to find a file on sys.path and the working directory. If it is not
found the argument passed to the function is returned (this does not
necessarily signal failure; could still be the legitimate path)."""
- import os
if os.path.isabs(file):
return file
path = sys.path
@@ -265,7 +348,7 @@
"""
if not (a == b):
- raise TestFailed, "%r == %r" % (a, b)
+ raise TestFailed("%r == %r" % (a, b))
def sortdict(dict):
"Like repr(dict), but in sorted order."
@@ -275,18 +358,30 @@
withcommas = ", ".join(reprpairs)
return "{%s}" % withcommas
-def check_syntax(statement):
+def make_bad_fd():
+ """
+ Create an invalid file descriptor by opening and closing a file and return
+ its fd.
+ """
+ file = open(TESTFN, "wb")
try:
- compile(statement, '<string>', 'exec')
+ return file.fileno()
+ finally:
+ file.close()
+ unlink(TESTFN)
+
+def check_syntax_error(testcase, statement):
+ try:
+ compile(statement, '<test string>', 'exec')
except SyntaxError:
pass
else:
- print 'Missing SyntaxError: "%s"' % statement
+ testcase.fail('Missing SyntaxError: "%s"' % statement)
def open_urlresource(url):
- import urllib, urlparse
- import os.path
+ import urlparse, urllib2
+ requires('urlfetch')
filename = urlparse.urlparse(url)[2].split('/')[-1] # '/': it's URL!
for path in [os.path.curdir, os.path.pardir]:
@@ -294,11 +389,250 @@
if os.path.exists(fn):
return open(fn)
- requires('urlfetch')
print >> get_original_stdout(), '\tfetching %s ...' % url
- fn, _ = urllib.urlretrieve(url, filename)
- return open(fn)
+ f = urllib2.urlopen(url, timeout=15)
+ try:
+ with open(filename, "wb") as out:
+ s = f.read()
+ while s:
+ out.write(s)
+ s = f.read()
+ finally:
+ f.close()
+ return open(filename)
+
+class WarningsRecorder(object):
+ """Convenience wrapper for the warnings list returned on
+ entry to the warnings.catch_warnings() context manager.
+ """
+ def __init__(self, warnings_list):
+ self._warnings = warnings_list
+ self._last = 0
+
+ def __getattr__(self, attr):
+ if len(self._warnings) > self._last:
+ return getattr(self._warnings[-1], attr)
+ elif attr in warnings.WarningMessage._WARNING_DETAILS:
+ return None
+ raise AttributeError("%r has no attribute %r" % (self, attr))
+
+ @property
+ def warnings(self):
+ return self._warnings[self._last:]
+
+ def reset(self):
+ self._last = len(self._warnings)
+
+
+def _filterwarnings(filters, quiet=False):
+ """Catch the warnings, then check if all the expected
+ warnings have been raised and re-raise unexpected warnings.
+ If 'quiet' is True, only re-raise the unexpected warnings.
+ """
+ # Clear the warning registry of the calling module
+ # in order to re-raise the warnings.
+ frame = sys._getframe(2)
+ registry = frame.f_globals.get('__warningregistry__')
+ if registry:
+ registry.clear()
+ with warnings.catch_warnings(record=True) as w:
+ # Set filter "always" to record all warnings. Because
+ # test_warnings swap the module, we need to look up in
+ # the sys.modules dictionary.
+ sys.modules['warnings'].simplefilter("always")
+ yield WarningsRecorder(w)
+ # Filter the recorded warnings
+ reraise = [warning.message for warning in w]
+ missing = []
+ for msg, cat in filters:
+ seen = False
+ for exc in reraise[:]:
+ message = str(exc)
+ # Filter out the matching messages
+ if (re.match(msg, message, re.I) and
+ issubclass(exc.__class__, cat)):
+ seen = True
+ reraise.remove(exc)
+ if not seen and not quiet:
+ # This filter caught nothing
+ missing.append((msg, cat.__name__))
+ if reraise:
+ raise AssertionError("unhandled warning %r" % reraise[0])
+ if missing:
+ raise AssertionError("filter (%r, %s) did not catch any warning" %
+ missing[0])
+
+
[email protected]
+def check_warnings(*filters, **kwargs):
+ """Context manager to silence warnings.
+
+ Accept 2-tuples as positional arguments:
+ ("message regexp", WarningCategory)
+
+ Optional argument:
+ - if 'quiet' is True, it does not fail if a filter catches nothing
+ (default True without argument,
+ default False if some filters are defined)
+
+ Without argument, it defaults to:
+ check_warnings(("", Warning), quiet=True)
+ """
+ quiet = kwargs.get('quiet')
+ if not filters:
+ filters = (("", Warning),)
+ # Preserve backward compatibility
+ if quiet is None:
+ quiet = True
+ return _filterwarnings(filters, quiet)
+
+
[email protected]
+def _check_py3k_warnings(*filters, **kwargs):
+ """Context manager to silence py3k warnings.
+
+ Accept 2-tuples as positional arguments:
+ ("message regexp", WarningCategory)
+
+ Optional argument:
+ - if 'quiet' is True, it does not fail if a filter catches nothing
+ (default False)
+
+ Without argument, it defaults to:
+ _check_py3k_warnings(("", DeprecationWarning), quiet=False)
+ """
+ if sys.py3kwarning:
+ if not filters:
+ filters = (("", DeprecationWarning),)
+ else:
+ # It should not raise any py3k warning
+ filters = ()
+ return _filterwarnings(filters, kwargs.get('quiet'))
+
+
+class CleanImport(object):
+ """Context manager to force import to return a new module reference.
+
+ This is useful for testing module-level behaviours, such as
+ the emission of a DeprecationWarning on import.
+
+ Use like this:
+
+ with CleanImport("foo"):
+ __import__("foo") # new reference
+ """
+
+ def __init__(self, *module_names):
+ self.original_modules = sys.modules.copy()
+ for module_name in module_names:
+ if module_name in sys.modules:
+ module = sys.modules[module_name]
+ # It is possible that module_name is just an alias for
+ # another module (e.g. stub for modules renamed in 3.x).
+ # In that case, we also need delete the real module to clear
+ # the import cache.
+ if module.__name__ != module_name:
+ del sys.modules[module.__name__]
+ del sys.modules[module_name]
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *ignore_exc):
+ sys.modules.update(self.original_modules)
+
+
+class EnvironmentVarGuard(object):
+
+ """Class to help protect the environment variable properly. Can be used as
+ a context manager."""
+
+ def __init__(self):
+ self._changed = {}
+
+ def set(self, envvar, value):
+ # Remember the initial value on the first access
+ if envvar not in self._changed:
+ self._changed[envvar] = os.environ.get(envvar)
+ os.environ[envvar] = value
+
+ def unset(self, envvar):
+ # Remember the initial value on the first access
+ if envvar not in self._changed:
+ self._changed[envvar] = os.environ.get(envvar)
+ if envvar in os.environ:
+ del os.environ[envvar]
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *ignore_exc):
+ for (k, v) in self._changed.items():
+ if v is None:
+ if k in os.environ:
+ del os.environ[k]
+ else:
+ os.environ[k] = v
+
+
+class TransientResource(object):
+
+ """Raise ResourceDenied if an exception is raised while the context manager
+ is in effect that matches the specified exception and attributes."""
+
+ def __init__(self, exc, **kwargs):
+ self.exc = exc
+ self.attrs = kwargs
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type_=None, value=None, traceback=None):
+ """If type_ is a subclass of self.exc and value has attributes matching
+ self.attrs, raise ResourceDenied. Otherwise let the exception
+ propagate (if any)."""
+ if type_ is not None and issubclass(self.exc, type_):
+ for attr, attr_value in self.attrs.iteritems():
+ if not hasattr(value, attr):
+ break
+ if getattr(value, attr) != attr_value:
+ break
+ else:
+ raise ResourceDenied("an optional resource is not available")
+
+
+def transient_internet():
+ """Return a context manager that raises ResourceDenied when various issues
+ with the Internet connection manifest themselves as exceptions."""
+ time_out = TransientResource(IOError, errno=errno.ETIMEDOUT)
+ socket_peer_reset = TransientResource(socket.error, errno=errno.ECONNRESET)
+ ioerror_peer_reset = TransientResource(IOError, errno=errno.ECONNRESET)
+ return contextlib.nested(time_out, socket_peer_reset, ioerror_peer_reset)
+
+
[email protected]
+def captured_output(stream_name):
+ """Run the 'with' statement body using a StringIO object in place of a
+ specific attribute on the sys module.
+ Example use (with 'stream_name=stdout')::
+
+ with captured_stdout() as s:
+ print "hello"
+ assert s.getvalue() == "hello"
+ """
+ import StringIO
+ orig_stdout = getattr(sys, stream_name)
+ setattr(sys, stream_name, StringIO.StringIO())
+ try:
+ yield getattr(sys, stream_name)
+ finally:
+ setattr(sys, stream_name, orig_stdout)
+
+def captured_stdout():
+ return captured_output("stdout")
+
+
#=======================================================================
# Decorator for running a function in a different locale, correctly resetting
# it afterwards.
@@ -343,16 +677,13 @@
_1M = 1024*1024
_1G = 1024 * _1M
_2G = 2 * _1G
+_4G = 4 * _1G
-# Hack to get at the maximum value an internal index can take.
-class _Dummy:
- def __getslice__(self, i, j):
- return j
-MAX_Py_ssize_t = _Dummy()[:]
+MAX_Py_ssize_t = sys.maxsize
def set_memlimit(limit):
- import re
global max_memuse
+ global real_max_memuse
sizes = {
'k': 1024,
'm': _1M,
@@ -364,6 +695,7 @@
if m is None:
raise ValueError('Invalid memory limit %r' % (limit,))
memlimit = int(float(m.group(1)) * sizes[m.group(3).lower()])
+ real_max_memuse = memlimit
if memlimit > MAX_Py_ssize_t:
memlimit = MAX_Py_ssize_t
if memlimit < _2G - 1:
@@ -376,7 +708,7 @@
'minsize' is the minimum useful size for the test (in arbitrary,
test-interpreted units.) 'memuse' is the number of 'bytes per size' for
the test, or a good estimate of it. 'overhead' specifies fixed overhead,
- independant of the testsize, and defaults to 5Mb.
+ independent of the testsize, and defaults to 5Mb.
The decorator tries to guess a good value for 'size' and passes it to
the decorated test function. If minsize * memuse is more than the
@@ -409,6 +741,27 @@
return wrapper
return decorator
+def precisionbigmemtest(size, memuse, overhead=5*_1M):
+ def decorator(f):
+ def wrapper(self):
+ if not real_max_memuse:
+ maxsize = 5147
+ else:
+ maxsize = size
+
+ if real_max_memuse and real_max_memuse < maxsize * memuse:
+ if verbose:
+ sys.stderr.write("Skipping %s because of memory "
+ "constraint\n" % (f.__name__,))
+ return
+
+ return f(self, maxsize)
+ wrapper.size = size
+ wrapper.memuse = memuse
+ wrapper.overhead = overhead
+ return wrapper
+ return decorator
+
def bigaddrspacetest(f):
"""Decorator for tests that fill the address space."""
def wrapper(self):
@@ -421,11 +774,8 @@
return wrapper
#=======================================================================
-# Preliminary PyUNIT integration.
+# unittest integration.
-import unittest
-
-
class BasicTestRunner:
def run(self, test):
result = unittest.TestResult()
@@ -433,31 +783,9 @@
return result
-def run_suite(suite, testclass=None):
- """Run all TestCases in their own individual TestSuite"""
- if not junit_xml_dir:
- # Splitting tests apart slightly changes the handling of the
- # TestFailed message
- return _run_suite(suite, testclass)
-
- failed = False
- for test in suite:
- suite = unittest.TestSuite()
- suite.addTest(test)
- try:
- _run_suite(suite, testclass)
- except TestFailed, e:
- if not failed:
- failed = e
- if failed:
- raise failed
-
-def _run_suite(suite, testclass=None):
+def _run_suite(suite):
"""Run tests from a unittest.TestSuite-derived class."""
- if junit_xml_dir:
- from junit_xml import JUnitXMLTestRunner
- runner = JUnitXMLTestRunner(junit_xml_dir)
- elif verbose:
+ if verbose:
runner = unittest.TextTestRunner(sys.stdout, verbosity=2)
else:
runner = BasicTestRunner()
@@ -469,28 +797,27 @@
elif len(result.failures) == 1 and not result.errors:
err = result.failures[0][1]
else:
- if testclass is None:
- msg = "errors occurred; run in verbose mode for details"
- else:
- msg = "errors occurred in %s.%s" \
- % (testclass.__module__, testclass.__name__)
- raise TestFailed(msg)
+ err = "multiple errors occurred"
+ if not verbose:
+ err += "; run in verbose mode for details"
raise TestFailed(err)
def run_unittest(*classes):
"""Run tests from unittest.TestCase-derived classes."""
+ valid_types = (unittest.TestSuite, unittest.TestCase)
suite = unittest.TestSuite()
for cls in classes:
- if isinstance(cls, (unittest.TestSuite, unittest.TestCase)):
+ if isinstance(cls, str):
+ if cls in sys.modules:
+ suite.addTest(unittest.findTestCases(sys.modules[cls]))
+ else:
+ raise ValueError("str arguments must be keys in sys.modules")
+ elif isinstance(cls, valid_types):
suite.addTest(cls)
else:
suite.addTest(unittest.makeSuite(cls))
- if len(classes)==1:
- testclass = classes[0]
- else:
- testclass = None
- run_suite(suite, testclass)
+ _run_suite(suite)
#=======================================================================
@@ -515,36 +842,12 @@
# output shouldn't be compared by regrtest.
save_stdout = sys.stdout
sys.stdout = get_original_stdout()
-
- if junit_xml_dir:
- from junit_xml import Tee, write_doctest
- save_stderr = sys.stderr
- sys.stdout = stdout = Tee(sys.stdout)
- sys.stderr = stderr = Tee(sys.stderr)
-
try:
- start = time.time()
- try:
- f, t = doctest.testmod(module, verbose=verbosity)
- except:
- took = time.time() - start
- if junit_xml_dir:
- write_doctest(junit_xml_dir, module.__name__, took, 'error',
- sys.exc_info(), stdout.getvalue(),
- stderr.getvalue())
- raise
- took = time.time() - start
+ f, t = doctest.testmod(module, verbose=verbosity)
if f:
- if junit_xml_dir:
- write_doctest(junit_xml_dir, module.__name__, took, 'failure',
- stdout=stdout.getvalue(),
- stderr=stderr.getvalue())
raise TestFailed("%d of %d doctests failed" % (f, t))
finally:
sys.stdout = save_stdout
- if junit_xml_dir:
- write_doctest(junit_xml_dir, module.__name__, took,
- stdout=stdout.getvalue(), stderr=stderr.getvalue())
if verbose:
print 'doctest (%s) ... %d tests with zero failures' % (module.__name__, t)
return f, t
@@ -554,7 +857,7 @@
def threading_setup():
import threading
- return len(threading._active), 0
+ return len(threading._active), len(threading._limbo)
def threading_cleanup(num_active, num_limbo):
import threading
@@ -566,6 +869,11 @@
count += 1
time.sleep(0.1)
+ count = 0
+ while len(threading._limbo) != num_limbo and count < _MAX_COUNT:
+ count += 1
+ time.sleep(0.1)
+
def reap_children():
"""Use this function at the end of test_main() whenever sub-processes
are started. This will help ensure that no extra children (zombies)
@@ -575,7 +883,6 @@
# Reap all our dead child processes so we don't leave zombies around.
# These hog resources and might be causing some of the buildbots to die.
- import os
if hasattr(os, 'waitpid'):
any_process = -1
while True:
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
------------------------------------------------------------------------------
Colocation vs. Managed Hosting
A question and answer guide to determining the best fit
for your organization - today and in the future.
http://p.sf.net/sfu/internap-sfd2d