Commit 2f24633a authored by Denis Bilenko's avatar Denis Bilenko

testrunner.py: make sure we kill the whole tree on Windows; do not use...

testrunner.py: make sure we kill the whole tree on Windows; do not use mysubprocess; allow per-file timeouts
parent 63768a94
# testrunner timeout: 300
import sys import sys
import glob import glob
import subprocess import subprocess
......
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# testrunner timeout: 300
from __future__ import with_statement from __future__ import with_statement
import sys import sys
import re import re
......
...@@ -36,7 +36,7 @@ one will be selected if not provided. ...@@ -36,7 +36,7 @@ one will be selected if not provided.
DEFAULT_FILENAME = '/tmp/gevent-testrunner.sqlite3' DEFAULT_FILENAME = '/tmp/gevent-testrunner.sqlite3'
# the number of seconds each test script is allowed to run # the number of seconds each test script is allowed to run
DEFAULT_TIMEOUT = 600 DEFAULT_TIMEOUT = 60
# the number of bytes of output that is recorded; the rest is thrown away # the number of bytes of output that is recorded; the rest is thrown away
OUTPUT_LIMIT = 50000 OUTPUT_LIMIT = 50000
...@@ -239,9 +239,9 @@ def run_tests(options, args): ...@@ -239,9 +239,9 @@ def run_tests(options, args):
sys.exit(not result.wasSuccessful()) sys.exit(not result.wasSuccessful())
def run_subprocess(args, options): def run_subprocess(args, options, timeout):
from threading import Timer from threading import Timer
from mysubprocess import Popen, PIPE, STDOUT from subprocess import Popen, PIPE, STDOUT
popen_args = [sys.executable, sys.argv[0], '--record', popen_args = [sys.executable, sys.argv[0], '--record',
'--runid', options.runid, '--runid', options.runid,
...@@ -257,25 +257,17 @@ def run_subprocess(args, options): ...@@ -257,25 +257,17 @@ def run_subprocess(args, options):
retcode = [] retcode = []
def kill():
if popen.poll() is None:
try:
popen.kill()
except Exception, ex:
log('%s: %s', popen.pid, ex)
if killpg:
try:
killpg(popen.pid, 9)
except OSError, ex:
if ex.errno != 3:
raise
def killer(): def killer():
retcode.append('TIMEOUT') retcode.append('TIMEOUT')
sys.stderr.write('Killing %s (%s) because of timeout\n' % (popen.pid, args)) sys.stderr.write('Killing %s (%s) because of timeout\n' % (popen.pid, args))
kill() try:
popen.stdout.close()
except EnvironmentError:
pass
finally:
killgroup(popen)
timeout = Timer(options.timeout, killer) timeout = Timer(timeout, killer)
timeout.start() timeout.start()
output = '' output = ''
output_printed = False output_printed = False
...@@ -291,8 +283,8 @@ def run_subprocess(args, options): ...@@ -291,8 +283,8 @@ def run_subprocess(args, options):
output_printed = True output_printed = True
retcode.append(popen.wait()) retcode.append(popen.wait())
finally: finally:
kill()
timeout.cancel() timeout.cancel()
killgroup(popen)
# QQQ compensating for run_tests' screw up # QQQ compensating for run_tests' screw up
module_name = args[0] module_name = args[0]
if module_name.endswith('.py'): if module_name.endswith('.py'):
...@@ -301,6 +293,28 @@ def run_subprocess(args, options): ...@@ -301,6 +293,28 @@ def run_subprocess(args, options):
return retcode[0], output, output_printed return retcode[0], output, output_printed
def killgroup(popen):
if killpg is not None:
try:
killpg(popen.pid, 9)
except OSError, ex:
if ex.errno != 3:
raise
elif sys.platform.startswith('win'):
os.system('taskkill /F /PID %s /T' % popen.pid)
else:
popen.kill()
def read_timeout(source):
data = open(source, 'rb').read(1000)
matches = re.findall('^# testrunner timeout: (\d+)', data)
if not matches:
return
assert len(matches) == 1, (source, matches)
return int(matches[0])
def spawn_subprocess(args, options, base_params): def spawn_subprocess(args, options, base_params):
success = False success = False
if options.db: if options.db:
...@@ -313,7 +327,18 @@ def spawn_subprocess(args, options, base_params): ...@@ -313,7 +327,18 @@ def spawn_subprocess(args, options, base_params):
'test': module_name}) 'test': module_name})
row_id = store_record(options.db, 'test', params) row_id = store_record(options.db, 'test', params)
params['id'] = row_id params['id'] = row_id
retcode, output, output_printed = run_subprocess(args, options)
timeout = options.timeout
if timeout is None:
timeout = read_timeout(args[0]) or DEFAULT_TIMEOUT
if '-dbg' in sys.executable:
timeout *= 5
if 'test_patched_' in args[0]:
timeout *= 2
retcode, output, output_printed = run_subprocess(args, options, timeout)
if len(output) > OUTPUT_LIMIT: if len(output) > OUTPUT_LIMIT:
warn = '<AbridgedOutputWarning>' warn = '<AbridgedOutputWarning>'
output = output[:OUTPUT_LIMIT - len(warn)] + warn output = output[:OUTPUT_LIMIT - len(warn)] + warn
...@@ -576,7 +601,7 @@ def main(): ...@@ -576,7 +601,7 @@ def main():
parser.add_option('--record', default=False, action='store_true') parser.add_option('--record', default=False, action='store_true')
parser.add_option('--no-capture', dest='capture', default=True, action='store_false') parser.add_option('--no-capture', dest='capture', default=True, action='store_false')
parser.add_option('--stats', default=False, action='store_true') parser.add_option('--stats', default=False, action='store_true')
parser.add_option('--timeout', default=DEFAULT_TIMEOUT, type=float, metavar='SECONDS') parser.add_option('--timeout', type=float, metavar='SECONDS')
options, args = parser.parse_args() options, args = parser.parse_args()
options.verbosity += options.verbose - options.quiet options.verbosity += options.verbose - options.quiet
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment