diff --git a/dist/pythonlibs/testrunner/__init__.py b/dist/pythonlibs/testrunner/__init__.py index 9d16cb3df76622531e1cbc673b3e04e2b7cd29ec..9ff2d9de3bae346a1c80281181fd47c69a09abcd 100755 --- a/dist/pythonlibs/testrunner/__init__.py +++ b/dist/pythonlibs/testrunner/__init__.py @@ -1,4 +1,5 @@ -# Copyright (C) 2017 Cenk Gündoğan <cenk.guendogan@haw-hamburg.de> +# Copyright (C) 2018-19 Freie Universität Berlin +# 2017 Cenk Gündoğan <cenk.guendogan@haw-hamburg.de> # 2016 Kaspar Schleiser <kaspar@schleiser.de> # 2014 Martine Lenders <mlenders@inf.fu-berlin.de> # @@ -7,51 +8,17 @@ # directory for more details. import os -import signal import sys -import subprocess -import time -from traceback import extract_tb, print_tb - +from traceback import print_tb import pexpect -PEXPECT_PATH = os.path.dirname(pexpect.__file__) -RIOTBASE = (os.environ.get('RIOTBASE') or - os.path.abspath(os.path.join(os.path.dirname(__file__), - "..", "..", ".."))) - -# Setting an empty 'TESTRUNNER_START_DELAY' environment variable use the -# default value (3) -MAKE_TERM_STARTED_DELAY = int(os.environ.get('TESTRUNNER_START_DELAY') or 3) - - -def list_until(l, cond): - return l[:([i for i, e in enumerate(l) if cond(e)][0])] - - -def find_exc_origin(exc_info): - pos = list_until(extract_tb(exc_info), - lambda frame: frame[0].startswith(PEXPECT_PATH) - )[-1] - return (pos[3], os.path.relpath(os.path.abspath(pos[0]), RIOTBASE), pos[1]) +from .spawn import find_exc_origin, setup_child, teardown_child +from .unittest import PexpectTestCase # noqa, F401 expose to users def run(testfunc, timeout=10, echo=True, traceback=False): - env = os.environ.copy() - child = pexpect.spawnu("make term", env=env, timeout=timeout, codec_errors='replace') - - # on many platforms, the termprog needs a short while to be ready... - time.sleep(MAKE_TERM_STARTED_DELAY) - - if echo: - child.logfile = sys.stdout - - try: - subprocess.check_output(('make', 'reset'), env=env, - stderr=subprocess.PIPE) - except subprocess.CalledProcessError: - # make reset yields error on some boards even if successful - pass + child = setup_child(timeout, env=os.environ, + logfile=sys.stdout if echo else None) try: testfunc(child) except pexpect.TIMEOUT: @@ -62,16 +29,12 @@ def run(testfunc, timeout=10, echo=True, traceback=False): return 1 except pexpect.EOF: trace = find_exc_origin(sys.exc_info()[2]) - print("Unexpected end of file in expect script at \"%s\" (%s:%d)" % trace) + print("Unexpected end of file in expect script at \"%s\" (%s:%d)" % + trace) if traceback: print_tb(sys.exc_info()[2]) return 1 finally: print("") - try: - os.killpg(os.getpgid(child.pid), signal.SIGKILL) - except ProcessLookupError: - print("Process already stopped") - - child.close() + teardown_child(child) return 0 diff --git a/dist/pythonlibs/testrunner/spawn.py b/dist/pythonlibs/testrunner/spawn.py new file mode 100644 index 0000000000000000000000000000000000000000..7beab825d5338dfabd4ee5e7617eb13426b7e496 --- /dev/null +++ b/dist/pythonlibs/testrunner/spawn.py @@ -0,0 +1,62 @@ +# Copyright (C) 2018-19 Freie Universität Berlin +# 2017 Cenk Gündoğan <cenk.guendogan@haw-hamburg.de> +# 2016 Kaspar Schleiser <kaspar@schleiser.de> +# 2014 Martine Lenders <mlenders@inf.fu-berlin.de> +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import os +import pexpect +import signal +import subprocess +import time +from traceback import extract_tb + +PEXPECT_PATH = os.path.dirname(pexpect.__file__) +RIOTBASE = (os.environ.get('RIOTBASE') or + os.path.abspath(os.path.join(os.path.dirname(__file__), + "..", "..", ".."))) + +# Setting an empty 'TESTRUNNER_START_DELAY' environment variable use the +# default value (3) +MAKE_TERM_STARTED_DELAY = int(os.environ.get('TESTRUNNER_START_DELAY') or 3) + + +def list_until(l, cond): + return l[:([i for i, e in enumerate(l) if cond(e)][0])] + + +def find_exc_origin(exc_info): + pos = list_until(extract_tb(exc_info), + lambda frame: frame[0].startswith(PEXPECT_PATH) + )[-1] + return (pos[3], os.path.relpath(os.path.abspath(pos[0]), RIOTBASE), pos[1]) + + +def setup_child(timeout=10, spawnclass=pexpect.spawnu, env=None, logfile=None): + child = spawnclass("make term", env=env, timeout=timeout, + codec_errors='replace') + + # on many platforms, the termprog needs a short while to be ready... + time.sleep(MAKE_TERM_STARTED_DELAY) + + child.logfile = logfile + + try: + subprocess.check_output(('make', 'reset'), env=env, + stderr=subprocess.PIPE) + except subprocess.CalledProcessError: + # make reset yields error on some boards even if successful + pass + return child + + +def teardown_child(child): + try: + os.killpg(os.getpgid(child.pid), signal.SIGKILL) + except ProcessLookupError: + print("Process already stopped") + + child.close() diff --git a/dist/pythonlibs/testrunner/unittest.py b/dist/pythonlibs/testrunner/unittest.py new file mode 100644 index 0000000000000000000000000000000000000000..4ee7b25959db147d22d8456d9d31d138ebfc87dd --- /dev/null +++ b/dist/pythonlibs/testrunner/unittest.py @@ -0,0 +1,23 @@ +# Copyright (C) 2018-19 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import unittest +from testrunner import setup_child, teardown_child + + +class PexpectTestCase(unittest.TestCase): + TIMEOUT = 10 + LOGFILE = None + + """A unittest TestCase providing a pexpect spawn object to it's tests + """ + @classmethod + def setUpClass(cls): + cls.spawn = setup_child(cls.TIMEOUT, logfile=cls.LOGFILE) + + @classmethod + def tearDownClass(cls): + teardown_child(cls.spawn)