From ea88e94318492db7c29808a145daa0653a1feae5 Mon Sep 17 00:00:00 2001 From: Johnny Chen Date: Tue, 21 Sep 2010 21:08:53 +0000 Subject: [PATCH] Added a more complex test case of breakpoint commands, which executes a sequence of 'breakpoint command add/list/remove' commands to set breakpoint callbacks, list them, and then remove one. Modified the lldbtest.TestBase.expect() method to add two additional keyword arguments: o matching (default to True), which, if set to False, reverses the semantics of 'expect' to 'expect not' o patterns (default to None), which specifies a list of regexp patterns to match against the output from running the command TestBreakpointCommand.py uses the matching=False and the patterns=[...] expect() API. llvm-svn: 114480 --- lldb/test/breakpoint_command/Makefile | 5 + .../TestBreakpointCommand.py | 115 ++++++++++++++++++ lldb/test/breakpoint_command/main.c | 13 ++ lldb/test/lldbtest.py | 47 +++++-- 4 files changed, 172 insertions(+), 8 deletions(-) create mode 100644 lldb/test/breakpoint_command/Makefile create mode 100644 lldb/test/breakpoint_command/TestBreakpointCommand.py create mode 100644 lldb/test/breakpoint_command/main.c diff --git a/lldb/test/breakpoint_command/Makefile b/lldb/test/breakpoint_command/Makefile new file mode 100644 index 000000000000..d6cd0db0506f --- /dev/null +++ b/lldb/test/breakpoint_command/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/breakpoint_command/TestBreakpointCommand.py b/lldb/test/breakpoint_command/TestBreakpointCommand.py new file mode 100644 index 000000000000..4f3a99327bfe --- /dev/null +++ b/lldb/test/breakpoint_command/TestBreakpointCommand.py @@ -0,0 +1,115 @@ +""" +Test lldb breakpoint command add/list/remove. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * + +class BreakpointCommandTestCase(TestBase): + + mydir = "breakpoint_command" + + @classmethod + def classCleanup(cls): + system(["/bin/sh", "-c", "rm output.txt"]) + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_with_dsym(self): + """Test a sequence of breakpoint command add, list, and remove.""" + self.buildDsym() + self.breakpoint_command_sequence() + + def test_with_dwarf(self): + """Test a sequence of breakpoint command add, list, and remove.""" + self.buildDwarf() + self.breakpoint_command_sequence() + + def breakpoint_command_sequence(self): + """Test a sequence of breakpoint command add, list, and remove.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add two breakpoints on the same line. + self.expect("breakpoint set -f main.c -l 12", BREAKPOINT_CREATED, + startstr = "Breakpoint created: 1: file ='main.c', line = 12, locations = 1") + self.expect("breakpoint set -f main.c -l 12", BREAKPOINT_CREATED, + startstr = "Breakpoint created: 2: file ='main.c', line = 12, locations = 1") + + # Now add callbacks for the breakpoints just created. + self.runCmd("breakpoint command add -c -o 'frame variable -s' 1") + self.runCmd("breakpoint command add -p -o 'here = open(\"output.txt\", \"w\"); print >> here, \"lldb\"; here.close()' 2") + + # Check that the breakpoint commands are correctly set. + + # The breakpoint list now only contains breakpoint 1. + self.expect("breakpoint list", "Breakpoints 1 & 2 created", + substrs = ["1: file ='main.c', line = 12, locations = 1", + "2: file ='main.c', line = 12, locations = 1"], + patterns = ["1.1: .+at main.c:12, .+unresolved, hit count = 0", + "2.1: .+at main.c:12, .+unresolved, hit count = 0"]) + + self.expect("breakpoint command list 1", "Breakpoint 1 command ok", + substrs = ["Breakpoint commands:", + "frame variable -s"]) + self.expect("breakpoint command list 2", "Breakpoint 2 command ok", + substrs = ["Breakpoint commands:", + "here = open", + "print >> here", + "here.close()"]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Check that the file 'output.txt' exists and contains the string "lldb". + + # Read the output file produced by running the program. + output = open('output.txt', 'r').read() + + self.assertTrue(output.startswith("lldb"), + "File 'output.txt' and the content matches") + + # Finish the program. + self.runCmd("process continue") + + # Remove the breakpoint command associated with breakpoint 1. + self.runCmd("breakpoint command remove 1") + + # Remove breakpoint 2. + self.runCmd("breakpoint delete 2") + + self.expect("breakpoint command list 1", + startstr = "Breakpoint 1 does not have an associated command.") + self.expect("breakpoint command list 2", error=True, + startstr = "error: '2' is not a currently valid breakpoint id.") + + # The breakpoint list now only contains breakpoint 1. + self.expect("breakpoint list", "Breakpoint 1 exists", + substrs = ["1: file ='main.c', line = 12, locations = 1, resolved = 1", + "hit count = 1"]) + + # Not breakpoint 2. + self.expect("breakpoint list", "No more breakpoint 2", matching=False, + substrs = ["2: file ='main.c', line = 12, locations = 1, resolved = 1"]) + + # Run the program again, with breakpoint 1 remaining. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to breakpoint 1. + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['state is Stopped', + 'stop reason = breakpoint']) + + # The breakpoint should have a hit count of 2. + self.expect("breakpoint list", BREAKPOINT_HIT_ONCE, + substrs = ['resolved, hit count = 2']) + + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/breakpoint_command/main.c b/lldb/test/breakpoint_command/main.c new file mode 100644 index 000000000000..343eac7e554a --- /dev/null +++ b/lldb/test/breakpoint_command/main.c @@ -0,0 +1,13 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +int main (int argc, char const *argv[]) +{ + return 0; +} diff --git a/lldb/test/lldbtest.py b/lldb/test/lldbtest.py index 65807c703cb8..6381a035c434 100644 --- a/lldb/test/lldbtest.py +++ b/lldb/test/lldbtest.py @@ -97,6 +97,7 @@ $ """ import os, sys +import re from subprocess import * import time import types @@ -399,19 +400,24 @@ class TestBase(unittest2.TestCase): self.assertTrue(self.res.Succeeded(), msg if msg else CMD_MSG(cmd)) - def expect(self, cmd, msg=None, startstr=None, substrs=None, trace=False, error=False): + def expect(self, cmd, msg=None, patterns=None, startstr=None, substrs=None, trace=False, error=False, matching=True): """ Similar to runCmd; with additional expect style output matching ability. Ask the command interpreter to handle the command and then check its return status. The 'msg' parameter specifies an informational assert message. We expect the output from running the command to start with - 'startstr' and matches the substrings contained in 'substrs'. + 'startstr', matches the substrings contained in 'substrs', and regexp + matches the patterns contained in 'patterns'. If the keyword argument error is set to True, it signifies that the API client is expecting the command to fail. In this case, the error stream from running the command is retrieved and compared against the golden input, instead. + + If the keyword argument matching is set to False, it signifies that the API + client is expecting the output of the command not to match the golden + input. """ trace = (True if traceAlways else trace) @@ -426,25 +432,50 @@ class TestBase(unittest2.TestCase): self.assertFalse(self.res.Succeeded(), "Command '" + cmd + "' is expected to fail!") - matched = output.startswith(startstr) if startstr else True + # The heading says either "Expecting" or "Not expecting". + if trace: + 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, "Expecting start string:", startstr + print >> sys.stderr, "%s start string: %s" % (heading, startstr) print >> sys.stderr, "Matched" if matched else "Not matched" print >> sys.stderr - if substrs and matched: + # 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) > 0 if trace: - print >> sys.stderr, "Expecting sub string:", str + print >> sys.stderr, "%s sub string: %s" % (heading, str) + print >> sys.stderr, "Matched" if matched else "Not matched" + keepgoing = matched if matching else not matched + if not keepgoing: + break + if trace: + print >> sys.stderr + + # Search for regular expression patterns, if specified. + keepgoing = matched if matching else not matched + if patterns and keepgoing: + 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" - if not matched: + keepgoing = matched if matching else not matched + if not keepgoing: break if trace: print >> sys.stderr - self.assertTrue(matched, msg if msg else CMD_MSG(cmd)) + self.assertTrue(matched if matching else not matched, + msg if msg else CMD_MSG(cmd)) def invoke(self, obj, name, trace=False): """Use reflection to call a method dynamically with no argument.""" -- GitLab