forked from OSchip/llvm-project
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:
parent
bc464ee614
commit
816cb3eed4
|
@ -1,5 +0,0 @@
|
|||
LEVEL = ../../../make
|
||||
|
||||
C_SOURCES := main.c
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -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())
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
break main.c:14
|
||||
continue
|
||||
var
|
|
@ -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'])
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
|
||||
|
Loading…
Reference in New Issue