aboutsummaryrefslogtreecommitdiff
path: root/debug/testlib.py
diff options
context:
space:
mode:
Diffstat (limited to 'debug/testlib.py')
-rw-r--r--debug/testlib.py196
1 files changed, 189 insertions, 7 deletions
diff --git a/debug/testlib.py b/debug/testlib.py
index 0da09a1..caf2140 100644
--- a/debug/testlib.py
+++ b/debug/testlib.py
@@ -2,7 +2,9 @@ import os.path
import re
import shlex
import subprocess
+import sys
import time
+import traceback
import pexpect
@@ -115,12 +117,7 @@ class VcsSim(object):
class Openocd(object):
logname = "openocd.log"
- def __init__(self, cmd=None, config=None, debug=False, otherProcess=None):
-
- # keep handles to other processes -- don't let them be
- # garbage collected yet.
-
- self.otherProcess = otherProcess
+ def __init__(self, cmd=None, config=None, debug=False):
if cmd:
cmd = shlex.split(cmd)
else:
@@ -147,7 +144,8 @@ class Openocd(object):
if "Examined RISCV core" in log:
break
if not self.process.poll() is None:
- raise Exception("OpenOCD exited before completing riscv_examine()")
+ raise Exception(
+ "OpenOCD exited before completing riscv_examine()")
if not messaged and time.time() - start > 1:
messaged = True
print "Waiting for OpenOCD to examine RISCV core..."
@@ -159,6 +157,17 @@ class Openocd(object):
except OSError:
pass
+class OpenocdCli(object):
+ def __init__(self, port=4444):
+ self.child = pexpect.spawn("sh -c 'telnet localhost %d | tee openocd-cli.log'" % port)
+ self.child.expect("> ")
+
+ def command(self, cmd):
+ self.child.sendline(cmd)
+ self.child.expect("\n")
+ self.child.expect("> ")
+ return self.child.before.strip()
+
class CannotAccess(Exception):
def __init__(self, address):
Exception.__init__(self)
@@ -239,3 +248,176 @@ class Gdb(object):
assert "not defined" not in output
assert "Hardware assisted breakpoint" in output
return output
+
+def run_all_tests(module, target, tests, fail_fast):
+ good_results = set(('pass', 'not_applicable'))
+
+ start = time.time()
+
+ results = {}
+ count = 0
+ for name in dir(module):
+ definition = getattr(module, name)
+ if type(definition) == type and hasattr(definition, 'test') and \
+ (not tests or any(test in name for test in tests)):
+ instance = definition(target)
+ result = instance.run()
+ results.setdefault(result, []).append(name)
+ count += 1
+ if result not in good_results and fail_fast:
+ break
+
+ header("ran %d tests in %.0fs" % (count, time.time() - start), dash=':')
+
+ result = 0
+ for key, value in results.iteritems():
+ print "%d tests returned %s" % (len(value), key)
+ if key not in good_results:
+ result = 1
+ for test in value:
+ print " ", test
+
+ return result
+
+def add_test_run_options(parser):
+ parser.add_argument("--fail-fast", "-f", action="store_true",
+ help="Exit as soon as any test fails.")
+ parser.add_argument("test", nargs='*',
+ help="Run only tests that are named here.")
+
+def header(title, dash='-'):
+ dashes = dash * (36 - len(title))
+ before = dashes[:len(dashes)/2]
+ after = dashes[len(dashes)/2:]
+ print "%s[ %s ]%s" % (before, title, after)
+
+class BaseTest(object):
+ compiled = {}
+ logs = []
+
+ def __init__(self, target):
+ self.target = target
+ self.server = None
+ self.target_process = None
+ self.binary = None
+ self.start = 0
+
+ def early_applicable(self):
+ """Return a false value if the test has determined it cannot run
+ without ever needing to talk to the target or server."""
+ # pylint: disable=no-self-use
+ return True
+
+ def setup(self):
+ pass
+
+ def compile(self):
+ compile_args = getattr(self, 'compile_args', None)
+ if compile_args:
+ if compile_args not in BaseTest.compiled:
+ try:
+ # pylint: disable=star-args
+ BaseTest.compiled[compile_args] = \
+ self.target.compile(*compile_args)
+ except Exception: # pylint: disable=broad-except
+ print "exception while compiling in %.2fs" % (
+ time.time() - self.start)
+ print "=" * 40
+ header("Traceback")
+ traceback.print_exc(file=sys.stdout)
+ print "/" * 40
+ return "exception"
+ self.binary = BaseTest.compiled.get(compile_args)
+
+ def classSetup(self):
+ self.compile()
+ self.target_process = self.target.target()
+ self.server = self.target.server()
+ self.logs.append(self.server.logname)
+
+ def classTeardown(self):
+ del self.server
+ del self.target_process
+
+ def run(self):
+ """
+ If compile_args is set, compile a program and set self.binary.
+
+ Call setup().
+
+ Then call test() and return the result, displaying relevant information
+ if an exception is raised.
+ """
+
+ print "Running", type(self).__name__, "...",
+ sys.stdout.flush()
+
+ if not self.early_applicable():
+ print "not_applicable"
+ return "not_applicable"
+
+ self.start = time.time()
+
+ self.classSetup()
+
+ try:
+ self.setup()
+ result = self.test() # pylint: disable=no-member
+ except Exception as e: # pylint: disable=broad-except
+ if isinstance(e, TestFailed):
+ result = "fail"
+ else:
+ result = "exception"
+ print "%s in %.2fs" % (result, time.time() - self.start)
+ print "=" * 40
+ if isinstance(e, TestFailed):
+ header("Message")
+ print e.message
+ header("Traceback")
+ traceback.print_exc(file=sys.stdout)
+ for log in self.logs:
+ header(log)
+ print open(log, "r").read()
+ print "/" * 40
+ return result
+
+ finally:
+ self.classTeardown()
+
+ if not result:
+ result = 'pass'
+ print "%s in %.2fs" % (result, time.time() - self.start)
+ return result
+
+class TestFailed(Exception):
+ def __init__(self, message):
+ Exception.__init__(self)
+ self.message = message
+
+def assertEqual(a, b):
+ if a != b:
+ raise TestFailed("%r != %r" % (a, b))
+
+def assertNotEqual(a, b):
+ if a == b:
+ raise TestFailed("%r == %r" % (a, b))
+
+def assertIn(a, b):
+ if a not in b:
+ raise TestFailed("%r not in %r" % (a, b))
+
+def assertNotIn(a, b):
+ if a in b:
+ raise TestFailed("%r in %r" % (a, b))
+
+def assertGreater(a, b):
+ if not a > b:
+ raise TestFailed("%r not greater than %r" % (a, b))
+
+def assertTrue(a):
+ if not a:
+ raise TestFailed("%r is not True" % a)
+
+def assertRegexpMatches(text, regexp):
+ if not re.search(regexp, text):
+ raise TestFailed("can't find %r in %r" % (regexp, text))