Added a new kind of test case: the "inline" test
authorSean Callanan <scallanan@apple.com>
Thu, 16 Oct 2014 23:15:22 +0000 (23:15 +0000)
committerSean Callanan <scallanan@apple.com>
Thu, 16 Oct 2014 23:15:22 +0000 (23:15 +0000)
case.  This test case style attempts to shed all
of the boilerplate that is required for test
cases, and let 80% of test cases use a much terser
syntax.

Inline testcases have much simplified python files
(the corresponding .py file should contain two
lines of code) and require no Makefile, because the
Makefile is generated automatically.  Breakpoints
are set automatically and the indicated breakpoint
actions (specified after a magic //% comment) are
executed when the breakpoint is hit.

All other testcases are unaffected.

One thing I'm not really happy with yet is the way
multiple actions for the same line are specified.
I'm going to use lang/c/struct_types as a guinea
pig to develop this further.

llvm-svn: 219984

lldb/test/lang/c/struct_types/Makefile [deleted file]
lldb/test/lang/c/struct_types/TestStructTypes.py
lldb/test/lang/c/struct_types/cmds.txt [deleted file]
lldb/test/lang/c/struct_types/main.c
lldb/test/lldbinline.py [new file with mode: 0644]

diff --git a/lldb/test/lang/c/struct_types/Makefile b/lldb/test/lang/c/struct_types/Makefile
deleted file mode 100644 (file)
index b09a579..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-LEVEL = ../../../make
-
-C_SOURCES := main.c
-
-include $(LEVEL)/Makefile.rules
index 3e25dff..a70d2ca 100644 (file)
@@ -1,102 +1,3 @@
-"""
-Test that break on a struct declaration has no effect.
+import lldbinline
 
-Instead, the first executable statement is set as the breakpoint.
-"""
-
-import os, time
-import unittest2
-import lldb
-from lldbtest import *
-import lldbutil
-
-class StructTypesTestCase(TestBase):
-
-    mydir = TestBase.compute_mydir(__file__)
-
-    # rdar://problem/12566646
-    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
-    @dsym_test
-    def test_with_dsym(self):
-        """Test that break on a struct declaration has no effect."""
-        self.buildDsym()
-        self.struct_types()
-
-    # rdar://problem/12566646
-    @expectedFailureIcc # llvm.org/pr16793
-                        # ICC generates DW_AT_byte_size zero with a zero-length 
-                        # array and LLDB doesn't process it correctly.
-    @dwarf_test
-    def test_with_dwarf(self):
-        """Test that break on a struct declaration has no effect."""
-        self.buildDwarf()
-        self.struct_types()
-
-    def setUp(self):
-        # Call super's setUp().
-        TestBase.setUp(self)
-        # Find the line number to break for main.c.
-        self.source = 'main.c'
-        self.line = line_number(self.source, '// Set break point at this line.')
-        self.first_executable_line = line_number(self.source,
-                                                 '// This is the first executable statement.')
-        self.return_line = line_number(self.source, '// This is the return statement.')
-
-    def struct_types(self):
-        """Test that break on a struct declaration has no effect and test structure access for zero sized arrays."""
-        exe = os.path.join(os.getcwd(), "a.out")
-
-        # Create a target by the debugger.
-        target = self.dbg.CreateTarget(exe)
-        self.assertTrue(target, VALID_TARGET)
-
-        # Break on the struct declration statement in main.c.
-        lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1, loc_exact=False)
-        lldbutil.run_break_set_by_file_and_line (self, "main.c", self.return_line, num_expected_locations=1, loc_exact=True)
-
-        # Now launch the process, and do not stop at entry point.
-        process = target.LaunchSimple (None, None, self.get_process_working_directory())
-
-        if not process:
-            self.fail("SBTarget.Launch() failed")
-
-        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
-
-        # We should be stopped on the first executable statement within the
-        # function where the original breakpoint was attempted.
-        self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT,
-            substrs = ['main.c:%d' % self.first_executable_line,
-                       'stop reason = breakpoint'])
-
-        # The breakpoint should have a hit count of 1.
-        self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE,
-            substrs = [' resolved, hit count = 1'])
-
-        process.Continue()
-        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
-
-        # Test zero length array access and make sure it succeeds with "frame variable"
-        self.expect("frame variable pt.padding[0]",
-            DATA_TYPES_DISPLAYED_CORRECTLY,
-            substrs = ["pt.padding[0] = "])
-        self.expect("frame variable pt.padding[1]",
-            DATA_TYPES_DISPLAYED_CORRECTLY,
-            substrs = ["pt.padding[1] = "])
-        # Test zero length array access and make sure it succeeds with "expression"
-        self.expect("expression -- (pt.padding[0])",
-            DATA_TYPES_DISPLAYED_CORRECTLY,
-            substrs = ["(char)", " = "])
-
-        # The padding should be an array of size 0
-        self.expect("image lookup -t point_tag",
-            DATA_TYPES_DISPLAYED_CORRECTLY,
-            substrs = ['padding[]']) # Once rdar://problem/12566646 is fixed, this should display correctly
-
-        self.expect("expression -- &pt == (struct point_tag*)0",
-                    substrs = ['false'])
-
-if __name__ == '__main__':
-    import atexit
-    lldb.SBDebugger.Initialize()
-    atexit.register(lambda: lldb.SBDebugger.Terminate())
-    unittest2.main()
+lldbinline.MakeInlineTest(__file__, globals())
diff --git a/lldb/test/lang/c/struct_types/cmds.txt b/lldb/test/lang/c/struct_types/cmds.txt
deleted file mode 100644 (file)
index c308a76..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-break main.c:14
-continue
-var
index 5519631..a4051a0 100644 (file)
@@ -12,13 +12,13 @@ int main (int argc, char const *argv[])
         int x;
         int y;
         char padding[0];
-    }; // Set break point at this line.
+    }; //% self.expect("frame variable pt.padding[0]", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["pt.padding[0] = "]); self.expect("frame variable pt.padding[1]", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["pt.padding[1] = "]); self.expect("expression -- (pt.padding[0])", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["(char)", " = "]); self.expect("image lookup -t point_tag", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ['padding[]']) # Once rdar://problem/12566646 is fixed, this should display correctly
 
     struct rect_tag {
         struct point_tag bottom_left;
         struct point_tag top_right;
     };
-    struct point_tag pt = { 2, 3, {} }; // This is the first executable statement.
+    struct point_tag pt = { 2, 3, {} }; //% self.
     struct rect_tag rect = {{1, 2, {}}, {3, 4, {}}};
-    return 0; // This is the return statement.
+    return 0; //% self.expect("expression -- &pt == (struct point_tag*)0", substrs = ['false'])
 }
diff --git a/lldb/test/lldbinline.py b/lldb/test/lldbinline.py
new file mode 100644 (file)
index 0000000..ad397b5
--- /dev/null
@@ -0,0 +1,161 @@
+import lldb
+from lldbtest import *
+import lldbutil
+import os
+import new
+
+def source_type(filename):
+    _, extension = os.path.splitext(filename)
+    return {
+        '.c' : 'C_SOURCES',
+        '.cpp' : 'CXX_SOURCES',
+        '.cxx' : 'CXX_SOURCES',
+        '.cc' : 'CXX_SOURCES',
+        '.m' : 'OBJC_SOURCES',
+        '.mm' : 'OBJCXX_SOURCES'
+    }.get(extension, None)
+
+class CommandParser:
+    def __init__(self):
+        self.breakpoints = []
+
+    def parse_one_command(self, line):
+        parts = line.split('//%')
+        if len(parts) != 2:
+            return None
+        else:
+            return parts[1].strip() # take off trailing whitespace
+
+    def parse_source_files(self, source_files):
+        for source_file in source_files:
+            file_handle = open(source_file)
+            lines = file_handle.readlines()
+            line_number = 0
+            for line in lines:
+                line_number = line_number + 1 # 1-based, so we do this first
+                command = self.parse_one_command(line)
+                if command != None:
+                    breakpoint = {}
+                    breakpoint['file_name'] = source_file
+                    breakpoint['line_number'] = line_number
+                    breakpoint['command'] = command
+                    self.breakpoints.append(breakpoint)
+
+    def set_breakpoints(self, target):
+        for breakpoint in self.breakpoints:
+            breakpoint['breakpoint'] = target.BreakpointCreateByLocation(breakpoint['file_name'], breakpoint['line_number'])
+
+    def handle_breakpoint(self, test, breakpoint_id):
+        for breakpoint in self.breakpoints:
+            if breakpoint['breakpoint'].GetID() == breakpoint_id:
+                test.execute_user_command(breakpoint['command'])
+                return
+
+def BuildMakefile(mydir):
+    categories = {}
+
+    for f in os.listdir(os.getcwd()):
+        t = source_type(f)
+        if t:
+            if t in categories.keys():
+                categories[t].append(f)
+            else:
+                categories[t] = [f]
+
+    makefile = open("Makefile", 'w+')
+
+    level = os.sep.join([".."] * len(mydir.split(os.sep))) + os.sep + "make"
+
+    makefile.write("LEVEL = " + level + "\n")
+   
+    for t in categories.keys():
+        line = t + " := " + " ".join(categories[t])
+        makefile.write(line + "\n")
+
+    if ('OBJCXX_SOURCES' in categories.keys()) or ('OBJC_SOURCES' in categories.keys()):
+        makefile.write("LDFLAGS = $(CFLAGS) -lobjc -framework Foundation\n")
+
+    if ('CXX_SOURCES' in categories.keys()):
+        makefile.write("CXXFLAGS += -std-c++11\n")
+
+    makefile.write("include $(LEVEL)/Makefile.rules\n")
+    makefile.flush()
+    makefile.close()
+
+def CleanMakefile():
+    if (os.path.isfile("Makefile")):
+        os.unlink("Makefile")
+
+class InlineTest(TestBase):
+    # Internal implementation
+
+    def buildDsymWithImplicitMakefile(self):
+        BuildMakefile(self.mydir)
+        self.buildDsym()
+
+    def buildDwarfWithImplicitMakefile(self):
+        BuildMakefile(self.mydir)
+        self.buildDwarf()
+
+    def test_with_dsym(self):
+        self.buildDsymWithImplicitMakefile()
+        self.do_test()
+
+    def test_with_dwarf(self):
+        self.buildDwarfWithImplicitMakefile()
+        self.do_test()
+
+    def execute_user_command(self, __command):
+        exec __command in globals(), locals()
+
+    def do_test(self):
+        exe_name = "a.out"
+        exe = os.path.join(os.getcwd(), exe_name)
+        source_files = [ f for f in os.listdir(os.getcwd()) if source_type(f) ]
+        target = self.dbg.CreateTarget(exe)
+
+        parser = CommandParser()
+        parser.parse_source_files(source_files)
+        parser.set_breakpoints(target)
+
+        process = target.LaunchSimple(None, None, os.getcwd())
+
+        while lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint):
+            thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
+            breakpoint_id = thread.GetStopReasonDataAtIndex (0)
+            parser.handle_breakpoint(self, breakpoint_id)
+            process.Continue()
+
+    @classmethod
+    def classCleanup(cls):
+        CleanMakefile()
+
+    # Utilities for testcases
+
+    def check_expression (self, expression, expected_result, use_summary = True):
+        value = self.frame().EvaluateExpression (expression)
+        self.assertTrue(value.IsValid(), expression+"returned a valid value")
+        if self.TraceOn():
+            print value.GetSummary()
+            print value.GetValue()
+        if use_summary:
+            answer = value.GetSummary()
+        else:
+            answer = value.GetValue()
+        report_str = "%s expected: %s got: %s"%(expression, expected_result, answer)
+        self.assertTrue(answer == expected_result, report_str)
+
+def MakeInlineTest(__file, __globals):
+    # Derive the test name from the current file name
+    file_basename = os.path.basename(__file)
+    InlineTest.mydir = TestBase.compute_mydir(__file)
+
+    test_name, _ = os.path.splitext(file_basename)
+    # Build the test case 
+    test = new.classobj(test_name, (InlineTest,), {})
+    test.name = test_name
+    # Add the test case to the globals, and hide InlineTest
+    __globals.update({test_name : test})
+    del globals()["InlineTest"]
+
+