[rtems-tools PATCH v2] tester/tftp: Add a session timeout
chrisj at rtems.org
chrisj at rtems.org
Sun Oct 8 06:11:36 UTC 2023
From: Chris Johns <chrisj at rtems.org>
- Fix listener done state
- Finish open with the state as finished
Closes #4959
---
tester/rt/config.py | 42 +++++++++++++++++++++---------
tester/rt/report.py | 2 +-
tester/rt/test.py | 1 +
tester/rt/tftp.py | 44 +++++++++++++++++++------------
tester/rt/tftpserver.py | 57 ++++++++++++++++++++++++++++++++++++-----
5 files changed, 110 insertions(+), 36 deletions(-)
diff --git a/tester/rt/config.py b/tester/rt/config.py
index 3b12c6c..139e1fa 100644
--- a/tester/rt/config.py
+++ b/tester/rt/config.py
@@ -284,7 +284,12 @@ class file(config.file):
raise error.general('invalid %tftp port')
self.kill_on_end = True
if not self.opts.dry_run():
+ if self.defined('session_timeout'):
+ session_timeout = int(self.expand('%{session_timeout}'))
+ else:
+ session_timeout = 120
self.process = tester.rt.tftp.tftp(bsp_arch, bsp,
+ session_timeout = session_timeout,
trace = self.exe_trace('tftp'))
if not self.in_error:
if self.console:
@@ -415,28 +420,41 @@ class file(config.file):
reset_target = True
else:
reset_target = False
- if self.target_start_regx is not None:
- if self.target_start_regx.match(text):
- if self.test_started:
- self._capture_console('target start detected')
+ if ('*** TIMEOUT TIMEOUT' in text or \
+ '*** TEST TOO LONG' in text) and \
+ self.defined('target_reset_on_timeout'):
+ reset_target = True
+ restart = \
+ (self.target_start_regx is not None and self.target_start_regx is not None)
+ if restart:
+ if self.test_started:
+ self._capture_console('target start detected')
+ ok_to_kill = True
+ else:
+ self.restarts += 1
+ if self.restarts > self.max_restarts:
+ self._capture_console('target restart maximum count reached')
ok_to_kill = True
else:
- self.restarts += 1
- if self.restarts > self.max_restarts:
- self._capture_console('target restart maximum count reached')
- ok_to_kill = True
- else:
- self.process.target_restart(self.test_started)
+ self.process.target_restart(self.test_started)
if not reset_target and self.target_reset_regx is not None:
if self.target_reset_regx.match(text):
self._capture_console('target reset condition detected')
self._target_command('reset')
self.process.target_reset(self.test_started)
if self.kill_on_end:
- if not ok_to_kill and '*** END OF TEST ' in text:
+ if not ok_to_kill and \
+ ('*** END OF TEST ' in text or \
+ '*** FATAL ***' in text or \
+ '*** TIMEOUT TIMEOUT' in text or \
+ '*** TEST TOO LONG' in text):
self._capture_console('test end: %s' % (self.test_label))
if self.test_label is not None:
- ok_to_kill = '*** END OF TEST %s ***' % (self.test_label) in text
+ ok_to_kill = \
+ '*** END OF TEST %s ***' % (self.test_label) in text or \
+ '*** FATAL ***' in text or \
+ '*** TIMEOUT TIMEOUT' in text or \
+ '*** TEST TOO LONG' in text
self.process.target_end()
text = [(self.console_prefix, l) for l in text.replace(chr(13), '').splitlines()]
if self.output is not None:
diff --git a/tester/rt/report.py b/tester/rt/report.py
index a688dc8..642ae73 100644
--- a/tester/rt/report.py
+++ b/tester/rt/report.py
@@ -327,7 +327,7 @@ class report(object):
for name in results:
if results[name]['result'] == state:
l += [' %s' % (path.basename(name))]
- return l
+ return sorted(l)
l = []
if self.failed:
l += ['Failures:']
diff --git a/tester/rt/test.py b/tester/rt/test.py
index 0e22002..db5939b 100644
--- a/tester/rt/test.py
+++ b/tester/rt/test.py
@@ -151,6 +151,7 @@ class test_run(object):
name = 'test[%s]' % path.basename(self.executable)
self.thread = threading.Thread(target = self.runner,
name = name)
+ self.thread.daemon = True
self.thread.start()
def is_alive(self):
diff --git a/tester/rt/tftp.py b/tester/rt/tftp.py
index 5a1c7b7..5d2e74a 100644
--- a/tester/rt/tftp.py
+++ b/tester/rt/tftp.py
@@ -49,7 +49,8 @@ import tester.rt.tftpserver
class tftp(object):
'''RTEMS Testing TFTP base.'''
- def __init__(self, bsp_arch, bsp, trace = False):
+ def __init__(self, bsp_arch, bsp, session_timeout, trace = False):
+ self.session_timeout = session_timeout
self.trace = trace
self.lock_trace = False
self.lock = threading.RLock()
@@ -60,7 +61,7 @@ class tftp(object):
def __del__(self):
self.kill()
- def _init(self):
+ def _init(self, state = 'reset'):
self.output_length = None
self.console = None
self.server = None
@@ -73,7 +74,7 @@ class tftp(object):
self.running = False
self.finished = False
self.caught = None
- self.target_state = 'reset'
+ self.target_state = state
def _lock(self, msg):
if self.lock_trace:
@@ -109,7 +110,8 @@ class tftp(object):
try:
if self.server is not None:
self.server.stop()
- self.finished = Finished
+ self.finished = finished
+ self._set_target_state('finished')
except:
pass
@@ -150,14 +152,15 @@ class tftp(object):
def _listener(self, exe):
self.server = tester.rt.tftpserver.tftp_server(host = 'all',
port = self.port,
+ session_timeout = self.session_timeout,
timeout = 10,
forced_file = exe,
sessions = 1)
try:
- if log.tracing:
+ if False and log.tracing:
self.server.trace_packets()
self.server.start()
- self.server.run()
+ return self.server.run() == 1
except:
self.server.stop()
raise
@@ -169,22 +172,30 @@ class tftp(object):
self.exe = None
self._unlock('_runner')
caught = None
+ retry = 0
+ target_loaded = False
try:
self._lock('_runner')
state = self.target_state
self._unlock('_runner')
self._trace('runner: ' + state)
- while state not in ['shutdown', 'finished']:
- if state != 'running':
+ while state not in ['shutdown', 'finished', 'timeout']:
+ if state in ['booting', 'running']:
+ time.sleep(0.25)
+ else:
self._trace('listening: begin: ' + state)
- self._listener(exe)
+ target_loaded = self._listener(exe)
self._lock('_runner')
- if self.target_state == 'booting':
- self._set_target_state('loaded')
+ if target_loaded:
+ self._set_target_state('booting')
+ else:
+ retry += 1
+ if retry > 1:
+ self._set_target_state('timeout')
+ self._timeout()
+ state = self.target_state
self._unlock('_runner')
self._trace('listening: end: ' + state)
- else:
- time.sleep(0.25)
self._lock('_runner')
state = self.target_state
self._unlock('_runner')
@@ -214,16 +225,17 @@ class tftp(object):
self.test_too_long = timeout[3]
self.opened = True
self.running = True
+ self._console('tftp: exe: %s' % (executable))
self.listener = threading.Thread(target = self._runner,
name = 'tftp-listener')
+ self.listener.daemon = True
self._unlock('_open: start listner')
- self._console('tftp: exe: %s' % (executable))
self.listener.start()
self._lock('_open: start listner')
step = 0.25
period = timeout[0]
seconds = timeout[1]
- output_len = self.output_length()
+ output_length = self.output_length()
while not self.finished and period > 0 and seconds > 0:
if not self.running and self.caught:
break
@@ -250,7 +262,7 @@ class tftp(object):
elif seconds == 0:
self._test_too_long()
caught = self.caught
- self._init()
+ self._init('finished')
self._unlock('_open')
if caught is not None:
reraise.reraise(*caught)
diff --git a/tester/rt/tftpserver.py b/tester/rt/tftpserver.py
index 92cd1fd..c200dad 100644
--- a/tester/rt/tftpserver.py
+++ b/tester/rt/tftpserver.py
@@ -453,14 +453,13 @@ class udp_handler(socketserver.BaseRequestHandler):
raise
self._notice('] tftp: %d: error: %s: %s' % (index, type(exp), exp))
self._notice('] tftp: %d: end: %s' % (index, client))
+ self.server.tftp.session_done()
def handle(self):
'''The UDP server handle method.'''
- if self.server.tftp.sessions is None \
- or self.server.tftp.session < self.server.tftp.sessions:
+ if self.server.tftp.sessions_available():
self.handle_session(self.server.tftp.next_session())
-
class udp_server(socketserver.ThreadingMixIn, socketserver.UDPServer):
'''UDP server. Default behaviour.'''
@@ -474,6 +473,7 @@ class tftp_server(object):
def __init__(self,
host,
port,
+ session_timeout=None,
timeout=10,
base=None,
forced_file=None,
@@ -484,6 +484,7 @@ class tftp_server(object):
self.notices = False
self.packet_trace = False
self.exception_is_raise = False
+ self.session_timeout = session_timeout
self.timeout = timeout
self.host = host
self.port = port
@@ -497,6 +498,7 @@ class tftp_server(object):
raise error.general('tftp session count is not a number')
self.sessions = sessions
self.session = 0
+ self.sessions_done = 0
self.reader = reader
def __del__(self):
@@ -542,6 +544,8 @@ class tftp_server(object):
def run(self):
'''Run the TFTP server for the specified number of sessions.'''
running = True
+ session_timeout = self.session_timeout
+ last_session = 0
while running:
period = 1
self._lock()
@@ -549,7 +553,7 @@ class tftp_server(object):
running = False
period = 0
elif self.sessions is not None:
- if self.sessions == 0:
+ if self.sessions_done >= self.sessions:
running = False
period = 0
else:
@@ -557,7 +561,24 @@ class tftp_server(object):
self._unlock()
if period > 0:
time.sleep(period)
+ if session_timeout is not None:
+ session = self.get_session()
+ if last_session != session:
+ last_session = session
+ session_timeout = self.session_timeout
+ else:
+ if session_timeout < period:
+ session_timeout = 0
+ else:
+ session_timeout -= period
+ if session_timeout == 0:
+ log.trace('] tftp: server: session timeout')
+ running = False
self.stop()
+ self._lock()
+ sessions_done = self.sessions_done
+ self._unlock()
+ return sessions_done
def get_session(self):
'''Return the session count.'''
@@ -580,6 +601,24 @@ class tftp_server(object):
self._unlock()
return count
+ def sessions_available(self):
+ '''Return True is there are available sessions.'''
+ available = False
+ self._lock()
+ try:
+ available = self.sessions is None or self.session < self.sessions
+ finally:
+ self._unlock()
+ return available
+
+ def session_done(self):
+ '''Call when a session is done.'''
+ self._lock()
+ try:
+ self.sessions_done += 1
+ finally:
+ self._unlock()
+
def enable_notices(self):
'''Call to enable notices. The server is quiet without this call.'''
self._lock()
@@ -654,10 +693,14 @@ def run(args=sys.argv, command_path=None):
help='port to bind the server too (default: %(default)s).',
type=int,
default='69')
+ argsp.add_argument('-S', '--session-timeout',
+ help='timeout in seconds, client can override ' \
+ '(default: %(default)s).',
+ type = int, default=None)
argsp.add_argument('-t', '--timeout',
- help = 'timeout in seconds, client can override ' \
+ help='timeout in seconds, client can override ' \
'(default: %(default)s).',
- type = int, default = '10')
+ type=int, default='10')
argsp.add_argument(
'-b',
'--base',
@@ -682,7 +725,7 @@ def run(args=sys.argv, command_path=None):
log.output(log.info(args))
log.tracing = argopts.trace
- server = tftp_server(argopts.bind, argopts.port, argopts.timeout,
+ server = tftp_server(argopts.bind, argopts.port, argopts.session_timeout, argopts.timeout,
argopts.base, argopts.force_file,
argopts.sessions)
server.enable_notices()
--
2.37.1
More information about the devel
mailing list