Added a new kind of test case: the "inline" test

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
This commit is contained in:
Sean Callanan 2014-10-16 23:15:22 +00:00
parent bc464ee614
commit 816cb3eed4
5 changed files with 166 additions and 112 deletions

View File

@ -1,5 +0,0 @@
LEVEL = ../../../make
C_SOURCES := main.c
include $(LEVEL)/Makefile.rules

View 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())

View File

@ -1,3 +0,0 @@
break main.c:14
continue
var

View 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'])
}

161
lldb/test/lldbinline.py Normal file
View File

@ -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"]