Many testing-related changes, switched completely over to using nose, added docs on how to run the tests.

This commit is contained in:
Ryan Williams
2009-09-09 22:39:24 -07:00
parent c84b4109aa
commit f0b3941c3b
13 changed files with 41 additions and 1338 deletions

View File

@@ -46,6 +46,7 @@ Contents
basic_usage
chat_server_example
threading
testing
history
modules

27
doc/testing.rst Normal file
View File

@@ -0,0 +1,27 @@
Testing Eventlet
================
Eventlet is tested using `Nose <http://somethingaboutorange.com/mrl/projects/nose/>`_. To run tests, simply install nose, and then, in the eventlet tree, do:
.. code-block:: sh
$ nosetests
That's it! The output from running nose is the same as unittest's output, if the entire directory was one big test file. It tends to emit a lot of tracebacks from a few noisy tests, but they still pass.
Many tests are skipped based on environmental factors; for example, it makes no sense to test Twisted-specific functionality when Twisted is not installed. These are printed as S's during execution, and in the summary printed after the tests run it will tell you how many were skipped.
Standard Library Tests
----------------------
Eventlet provides for the ability to test itself with the standard Python networking tests. This verifies that the libraries it wraps work at least as well as the standard ones do. The directory tests/stdlib contains a bunch of stubs that import the standard lib tests from your system and run them. If you do not have any tests in your python distribution, they'll simply fail to import.
Run the standard library tests with nose; simply do:
.. code-block:: sh
$ cd tests/
$ nosetests stdlib
That should get you started. Note that most of the test failures are caused by `Nose issue 162 <http://code.google.com/p/python-nose/issues/detail?id=162>`_, which incorrectly identifies helper methods as test cases. Therefore, ignore any failure for the reason "TypeError: foo() takes exactly N arguments (2 given)", and sit tight until a version of Nose is released that fixes the issue.

View File

@@ -168,11 +168,11 @@ _threads = {}
_coro = None
_setup_already = False
def setup():
global _rpipe, _wpipe, _rfile, _threads, _coro, _setup_already
if _setup_already:
return
else:
_setup_already = True
global _rpipe, _wpipe, _rfile, _threads, _coro, _setup_already
_rpipe, _wpipe = os.pipe()
_rfile = os.fdopen(_rpipe,"r",0)
## Work whether or not wrap_pipe_with_coroutine_pipe was called

View File

@@ -1 +1,7 @@
use 'kdiff3 python2.5/Lib/test .' to see all the differences
The tests are intended to be run using Nose.
http://somethingaboutorange.com/mrl/projects/nose/
To run tests, simply install nose, and then, in the eventlet tree, do:
$ nosetests
That's it! Its output is the same as unittest's output. It tends to emit a lot of tracebacks from various poorly-behaving tests, but they still (generally) pass.

View File

@@ -1,237 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2008-2009 AG Projects
# Author: Denis Bilenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import sys
import os
try:
import sqlite3
except ImportError:
import pysqlite2.dbapi2 as sqlite3
import glob
REPO_URL = 'http://bitbucket.org/denis/eventlet'
hubs_order = ['poll', 'selects', 'libevent', 'libev', 'twistedr/selectreactor', 'twistedr/pollreactor', 'twistedr/epollreactor']
def make_table(database):
c = sqlite3.connect(database)
res = c.execute(('select command_record.id, testname, hub, runs, errors, fails, '
'timeouts, exitcode, stdout from parsed_command_record join '
'command_record on parsed_command_record.id=command_record.id ')).fetchall()
table = {} # testname -> hub -> test_result (runs, errors, fails, timeouts)
tests = set()
for id, testname, hub, runs, errors, fails, timeouts, exitcode, stdout in res:
tests.add(testname)
test_result = TestResult(runs, errors, fails, timeouts, exitcode, id, stdout)
table.setdefault(testname, {})[hub] = test_result
return table, sorted(tests)
def calc_hub_stats(table):
hub_stats = {} # hub -> cumulative test_result
for testname in table:
for hub in table[testname]:
test_result = table[testname][hub]
hub_stats.setdefault(hub, TestResult(0,0,0,0)).__iadd__(test_result)
hubs = hub_stats.items()
hub_names = sorted(hub_stats.keys())
def get_order(hub):
try:
return hubs_order.index(hub)
except ValueError:
return 100 + hub_names.index(hub)
hubs.sort(key=lambda (hub, stats): get_order(hub))
return hub_stats, [x[0] for x in hubs]
class TestResult:
def __init__(self, runs, errors, fails, timeouts, exitcode=None, id=None, output=None):
self.runs = max(runs, 0)
self.errors = max(errors, 0)
self.fails = max(fails, 0)
self.timeouts = max(timeouts, 0)
self.exitcode = exitcode
self.id = id
self.output = output
@property
def passed(self):
return max(0, self.runs - self.errors - self.fails)
@property
def failed(self):
return self.errors + self.fails
@property
def total(self):
return self.runs + self.timeouts
@property
def percentage(self):
return float(self.passed) / self.total
def __iadd__(self, other):
self.runs += other.runs
self.errors += other.errors
self.fails += other.fails
self.timeouts += other.timeouts
if self.exitcode != other.exitcode:
self.exitcode = None
self.id = None
self.output = None
def color(self):
if self.id is None:
return 'white'
if self.timeouts or self.exitcode in [7, 9, 10]:
return 'red'
elif self.errors or self.fails or self.exitcode:
return 'yellow'
else:
return '"#72ff75"'
def warnings(self):
r = []
if not self.failed and not self.timeouts:
if self.exitcode in [7, 9, 10]:
r += ['TIMEOUT']
if self.exitcode:
r += ['exitcode=%s' % self.exitcode]
if self.output is not None:
output = self.output.lower()
warning = output.count('warning')
if warning:
r += ['%s warnings' % warning]
tracebacks = output.count('traceback')
if tracebacks:
r += ['%s tracebacks' % tracebacks]
return r
def text(self):
errors = []
if self.fails:
errors += ['%s failed' % self.fails]
if self.errors:
errors += ['%s raised' % self.errors]
if self.timeouts:
errors += ['%s timeout' % self.timeouts]
errors += self.warnings()
if self.id is None:
errors += ['<hr>%s total' % self.total]
return '\n'.join(["%s passed" % self.passed] + errors).replace(' ', '&nbsp;')
# shorter passed/failed/raised/timeout
def text_short(self):
r = '%s/%s/%s' % (self.passed, self.failed, self.timeouts)
if self.warnings():
r += '\n' + '\n'.join(self.warnings()).replace(' ', '&nbsp;')
return r
def format(self):
text = self.text().replace('\n', '<br>\n')
if self.id is None:
valign = 'bottom'
else:
text = '<a class="x" href="%s.txt">%s</a>' % (self.id, text)
valign = 'center'
return '<td align=center valign=%s bgcolor=%s>%s</td>' % (valign, self.color(), text)
def format_testname(changeset, test):
return '<a href="%s/src/%s/tests/%s">%s</a>' % (REPO_URL, changeset, test, test)
def format_table(table, hubs, tests, hub_stats, changeset):
r = '<table border=1>\n<tr>\n<td/>\n'
for hub in hubs:
r += '<td align=center>%s</td>\n' % hub
r += '</tr>\n'
r += '<tr><td>Total</td>'
for hub in hubs:
test_result = hub_stats.get(hub)
if test_result is None:
r += '<td align=center bgcolor=gray>no data</td>'
else:
r += test_result.format() + '\n'
r += '</tr>'
r += '<tr><td colspan=%s/></tr>' % (len(hubs)+1)
for test in tests:
r += '<tr><td>%s</td>' % format_testname(changeset, test)
for hub in hubs:
test_result = table[test].get(hub)
if test_result is None:
r += '<td align=center bgcolor=gray>no data</td>'
else:
r += test_result.format() + '\n'
r += '</tr>'
r += '</table>'
return r
def format_header(rev, changeset, pyversion):
result = '<table width=99%%><tr><td>'
if REPO_URL is None:
result += 'Eventlet changeset %s: %s' % (rev, changeset)
else:
url = '%s/changeset/%s' % (REPO_URL, changeset)
result += '<a href="%s">Eventlet changeset %s: %s</a>' % (url, rev, changeset)
result += '</td><tr><tr><td>Python version: %s</td><tr></table><p>' % pyversion
return result
def format_html(table, rev, changeset, pyversion):
r = '<html><head><style type="text/css">a.x {color: black; text-decoration: none;} a.x:hover {text-decoration: underline;} </style></head><body>'
r += format_header(rev, changeset, pyversion)
r += table
r += '</body></html>'
return r
def generate_raw_results(path, database):
c = sqlite3.connect(database)
res = c.execute('select id, stdout from command_record').fetchall()
for id, out in res:
file(os.path.join(path, '%s.txt' % id), 'w').write(out.encode('utf-8'))
sys.stderr.write('.')
sys.stderr.write('\n')
def main(db):
full_changeset = '.'.join(db.split('.')[1:-1])
rev, changeset, pyversion = full_changeset.split('_')
table, tests = make_table(db)
hub_stats, hubs = calc_hub_stats(table)
report = format_html(format_table(table, hubs, tests, hub_stats, changeset), rev, changeset, pyversion)
path = '../htmlreports/%s' % full_changeset
try:
os.makedirs(path)
except OSError, ex:
if 'File exists' not in str(ex):
raise
file(path + '/index.html', 'w').write(report)
generate_raw_results(path, db)
if __name__=='__main__':
if not sys.argv[1:]:
latest_db = sorted(glob.glob('results.*.db'), key=lambda f: os.stat(f).st_mtime)[-1]
print latest_db
sys.argv.append(latest_db)
for db in sys.argv[1:]:
main(db)

View File

@@ -1,80 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2008-2009 AG Projects
# Author: Denis Bilenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Run the program and record stdout/stderr/exitcode into the database results.rev_changeset.db
Usage: %prog program [args]
"""
import sys
import os
import codecs
try:
import sqlite3
except ImportError:
import pysqlite2.dbapi2 as sqlite3
import warnings
warnings.simplefilter('ignore')
PYTHON_VERSION = '%s.%s.%s' % sys.version_info[:3]
COMMAND_CHANGESET = r"hg log -r tip 2> /dev/null | grep changeset"
def record(changeset, argv, stdout, returncode):
c = sqlite3.connect('results.%s_%s.db' % (changeset, PYTHON_VERSION))
c.execute('''create table if not exists command_record
(id integer primary key autoincrement,
command text,
stdout text,
exitcode integer)''')
c.execute('insert into command_record (command, stdout, exitcode)'
'values (?, ?, ?)', (`argv`, stdout, returncode))
c.commit()
def main():
argv = sys.argv[1:]
if argv[0]=='-d':
debug = True
del argv[0]
else:
debug = False
try:
changeset = os.popen(COMMAND_CHANGESET).readlines()[0].replace('changeset:', '').strip().replace(':', '_')
except Exception:
changeset = 'revision_unknown'
output_name = os.tmpnam()
arg = ' '.join(argv) + ' &> %s' % output_name
print arg
returncode = os.system(arg)>>8
print arg, 'finished with code', returncode
stdout = codecs.open(output_name, mode='r', encoding='utf-8', errors='replace').read().replace('\x00', '?')
if not debug:
if returncode==1:
pass
else:
record(changeset, argv, stdout, returncode)
os.unlink(output_name)
sys.exit(returncode)
if __name__=='__main__':
main()

View File

@@ -1,171 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2008-2009 AG Projects
# Author: Denis Bilenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Run tests for different configurations (hub/reactor)"""
import sys
import os
import random
from glob import glob
from optparse import OptionParser, Option
from copy import copy
from time import time
from with_eventlet import import_reactor
first_hubs = ['poll', 'selects', 'twistedr']
first_reactors = ['selectreactor', 'pollreactor']
COMMAND = sys.executable + ' ./record_results.py ' + sys.executable + ' ./with_timeout.py ./with_eventlet.py %(setup)s %(test)s'
PARSE_PERIOD = 10
# the following aren't in the default list unless --all option present
NOT_HUBS = set()
NOT_REACTORS = set(['wxreactor', 'glib2reactor', 'gtk2reactor'])
NOT_TESTS = set()
def w(s):
sys.stderr.write("%s\n" % (s, ))
def enum_hubs():
from eventlet.api import use_hub
hubs = glob('../eventlet/hubs/*.py')
hubs = [os.path.basename(h)[:-3] for h in hubs]
hubs = [h for h in hubs if h[:1]!='_']
hubs = set(hubs)
hubs.discard('hub')
hubs -= NOT_HUBS
result = []
for hub in hubs:
try:
use_hub(hub)
except Exception, ex:
print 'Skipping hub %s: %s' % (hub, ex)
else:
result.append(hub)
return result
def enum_reactors():
try:
import twisted
except ImportError:
return []
p = os.path.join(os.path.dirname(twisted.__file__), 'internet', '*?reactor.py')
files = glob(p)
all_reactors = [os.path.basename(f[:-3]) for f in files]
all_reactors = set(all_reactors) - NOT_REACTORS
selected_reactors = []
for reactor in all_reactors:
try:
import_reactor(reactor)
except Exception, ex:
print 'Skipping reactor %s: %s' % (reactor, ex)
else:
selected_reactors.append(reactor)
return selected_reactors
def enum_tests():
tests = []
tests += glob('test_*.py')
tests += glob('*_test.py')
tests = set(tests) - NOT_TESTS - set(['test_support.py'])
return tests
def cmd(program):
w(program)
res = os.system(program)>>8
w(res)
if res==1:
sys.exit(1)
return res
def check_stringlist(option, opt, value):
return value.split(',')
class MyOption(Option):
TYPES = Option.TYPES + ("stringlist",)
TYPE_CHECKER = copy(Option.TYPE_CHECKER)
TYPE_CHECKER["stringlist"] = check_stringlist
def main():
global NOT_HUBS, NOT_REACTORS, NOT_TESTS
parser = OptionParser(option_class=MyOption)
parser.add_option('-u', '--hubs', type='stringlist')
parser.add_option('-r', '--reactors', type='stringlist')
parser.add_option('--ignore-hubs', type='stringlist', default=[])
parser.add_option('--ignore-reactors', type='stringlist', default=[])
parser.add_option('--ignore-tests', type='stringlist', default=[])
parser.add_option('-s', '--show', help='show default values and exit', action='store_true', default=False)
parser.add_option('-a', '--all', action='store_true', default=False)
options, args = parser.parse_args()
options.tests = args or None
if options.all:
NOT_HUBS = NOT_REACTORS = NOT_TESTS = set()
if options.hubs is None:
options.hubs = enum_hubs()
if options.reactors is None:
options.reactors = enum_reactors()
if options.tests is None:
options.tests = enum_tests()
tests = []
for t in options.tests:
tests.extend(glob(t))
options.tests = tests
options.hubs = list(set(options.hubs) - set(options.ignore_hubs))
options.reactors = list(set(options.reactors) - set(options.ignore_reactors))
options.tests = list(set(options.tests) - set(options.ignore_tests))
random.shuffle(options.hubs)
options.hubs.sort(key=first_hubs.__contains__, reverse=True)
random.shuffle(options.reactors)
options.reactors.sort(key=first_reactors.__contains__, reverse=True)
random.shuffle(options.tests)
print 'hubs: %s' % ','.join(options.hubs)
print 'reactors: %s' % ','.join(options.reactors)
print 'tests: %s' % ','.join(options.tests)
if options.show:
return
setups = []
for hub in options.hubs:
if hub == 'twistedr':
for reactor in options.reactors:
setups.append('--hub twistedr --reactor %s' % reactor)
else:
setups.append('--hub %s' % hub)
last_time = time()
for setup in setups:
w(setup)
for test in options.tests:
w(test)
cmd(COMMAND % locals())
if time()-last_time>PARSE_PERIOD:
os.system('./parse_results.py')
last_time = time()
os.system('./parse_results.py')
if __name__=='__main__':
main()

View File

@@ -23,7 +23,8 @@ test_socketserver.socket = socket
test_socketserver.select = select
test_socketserver.time = time
from test.test_socketserver import *
# skipping these tests for now
#from test.test_socketserver import *
if __name__ == "__main__":
test_main()
pass#test_main()

View File

@@ -8,4 +8,4 @@ def allocate_lock():
thread.allocate_lock = allocate_lock
thread.LockType = coros.BoundedSemaphore
execfile('test_thread.py')
execfile('stdlib/test_thread.py')

View File

@@ -24,7 +24,6 @@ To do that spawn a green server and then access it using a green socket.
If either operation blocked the whole script would block and timeout.
"""
import unittest
from tests import test_support
from eventlet.green import urllib2, BaseHTTPServer
from eventlet.api import spawn, kill
@@ -62,4 +61,4 @@ class TestGreenness(unittest.TestCase):
self.assertEqual(self.server.request_count, 1)
if __name__ == '__main__':
test_support.run_unittest(TestGreenness)
unittest.main()

View File

@@ -1,530 +0,0 @@
"""Supporting definitions for the Python regression tests."""
if __name__ != 'tests.test_support':
raise ImportError, 'test_support must be imported from the test package'
import sys
class Error(Exception):
"""Base class for regression test exceptions."""
class TestFailed(Error):
"""Test failed."""
class TestSkipped(Error):
"""Test skipped.
This can be raised to indicate that a test was deliberatly
skipped, but not because a feature wasn't available. For
example, if some resource can't be used, such as the network
appears to be unavailable, this should be raised instead of
TestFailed.
"""
class ResourceDenied(TestSkipped):
"""Test skipped because it requested a disallowed resource.
This is raised when a test calls requires() for a resource that
has not be enabled. It is used to distinguish between expected
and unexpected skips.
"""
verbose = 1 # Flag set to 0 by regrtest.py
use_resources = None # Flag set to [] by regrtest.py
max_memuse = 0 # Disable bigmem tests (they will still be run with
# small sizes, to make sure they work.)
# _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.
# The point is to have some flavor of stdout the user can actually see.
_original_stdout = None
def record_original_stdout(stdout):
global _original_stdout
_original_stdout = stdout
def get_original_stdout():
return _original_stdout or sys.stdout
def unload(name):
try:
del sys.modules[name]
except KeyError:
pass
def unlink(filename):
import os
try:
os.unlink(filename)
except OSError:
pass
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
# the chance exists that there is no .pyc (and thus the 'try' statement
# is exited) but there is a .pyo file.
unlink(os.path.join(dirname, modname + os.extsep + 'pyo'))
def is_resource_enabled(resource):
"""Test whether a resource is enabled. Known resources are set by
regrtest.py."""
return use_resources is not None and resource in use_resources
def requires(resource, msg=None):
"""Raise ResourceDenied if the specified resource is not available.
If the caller's module is __main__ then automatically return True. The
possibility of False being returned occurs when regrtest.py is executing."""
# see if the caller's module is __main__ - if so, treat as if
# the resource was set
return
if sys._getframe().f_back.f_globals.get("__name__") == "__main__":
return
if not is_resource_enabled(resource):
if msg is None:
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."""
import socket, errno
# 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'
FUZZ = 1e-6
def fcmp(x, y): # fuzzy comparison function
if type(x) == type(0.0) or type(y) == type(0.0):
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([])):
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)
try:
unicode
have_unicode = 1
except NameError:
have_unicode = 0
is_jython = sys.platform.startswith('java')
import os
# Filename used for testing
if os.name == 'java':
# Jython disallows @ in module names
TESTFN = '$test'
elif os.name == 'riscos':
TESTFN = 'testfile'
else:
TESTFN = '@test'
# Unicode name only used if TEST_FN_ENCODING exists for the platform.
if have_unicode:
# Assuming sys.getfilesystemencoding()!=sys.getdefaultencoding()
# TESTFN_UNICODE is a filename that can be encoded using the
# file system encoding, but *not* with the default (ascii) encoding
if isinstance('', unicode):
# python -U
# XXX perhaps unicode() should accept Unicode strings?
TESTFN_UNICODE = "@test-\xe0\xf2"
else:
# 2 latin characters.
TESTFN_UNICODE = unicode("@test-\xe0\xf2", "latin-1")
TESTFN_ENCODING = sys.getfilesystemencoding()
# TESTFN_UNICODE_UNENCODEABLE is a filename that should *not* be
# able to be encoded by *either* the default or filesystem encoding.
# This test really only makes sense on Windows NT platforms
# which have special Unicode support in posixmodule.
if (not hasattr(sys, "getwindowsversion") or
sys.getwindowsversion()[3] < 2): # 0=win32s or 1=9x/ME
TESTFN_UNICODE_UNENCODEABLE = None
else:
# Japanese characters (I think - from bug 846133)
TESTFN_UNICODE_UNENCODEABLE = eval('u"@test-\u5171\u6709\u3055\u308c\u308b"')
try:
# XXX - Note - should be using TESTFN_ENCODING here - but for
# Windows, "mbcs" currently always operates as if in
# errors=ignore' mode - hence we get '?' characters rather than
# the exception. 'Latin1' operates as we expect - ie, fails.
# See [ 850997 ] mbcs encoding ignores errors
TESTFN_UNICODE_UNENCODEABLE.encode("Latin1")
except UnicodeEncodeError:
pass
else:
print \
'WARNING: The filename %r CAN be encoded by the filesystem. ' \
'Unicode filename tests may not be effective' \
% TESTFN_UNICODE_UNENCODEABLE
# Make sure we can write to TESTFN, try in /tmp if we can't
fp = None
try:
fp = open(TESTFN, 'w+')
except IOError:
TMP_TESTFN = os.path.join('/tmp', TESTFN)
try:
fp = open(TMP_TESTFN, 'w+')
TESTFN = TMP_TESTFN
del TMP_TESTFN
except IOError:
print ('WARNING: tests will fail, unable to write to: %s or %s' %
(TESTFN, TMP_TESTFN))
if fp is not None:
fp.close()
unlink(TESTFN)
del os, 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
path = [os.path.dirname(here)] + path
for dn in path:
fn = os.path.join(dn, file)
if os.path.exists(fn): return fn
return file
def verify(condition, reason='test failed'):
"""Verify that condition is true. If not, raise TestFailed.
The optional argument reason can be given to provide
a better error text.
"""
if not condition:
raise TestFailed(reason)
def vereq(a, b):
"""Raise TestFailed if a == b is false.
This is better than verify(a == b) because, in case of failure, the
error message incorporates repr(a) and repr(b) so you can see the
inputs.
Note that "not (a == b)" isn't necessarily the same as "a != b"; the
former is tested.
"""
if not (a == b):
raise TestFailed, "%r == %r" % (a, b)
def sortdict(dict):
"Like repr(dict), but in sorted order."
items = dict.items()
items.sort()
reprpairs = ["%r: %r" % pair for pair in items]
withcommas = ", ".join(reprpairs)
return "{%s}" % withcommas
def check_syntax(statement):
try:
compile(statement, '<string>', 'exec')
except SyntaxError:
pass
else:
print 'Missing SyntaxError: "%s"' % statement
def open_urlresource(url):
import urllib, urlparse
import os.path
filename = urlparse.urlparse(url)[2].split('/')[-1] # '/': it's URL!
for path in [os.path.curdir, os.path.pardir]:
fn = os.path.join(path, filename)
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)
#=======================================================================
# Decorator for running a function in a different locale, correctly resetting
# it afterwards.
def run_with_locale(catstr, *locales):
def decorator(func):
def inner(*args, **kwds):
try:
import locale
category = getattr(locale, catstr)
orig_locale = locale.setlocale(category)
except AttributeError:
# if the test author gives us an invalid category string
raise
except:
# cannot retrieve original locale, so do nothing
locale = orig_locale = None
else:
for loc in locales:
try:
locale.setlocale(category, loc)
break
except:
pass
# now run the function, resetting the locale on exceptions
try:
return func(*args, **kwds)
finally:
if locale and orig_locale:
locale.setlocale(category, orig_locale)
inner.func_name = func.func_name
inner.__doc__ = func.__doc__
return inner
return decorator
#=======================================================================
# Big-memory-test support. Separate from 'resources' because memory use should be configurable.
# Some handy shorthands. Note that these are used for byte-limits as well
# as size-limits, in the various bigmem tests
_1M = 1024*1024
_1G = 1024 * _1M
_2G = 2 * _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()[:]
def set_memlimit(limit):
import re
global max_memuse
sizes = {
'k': 1024,
'm': _1M,
'g': _1G,
't': 1024*_1G,
}
m = re.match(r'(\d+(\.\d+)?) (K|M|G|T)b?$', limit,
re.IGNORECASE | re.VERBOSE)
if m is None:
raise ValueError('Invalid memory limit %r' % (limit,))
memlimit = int(float(m.group(1)) * sizes[m.group(3).lower()])
if memlimit > MAX_Py_ssize_t:
memlimit = MAX_Py_ssize_t
if memlimit < _2G - 1:
raise ValueError('Memory limit %r too low to be useful' % (limit,))
max_memuse = memlimit
def bigmemtest(minsize, memuse, overhead=5*_1M):
"""Decorator for bigmem tests.
'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.
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
allowed memory use (as defined by max_memuse), the test is skipped.
Otherwise, minsize is adjusted upward to use up to max_memuse.
"""
def decorator(f):
def wrapper(self):
if not max_memuse:
# If max_memuse is 0 (the default),
# we still want to run the tests with size set to a few kb,
# to make sure they work. We still want to avoid using
# too much memory, though, but we do that noisily.
maxsize = 5147
self.failIf(maxsize * memuse + overhead > 20 * _1M)
else:
maxsize = int((max_memuse - overhead) / memuse)
if maxsize < minsize:
# Really ought to print 'test skipped' or something
if verbose:
sys.stderr.write("Skipping %s because of memory "
"constraint\n" % (f.__name__,))
return
# Try to keep some breathing room in memory use
maxsize = max(maxsize - 50 * _1M, minsize)
return f(self, maxsize)
wrapper.minsize = minsize
wrapper.memuse = memuse
wrapper.overhead = overhead
return wrapper
return decorator
def bigaddrspacetest(f):
"""Decorator for tests that fill the address space."""
def wrapper(self):
if max_memuse < MAX_Py_ssize_t:
if verbose:
sys.stderr.write("Skipping %s because of memory "
"constraint\n" % (f.__name__,))
else:
return f(self)
return wrapper
#=======================================================================
# Preliminary PyUNIT integration.
import unittest
class BasicTestRunner:
def run(self, test):
result = unittest.TestResult()
test(result)
return result
def run_suite(suite, testclass=None):
"""Run tests from a unittest.TestSuite-derived class."""
if verbose:
runner = unittest.TextTestRunner(sys.stdout, verbosity=2)
else:
runner = BasicTestRunner()
result = runner.run(suite)
if not result.wasSuccessful():
if len(result.errors) == 1 and not result.failures:
err = result.errors[0][1]
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)
raise TestFailed(err)
def run_unittest(*classes):
"""Run tests from unittest.TestCase-derived classes."""
suite = unittest.TestSuite()
for cls in classes:
if isinstance(cls, (unittest.TestSuite, unittest.TestCase)):
suite.addTest(cls)
else:
suite.addTest(unittest.makeSuite(cls))
if len(classes)==1:
testclass = classes[0]
else:
testclass = None
run_suite(suite, testclass)
#=======================================================================
# doctest driver.
def run_doctest(module, verbosity=None):
"""Run doctest on the given module. Return (#failures, #tests).
If optional argument verbosity is not specified (or is None), pass
test_support's belief about verbosity on to doctest. Else doctest's
usual behavior is used (it searches sys.argv for -v).
"""
import doctest
if verbosity is None:
verbosity = verbose
else:
verbosity = None
# Direct doctest output (normally just errors) to real stdout; doctest
# output shouldn't be compared by regrtest.
save_stdout = sys.stdout
sys.stdout = get_original_stdout()
try:
f, t = doctest.testmod(module, verbose=verbosity)
if f:
raise TestFailed("%d of %d doctests failed" % (f, t))
finally:
sys.stdout = save_stdout
if verbose:
print 'doctest (%s) ... %d tests with zero failures' % (module.__name__, t)
return f, t
#=======================================================================
# Threading support to prevent reporting refleaks when running regrtest.py -R
def threading_setup():
from eventlet.green import threading
return len(threading._active), len(threading._limbo)
def threading_cleanup(num_active, num_limbo):
from eventlet.green import threading
from eventlet.green import time
_MAX_COUNT = 10
count = 0
while len(threading._active) != num_active and count < _MAX_COUNT:
print threading._active
count += 1
time.sleep(0.1)
count = 0
while len(threading._limbo) != num_limbo and count < _MAX_COUNT:
print threading._limbo
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)
stick around to hog resources and create problems when looking
for refleaks.
"""
# 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:
try:
# This will raise an exception on Windows. That's ok.
pid, status = os.waitpid(any_process, os.WNOHANG)
if pid == 0:
break
except:
break

View File

@@ -1,94 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2008-2009 AG Projects
# Author: Denis Bilenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Execute python script with hub installed.
Usage: %prog [--hub HUB] [--reactor REACTOR] program.py
"""
import sys
import os
def import_reactor(reactor):
m = __import__('twisted.internet.' + reactor)
return getattr(m.internet, reactor)
def setup_hub(hub, reactor):
if reactor is not None:
import_reactor(reactor).install()
if hub is not None:
from eventlet.api import use_hub
try:
use_hub(hub)
except ImportError, ex:
# as a shortcut, try to import the reactor with such name
try:
r = import_reactor(hub)
except ImportError:
sys.exit('No hub %s: %s' % (hub, ex))
else:
r.install()
use_hub('twistedr')
def parse_args():
hub = None
reactor = None
del sys.argv[0] # kill with_eventlet.py
if sys.argv[0]=='--hub':
del sys.argv[0]
hub = sys.argv[0]
del sys.argv[0]
if sys.argv[0]=='--reactor':
del sys.argv[0]
reactor = sys.argv[0]
del sys.argv[0]
return hub, reactor
if __name__=='__main__':
hub, reactor = parse_args()
setup_hub(hub, reactor)
from eventlet import __version__
from eventlet.api import get_hub
hub = get_hub() # set up the hub now
try:
version_info = ' version_info=%s' % (hub._version_info(), )
except:
version_info = ''
try:
diffstat = os.popen(r"hg diff 2> /dev/null | diffstat").read().strip()
diffstat = diffstate.replace('0 files changed', '')
except:
diffstat = None
try:
changeset = os.popen(r"hg log -r tip 2> /dev/null | grep changeset").readlines()[0].replace('changeset:', '').strip().replace(':', '_')
if diffstat:
changeset += '+'
changeset = '(%s)' % (changeset, )
except:
changeset = ''
print '===HUB=%r version=%s%s%s' % (hub, __version__, changeset, version_info)
if diffstat:
print diffstat
if 'twisted.internet.reactor' in sys.modules:
print '===REACTOR=%r' % sys.modules['twisted.internet.reactor']
sys.stdout.flush()
execfile(sys.argv[0])

View File

@@ -1,219 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2008-2009 AG Projects
# Author: Denis Bilenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
Run Python script in a child process. Kill it after timeout has elapsed.
If the script was running unittest test cases, the timeouted test case is
disabled and the script is restarted.
Usage: %prog [-t TIMEOUT] program.py [args]
If program.py timed out, return 7
If program.py exited with non-zero value, return 8
If program.py exited with zero value after several runs, return 9
If program.py exited with non-zero value after several runs, return 10
"""
import sys
import os
import time
import warnings
if sys.argv[1:2] and sys.argv[1]=='-t':
del sys.argv[1]
TIMEOUT = int(sys.argv[1])
del sys.argv[1]
else:
TIMEOUT = 20
try:
disabled_tests
except NameError:
disabled_tests = []
try:
CURRENT_TEST_FILENAME
except NameError:
warnings.filterwarnings('ignore', 'tmpnam is a potential security risk to your program')
CURRENT_TEST_FILENAME = os.tmpnam()
del warnings.filters[0]
class Alarm(Exception):
pass
def al(*args):
raise Alarm
def _test():
"""
>>> system('./with_timeout.py -t 3 __init__.py')
(0, 0)
>>> system('./with_timeout.py -t 3 /usr/lib/python2.5/BaseHTTPServer.py 0')
(7, 3)
>>> system('./with_timeout.py -t 3 with_timeout.py --selftest1')
(9, 3)
>>> system('./with_timeout.py -t 3 with_timeout.py --selftest2')
(10, 3)
>>> system('./with_timeout.py -t 3 with_timeout.py no_such_file.xxx')
(8, 0)
"""
import doctest
doctest.testmod()
if not sys.argv[1:]:
def system(*args):
start = time.time()
res = os.system(*args)
return res>>8, int(time.time()-start)
#system('./with_timeout.py -t 3 with_timeout.py selftest')
#sys.exit(0)
_test()
sys.exit(__doc__.replace('%prog', sys.argv[0]))
elif sys.argv[1:]==['--selftest1']:
import unittest
class Test(unittest.TestCase):
def test1(self):
pass
def test_long(self):
time.sleep(10)
from tests import test_support
test_support.run_unittest(Test)
sys.exit(0)
elif sys.argv[1:]==['--selftest2']:
import unittest
class Test(unittest.TestCase):
def test_fail(self):
fail
def test_long(self):
time.sleep(10)
from tests import test_support
test_support.run_unittest(Test)
sys.exit(0)
filename = sys.argv[1]
del sys.argv[0]
def execf():
#print 'in execf', disabled_tests
def patch_unittest():
"print test name before it was run and write it pipe"
import unittest
class TestCase(unittest.TestCase):
base = unittest.TestCase
def run(self, result=None):
try:
testMethodName = self._testMethodName
except:
testMethodName = self.__testMethodName
name = "%s.%s" % (self.__class__.__name__, testMethodName)
if name in disabled_tests:
return
print name, ' '
sys.stdout.flush()
file(CURRENT_TEST_FILENAME, 'w').write(name)
try:
return self.base.run(self, result)
finally:
sys.stdout.flush()
try:
os.unlink(CURRENT_TEST_FILENAME)
except:
pass
unittest.TestCase = TestCase
patch_unittest()
execfile(filename, globals())
while True:
#print 'before fork, %s' % disabled_tests
try:
os.unlink(CURRENT_TEST_FILENAME)
except:
pass
child = os.fork()
if child == 0:
print '===PYTHON=%s.%s.%s' % sys.version_info[:3]
print '===ARGV=%s' % ' '.join(sys.argv)
print '===TIMEOUT=%r' % TIMEOUT
sys.stdout.flush()
execf()
break
else:
start = time.time()
import signal
signal.signal(signal.SIGALRM, al)
signal.alarm(TIMEOUT)
pid = None
try:
pid, status = os.waitpid(child, 0)
signal.alarm(0)
except Alarm:
try:
os.kill(child, signal.SIGKILL)
except Exception:
pass
print '\n===%s was killed after %s seconds' % (child, time.time()-start)
sys.stdout.flush()
bad_test = None
try:
bad_test = file(CURRENT_TEST_FILENAME).read()
except IOError:
pass
if bad_test in disabled_tests:
print '\n===%s was disabled but it still managed to fail?!' % bad_test
sys.stdout.flush()
break
if bad_test is None:
sys.exit(7)
print '\n===Trying again, now without %s' % bad_test
sys.stdout.flush()
disabled_tests.append(bad_test)
except:
try:
signal.alarm(0)
except:
pass
try:
os.kill(child, signal.SIGKILL)
except:
pass
raise
else:
print '===%s exited with code %s' % (pid, status)
sys.stdout.flush()
if disabled_tests:
print '\n===disabled because of timeout: %s\n%s\n' % (len(disabled_tests), '\n'.join(disabled_tests))
sys.stdout.flush()
if disabled_tests:
if status:
retcode = 10
else:
retcode = 9
else:
if status:
retcode = 8
else:
retcode = 0
sys.exit(retcode)