summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xlldb/test/dotest.py24
-rw-r--r--lldb/test/foundation/TestObjCMethods.py2
-rw-r--r--lldb/test/lldbtest.py141
-rw-r--r--lldb/test/lldbutil.py6
4 files changed, 137 insertions, 36 deletions
diff --git a/lldb/test/dotest.py b/lldb/test/dotest.py
index 4afb769fd3c..5172ac57dd8 100755
--- a/lldb/test/dotest.py
+++ b/lldb/test/dotest.py
@@ -597,7 +597,29 @@ for ia in range(len(archs) if iterArchs else 1):
suite.countTestCases() != 1 and "s" or ""))
# Invoke the test runner.
- result = unittest2.TextTestRunner(stream=sys.stderr, verbosity=verbose).run(suite)
+ class LLDBTestResult(unittest2.TextTestResult):
+ """
+ Enforce a singleton pattern to allow inspection of test progress.
+ """
+ __singleton__ = None
+
+ def __init__(self, *args):
+ if LLDBTestResult.__singleton__:
+ raise "LLDBTestResult instantiated more than once"
+ super(LLDBTestResult, self).__init__(*args)
+ LLDBTestResult.__singleton__ = self
+ # Now put this singleton into the lldb module namespace.
+ lldb.test_result = self
+
+ def addFailure(self, test, err):
+ super(LLDBTestResult, self).addFailure(test, err)
+ method = getattr(test, "markFailure", None)
+ if method:
+ method()
+ setattr(test, "__failed__", True)
+
+ result = unittest2.TextTestRunner(stream=sys.stderr, verbosity=verbose,
+ resultclass=LLDBTestResult).run(suite)
# Terminate the test suite if ${LLDB_TESTSUITE_FORCE_FINISH} is defined.
diff --git a/lldb/test/foundation/TestObjCMethods.py b/lldb/test/foundation/TestObjCMethods.py
index b9ac8a5828c..34b5f6984f9 100644
--- a/lldb/test/foundation/TestObjCMethods.py
+++ b/lldb/test/foundation/TestObjCMethods.py
@@ -23,6 +23,7 @@ class FoundationTestCase(TestBase):
self.buildDwarf()
self.break_on_objc_methods()
+ @unittest2.skip("Skip due to deadlock?")
@unittest2.expectedFailure
# rdar://problem/8542091
# rdar://problem/8492646
@@ -31,6 +32,7 @@ class FoundationTestCase(TestBase):
self.buildDsym()
self.data_type_and_expr_objc()
+ @unittest2.skip("Skip due to deadlock?")
@unittest2.expectedFailure
# rdar://problem/8542091
# rdar://problem/8492646
diff --git a/lldb/test/lldbtest.py b/lldb/test/lldbtest.py
index 373f8bfc08b..c64e666e601 100644
--- a/lldb/test/lldbtest.py
+++ b/lldb/test/lldbtest.py
@@ -99,6 +99,7 @@ $
import os, sys, traceback
import re
from subprocess import *
+import StringIO
import time
import types
import unittest2
@@ -244,6 +245,37 @@ def pointer_size():
return 8 * ctypes.sizeof(a_pointer)
+class recording(StringIO.StringIO):
+ """
+ A nice little context manager for recording the debugger interactions into
+ our session object. If trace flag is ON, it also emits the interactions
+ into the stderr.
+ """
+ def __init__(self, test, trace):
+ """Create a StringIO instance; record session, stderr, and trace."""
+ StringIO.StringIO.__init__(self)
+ self.session = test.session
+ self.stderr = test.old_stderr
+ self.trace = trace
+
+ def __enter__(self):
+ """
+ Context management protocol on entry to the body of the with statement.
+ Just return the StringIO object.
+ """
+ return self
+
+ def __exit__(self, type, value, tb):
+ """
+ Context management protocol on exit from the body of the with statement.
+ If trace is ON, it emits the recordings into stderr. Always add the
+ recordings to our session object. And close the StringIO object, too.
+ """
+ if self.trace:
+ print >> self.stderr, self.getvalue()
+ print >> self.session, self.getvalue()
+ self.close()
+
class TestBase(unittest2.TestCase):
"""This LLDB abstract base class is meant to be subclassed."""
@@ -369,10 +401,39 @@ class TestBase(unittest2.TestCase):
self.dict = None
self.doTearDownCleanup = False
+ # Create a string buffer to record the session info.
+ self.session = StringIO.StringIO()
+
+ # Substitute self.session as the sys.stderr and restore it at the end of
+ # the test during tearDown(). If trace is ON, we dump the session info
+ # into the real stderr as well. The session info will be dumped into a
+ # test case specific file if a failure is encountered.
+ self.old_stderr = sys.stderr
+ sys.stderr = self.session
+
def setTearDownCleanup(self, dictionary=None):
self.dict = dictionary
self.doTearDownCleanup = True
+ def markFailure(self):
+ """Callback invoked when we (the test case instance) failed."""
+ with recording(self, False) as sbuf:
+ # False because there's no need to write "FAIL" to the stderr again.
+ print >> sbuf, "FAIL"
+
+ def dumpSessionInfo(self):
+ """
+ Dump the debugger interactions leading to a test failure. This allows
+ for more convenient postmortem analysis.
+ """
+ for test, err in lldb.test_result.failures:
+ if test is self:
+ print >> self.session, err
+
+ fname = os.path.join(os.environ["LLDB_TEST"], ".session-" + self.id())
+ with open(fname, "w") as f:
+ print >> f, self.session.getvalue()
+
def tearDown(self):
#import traceback
#traceback.print_stack()
@@ -393,6 +454,15 @@ class TestBase(unittest2.TestCase):
if not module.cleanup(dictionary=self.dict):
raise Exception("Don't know how to do cleanup")
+ # lldb.test_result is an instance of unittest2.TextTestResult enforced
+ # as a singleton. During tearDown(), lldb.test_result can be consulted
+ # in order to determine whether we failed for the current test instance.
+ if getattr(self, "__failed__", False):
+ self.dumpSessionInfo()
+
+ # Restore the sys.stderr to what it was before.
+ sys.stderr = self.old_stderr
+
def runCmd(self, cmd, msg=None, check=True, trace=False, setCookie=True):
"""
Ask the command interpreter to handle the command and then check its
@@ -409,13 +479,13 @@ class TestBase(unittest2.TestCase):
for i in range(self.maxLaunchCount if running else 1):
self.ci.HandleCommand(cmd, self.res)
- if trace:
- print >> sys.stderr, "runCmd:", cmd
+ with recording(self, trace) as sbuf:
+ print >> sbuf, "runCmd:", cmd
if self.res.Succeeded():
- print >> sys.stderr, "output:", self.res.GetOutput()
+ print >> sbuf, "output:", self.res.GetOutput()
else:
- print >> sys.stderr, "runCmd failed!"
- print >> sys.stderr, self.res.GetError()
+ print >> sbuf, "runCmd failed!"
+ print >> sbuf, self.res.GetError()
if running:
# For process launch, wait some time before possible next try.
@@ -423,8 +493,9 @@ class TestBase(unittest2.TestCase):
if self.res.Succeeded():
break
- elif running:
- print >> sys.stderr, "Command '" + cmd + "' failed!"
+ elif running:
+ with recording(self, True) as sbuf:
+ print >> sbuf, "Command '" + cmd + "' failed!"
# Modify runStarted only if "run" or "process launch" was encountered.
if running:
@@ -474,35 +545,35 @@ class TestBase(unittest2.TestCase):
else:
# No execution required, just compare str against the golden input.
output = str
- if trace:
- print >> sys.stderr, "looking at:", output
+ with recording(self, trace) as sbuf:
+ print >> sbuf, "looking at:", output
# The heading says either "Expecting" or "Not expecting".
- if trace:
- heading = "Expecting" if matching else "Not expecting"
+ heading = "Expecting" if matching else "Not expecting"
# Start from the startstr, if specified.
# If there's no startstr, set the initial state appropriately.
matched = output.startswith(startstr) if startstr else (True if matching else False)
- if startstr and trace:
- print >> sys.stderr, "%s start string: %s" % (heading, startstr)
- print >> sys.stderr, "Matched" if matched else "Not matched"
- print >> sys.stderr
+ if startstr:
+ with recording(self, trace) as sbuf:
+ print >> sbuf, "%s start string: %s" % (heading, startstr)
+ print >> sbuf, "Matched" if matched else "Not matched"
+ print >> sbuf
# Look for sub strings, if specified.
keepgoing = matched if matching else not matched
if substrs and keepgoing:
for str in substrs:
matched = output.find(str) != -1
- if trace:
- print >> sys.stderr, "%s sub string: %s" % (heading, str)
- print >> sys.stderr, "Matched" if matched else "Not matched"
+ with recording(self, trace) as sbuf:
+ print >> sbuf, "%s sub string: %s" % (heading, str)
+ print >> sbuf, "Matched" if matched else "Not matched"
keepgoing = matched if matching else not matched
if not keepgoing:
break
- if trace:
- print >> sys.stderr
+ with recording(self, trace) as sbuf:
+ print >> sbuf
# Search for regular expression patterns, if specified.
keepgoing = matched if matching else not matched
@@ -510,14 +581,14 @@ class TestBase(unittest2.TestCase):
for pattern in patterns:
# Match Objects always have a boolean value of True.
matched = bool(re.search(pattern, output))
- if trace:
- print >> sys.stderr, "%s pattern: %s" % (heading, pattern)
- print >> sys.stderr, "Matched" if matched else "Not matched"
+ with recording(self, trace) as sbuf:
+ print >> sbuf, "%s pattern: %s" % (heading, pattern)
+ print >> sbuf, "Matched" if matched else "Not matched"
keepgoing = matched if matching else not matched
if not keepgoing:
break
- if trace:
- print >> sys.stderr
+ with recording(self, trace) as sbuf:
+ print >> sbuf
self.assertTrue(matched if matching else not matched,
msg if msg else CMD_MSG(str, exe))
@@ -531,8 +602,8 @@ class TestBase(unittest2.TestCase):
self.assertTrue(inspect.ismethod(method),
name + "is a method name of object: " + str(obj))
result = method()
- if trace:
- print >> sys.stderr, str(method) + ":", result
+ with recording(self, trace) as sbuf:
+ print >> sbuf, str(method) + ":", result
return result
def breakAfterLaunch(self, process, func, trace=False):
@@ -548,28 +619,28 @@ class TestBase(unittest2.TestCase):
# The stop reason of the thread should be breakpoint.
thread = process.GetThreadAtIndex(0)
SR = thread.GetStopReason()
- if trace:
- print >> sys.stderr, "StopReason =", StopReasonString(SR)
+ with recording(self, trace) as sbuf:
+ print >> sbuf, "StopReason =", StopReasonString(SR)
if SR == StopReasonEnum("Breakpoint"):
frame = thread.GetFrameAtIndex(0)
name = frame.GetFunction().GetName()
- if trace:
- print >> sys.stderr, "function =", name
+ with recording(self, trace) as sbuf:
+ print >> sbuf, "function =", name
if (name == func):
# We got what we want; now break out of the loop.
return True
# The inferior is in a transient state; continue the process.
time.sleep(1.0)
- if trace:
- print >> sys.stderr, "Continuing the process:", process
+ with recording(self, trace) as sbuf:
+ print >> sbuf, "Continuing the process:", process
process.Continue()
count = count + 1
if count == 15:
- if trace:
- print >> sys.stderr, "Reached 15 iterations, giving up..."
+ with recording(self, trace) as sbuf:
+ print >> sbuf, "Reached 15 iterations, giving up..."
# Enough iterations already, break out of the loop.
return False
diff --git a/lldb/test/lldbutil.py b/lldb/test/lldbutil.py
index 90a0b40f608..d7a1e32a791 100644
--- a/lldb/test/lldbutil.py
+++ b/lldb/test/lldbutil.py
@@ -6,6 +6,12 @@ import lldb
import sys
import StringIO
+################################################
+# #
+# Iterator for lldb aggregate data structures. #
+# #
+################################################
+
def lldb_iter(obj, getsize, getelem):
"""
A generator adaptor for lldb aggregate data structures.
OpenPOWER on IntegriCloud