[Lldb-commits] [lldb] r116552 - in /lldb/trunk/test: dotest.py foundation/TestObjCMethods.py lldbtest.py lldbutil.py
Johnny Chen
johnny.chen at apple.com
Thu Oct 14 18:18:30 PDT 2010
Author: johnny
Date: Thu Oct 14 20:18:29 2010
New Revision: 116552
URL: http://llvm.org/viewvc/llvm-project?rev=116552&view=rev
Log:
This is an initial version of test driver enhanceent to be able to dump the
session info after a test case failure, allowing more direct inspection of
debugger session which leads to the test failure.
For a simple usage scenario:
[18:06:26] johnny:/Volumes/data/lldb/svn/trunk/test $ ./dotest.py -v . 2> ~/Developer/Log/lldbtest.log
...
[18:14:43] johnny:/Volumes/data/lldb/svn/trunk/test $ ls -l .session-*
-rw-r--r-- 1 johnny admin 1359 Oct 14 18:06 .session-TestArrayTypes.ArrayTypesTestCase.test_with_dwarf_and_run_command
-rw-r--r-- 1 johnny admin 2054 Oct 14 18:07 .session-TestClassTypes.ClassTypesTestCase.test_with_dsym_and_expr_parser
-rw-r--r-- 1 johnny admin 2055 Oct 14 18:07 .session-TestClassTypes.ClassTypesTestCase.test_with_dwarf_and_expr_parser
-rw-r--r-- 1 johnny admin 1351 Oct 14 17:57 .session-TestClassTypes.ClassTypesTestCase.test_with_dwarf_and_run_command
[18:14:51] johnny:/Volumes/data/lldb/svn/trunk/test $
The test case which failed will have its recorded session info dumped to a
.session-* file in the current working directory. For test suite using
relocated directory, expect to find the .session-* files there.
In this checkin, I also add @skip decorator to the two test methods in
test/foundation/TestObjCMethods.py as it looks like the test suite is
deadlocking when running the tests. More investigations are needed.
Modified:
lldb/trunk/test/dotest.py
lldb/trunk/test/foundation/TestObjCMethods.py
lldb/trunk/test/lldbtest.py
lldb/trunk/test/lldbutil.py
Modified: lldb/trunk/test/dotest.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/dotest.py?rev=116552&r1=116551&r2=116552&view=diff
==============================================================================
--- lldb/trunk/test/dotest.py (original)
+++ lldb/trunk/test/dotest.py Thu Oct 14 20:18:29 2010
@@ -597,7 +597,29 @@
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.
Modified: lldb/trunk/test/foundation/TestObjCMethods.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/foundation/TestObjCMethods.py?rev=116552&r1=116551&r2=116552&view=diff
==============================================================================
--- lldb/trunk/test/foundation/TestObjCMethods.py (original)
+++ lldb/trunk/test/foundation/TestObjCMethods.py Thu Oct 14 20:18:29 2010
@@ -23,6 +23,7 @@
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 @@
self.buildDsym()
self.data_type_and_expr_objc()
+ @unittest2.skip("Skip due to deadlock?")
@unittest2.expectedFailure
# rdar://problem/8542091
# rdar://problem/8492646
Modified: lldb/trunk/test/lldbtest.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lldbtest.py?rev=116552&r1=116551&r2=116552&view=diff
==============================================================================
--- lldb/trunk/test/lldbtest.py (original)
+++ lldb/trunk/test/lldbtest.py Thu Oct 14 20:18:29 2010
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
# 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
Modified: lldb/trunk/test/lldbutil.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lldbutil.py?rev=116552&r1=116551&r2=116552&view=diff
==============================================================================
--- lldb/trunk/test/lldbutil.py (original)
+++ lldb/trunk/test/lldbutil.py Thu Oct 14 20:18:29 2010
@@ -6,6 +6,12 @@
import sys
import StringIO
+################################################
+# #
+# Iterator for lldb aggregate data structures. #
+# #
+################################################
+
def lldb_iter(obj, getsize, getelem):
"""
A generator adaptor for lldb aggregate data structures.
More information about the lldb-commits
mailing list