tester: Correctly handle contro-c.

Add support to kill running tests if the user presses control-c.
This commit is contained in:
Chris Johns
2014-05-31 20:03:05 +10:00
parent 5cdcde1ec2
commit c04a84917a
5 changed files with 164 additions and 18 deletions

View File

@@ -116,6 +116,7 @@ class execute(object):
self.environment = None
self.outputting = False
self.timing_out = False
self.proc = None
def _capture(self, command, proc, timeout = None):
"""Create 3 threads to read stdout and stderr and send to the output handler
@@ -262,12 +263,25 @@ class execute(object):
timeout_thread.daemon = True
timeout_thread.start()
try:
self.lock.acquire()
try:
self.proc = proc
except:
raise
finally:
self.lock.release()
exitcode = proc.wait()
except:
print 'killing'
proc.kill()
raise
finally:
self.lock.acquire()
try:
self.proc = None
except:
raise
finally:
self.lock.release()
if self.cleanup:
self.cleanup(proc)
if timeout_thread:
@@ -415,6 +429,37 @@ class execute(object):
self.environment = environment
return old_environment
def kill(self):
self.lock.acquire()
try:
if self.proc is not None:
self.proc.kill()
except:
raise
finally:
self.lock.release()
def terminate(self):
self.lock.acquire()
try:
if self.proc is not None:
self.proc.terminate()
except:
raise
finally:
self.lock.release()
def send_signal(self, signal):
self.lock.acquire()
try:
if self.proc is not None:
print "sending sig"
self.proc.send_signal(signal)
except:
raise
finally:
self.lock.release()
class capture_execution(execute):
"""Capture all output as a string and return it."""

View File

@@ -0,0 +1,43 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2013-2014 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
import sys
import traceback
def trace():
code = []
for threadId, stack in sys._current_frames().items():
code.append("\n# thread-id: %s" % threadId)
for filename, lineno, name, line in traceback.extract_stack(stack):
code.append('file: "%s", line %d, in %s' % (filename, lineno, name))
if line:
code.append(" %s" % (line.strip()))
return '\n'.join(code)

41
tester/rt/bsps.py Normal file
View File

@@ -0,0 +1,41 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2013-2014 Chris Johns (chrisj@rtems.org)
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
from rtemstoolkit import error
from rtemstoolkit import log
from rtemstoolkit import path
def list(opts):
path_ = opts.defaults.expand('%%{_configdir}/bsps/*.mc')
bsps = path.collect_files(path_)
log.notice(' BSP List:')
for bsp in bsps:
log.notice(' %s' % (path.basename(bsp[:-3])))
raise error.exit()

View File

@@ -62,7 +62,7 @@ class file(config.file):
self.console = None
self.output = None
self.report = report
self.load(name)
self.name = name
def __del__(self):
if self.console:
@@ -181,6 +181,9 @@ class file(config.file):
for l in text:
print ' '.join(l)
def run(self):
self.load(self.name)
def capture(self, text):
text = [(']', l) for l in text.replace(chr(13), '').splitlines()]
self._lock()
@@ -203,3 +206,7 @@ class file(config.file):
if flag in dt.split(','):
return True
return False
def kill(self):
if self.process:
self.process.kill()

View File

@@ -38,7 +38,9 @@ import time
from rtemstoolkit import error
from rtemstoolkit import log
from rtemstoolkit import path
from rtemstoolkit import stacktraces
import bsps
import config
import console
import options
@@ -46,17 +48,6 @@ import report
import version
import fnmatch
def stacktraces():
import traceback
code = []
for threadId, stack in sys._current_frames().items():
code.append("\n# thread-id: %s" % threadId)
for filename, lineno, name, line in traceback.extract_stack(stack):
code.append('file: "%s", line %d, in %s' % (filename, lineno, name))
if line:
code.append(" %s" % (line.strip()))
return '\n'.join(code)
class test(object):
def __init__(self, index, total, report, executable, rtems_tools, bsp, bsp_config, opts):
self.index = index
@@ -78,7 +69,15 @@ class test(object):
if not path.isdir(rtems_tools_bin):
raise error.general('cannot find RTEMS tools path: %s' % (rtems_tools_bin))
self.opts.defaults['rtems_tools'] = rtems_tools_bin
self.config = config.file(report, bsp_config, self.opts)
self.config = config.file(self.report, self.bsp_config, self.opts)
def run(self):
if self.config:
self.config.run()
def kill(self):
if self.config:
self.config.kill()
class test_run(object):
def __init__(self, index, total, report, executable, rtems_tools, bsp, bsp_config, opts):
@@ -102,6 +101,7 @@ class test_run(object):
self.executable, self.rtems_tools,
self.bsp, self.bsp_config,
self.opts)
self.test.run()
except KeyboardInterrupt:
pass
except:
@@ -120,6 +120,10 @@ class test_run(object):
if self.result is not None:
raise self.result[0], self.result[1], self.result[2]
def kill(self):
if self.test:
self.test.kill()
def find_executables(paths, glob):
executables = []
for p in paths:
@@ -175,8 +179,13 @@ def list_bsps(opts):
log.notice(' %s' % (path.basename(bsp[:-3])))
raise error.exit()
def killall(tests):
for test in tests:
test.kill()
def run(command_path = None):
import sys
tests = []
stdtty = console.save()
opts = None
default_exefilter = '*.exe'
@@ -194,7 +203,7 @@ def run(command_path = None):
command_path = command_path)
log.notice('RTEMS Testing - Tester, v%s' % (version.str()))
if opts.find_arg('--list-bsps'):
list_bsps(opts)
bsps.list(opts)
exe_filter = opts.find_arg('--filter')
if exe_filter:
exe_filter = exe_filter[1]
@@ -246,7 +255,6 @@ def run(command_path = None):
reporting = 1
jobs = int(opts.jobs(opts.defaults['_ncpus']))
exe = 0
tests = []
finished = []
if jobs > len(executables):
jobs = len(executables)
@@ -288,7 +296,8 @@ def run(command_path = None):
report_finished(reports, report_mode, -1, finished, job_trace)
reports.summary()
end_time = datetime.datetime.now()
log.notice('Testing time: %s' % (str(end_time - start_time)))
log.notice('Average test time: %s' % (str((end_time - start_time) / total)))
log.notice('Testing time : %s' % (str(end_time - start_time)))
except error.general, gerr:
print gerr
sys.exit(1)
@@ -302,8 +311,9 @@ def run(command_path = None):
print '}} dumping:', threading.active_count()
for t in threading.enumerate():
print '}} ', t.name
print stacktraces()
print stacktraces.trace()
log.notice('abort: user terminated')
killall(tests)
sys.exit(1)
finally:
console.restore(stdtty)