Many testing-related changes, switched completely over to using nose, added docs on how to run the tests.
This commit is contained in:
@@ -46,6 +46,7 @@ Contents
|
||||
basic_usage
|
||||
chat_server_example
|
||||
threading
|
||||
testing
|
||||
history
|
||||
|
||||
modules
|
||||
|
||||
27
doc/testing.rst
Normal file
27
doc/testing.rst
Normal 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.
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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(' ', ' ')
|
||||
|
||||
# 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(' ', ' ')
|
||||
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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
171
tests/runall.py
171
tests/runall.py
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
@@ -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')
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
@@ -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])
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user