1061da546Spatrickfrom __future__ import print_function 2061da546Spatrickfrom __future__ import absolute_import 3061da546Spatrick 4061da546Spatrick# System modules 5061da546Spatrickimport os 6*f6aab3d8Srobertimport textwrap 7061da546Spatrick 8061da546Spatrick# Third-party modules 9061da546Spatrickimport io 10061da546Spatrick 11061da546Spatrick# LLDB modules 12061da546Spatrickimport lldb 13061da546Spatrickfrom .lldbtest import * 14061da546Spatrickfrom . import configuration 15061da546Spatrickfrom . import lldbutil 16061da546Spatrickfrom .decorators import * 17061da546Spatrick 18061da546Spatrickdef source_type(filename): 19061da546Spatrick _, extension = os.path.splitext(filename) 20061da546Spatrick return { 21061da546Spatrick '.c': 'C_SOURCES', 22061da546Spatrick '.cpp': 'CXX_SOURCES', 23061da546Spatrick '.cxx': 'CXX_SOURCES', 24061da546Spatrick '.cc': 'CXX_SOURCES', 25061da546Spatrick '.m': 'OBJC_SOURCES', 26061da546Spatrick '.mm': 'OBJCXX_SOURCES' 27061da546Spatrick }.get(extension, None) 28061da546Spatrick 29061da546Spatrick 30061da546Spatrickclass CommandParser: 31061da546Spatrick 32061da546Spatrick def __init__(self): 33061da546Spatrick self.breakpoints = [] 34061da546Spatrick 35061da546Spatrick def parse_one_command(self, line): 36061da546Spatrick parts = line.split('//%') 37061da546Spatrick 38061da546Spatrick command = None 39061da546Spatrick new_breakpoint = True 40061da546Spatrick 41061da546Spatrick if len(parts) == 2: 42*f6aab3d8Srobert command = parts[1].rstrip() 43061da546Spatrick new_breakpoint = parts[0].strip() != "" 44061da546Spatrick 45061da546Spatrick return (command, new_breakpoint) 46061da546Spatrick 47061da546Spatrick def parse_source_files(self, source_files): 48061da546Spatrick for source_file in source_files: 49061da546Spatrick file_handle = io.open(source_file, encoding='utf-8') 50061da546Spatrick lines = file_handle.readlines() 51061da546Spatrick line_number = 0 52061da546Spatrick # non-NULL means we're looking through whitespace to find 53061da546Spatrick # additional commands 54061da546Spatrick current_breakpoint = None 55061da546Spatrick for line in lines: 56061da546Spatrick line_number = line_number + 1 # 1-based, so we do this first 57061da546Spatrick (command, new_breakpoint) = self.parse_one_command(line) 58061da546Spatrick 59061da546Spatrick if new_breakpoint: 60061da546Spatrick current_breakpoint = None 61061da546Spatrick 62061da546Spatrick if command is not None: 63061da546Spatrick if current_breakpoint is None: 64061da546Spatrick current_breakpoint = {} 65061da546Spatrick current_breakpoint['file_name'] = source_file 66061da546Spatrick current_breakpoint['line_number'] = line_number 67061da546Spatrick current_breakpoint['command'] = command 68061da546Spatrick self.breakpoints.append(current_breakpoint) 69061da546Spatrick else: 70061da546Spatrick current_breakpoint['command'] = current_breakpoint[ 71061da546Spatrick 'command'] + "\n" + command 72*f6aab3d8Srobert for bkpt in self.breakpoints: 73*f6aab3d8Srobert bkpt['command'] = textwrap.dedent(bkpt['command']) 74061da546Spatrick 75061da546Spatrick def set_breakpoints(self, target): 76061da546Spatrick for breakpoint in self.breakpoints: 77061da546Spatrick breakpoint['breakpoint'] = target.BreakpointCreateByLocation( 78061da546Spatrick breakpoint['file_name'], breakpoint['line_number']) 79061da546Spatrick 80061da546Spatrick def handle_breakpoint(self, test, breakpoint_id): 81061da546Spatrick for breakpoint in self.breakpoints: 82061da546Spatrick if breakpoint['breakpoint'].GetID() == breakpoint_id: 83061da546Spatrick test.execute_user_command(breakpoint['command']) 84061da546Spatrick return 85061da546Spatrick 86061da546Spatrick 87061da546Spatrickclass InlineTest(TestBase): 88dda28197Spatrick 89dda28197Spatrick def getBuildDirBasename(self): 90dda28197Spatrick return self.__class__.__name__ + "." + self.testMethodName 91061da546Spatrick 92061da546Spatrick def BuildMakefile(self): 93061da546Spatrick makefilePath = self.getBuildArtifact("Makefile") 94061da546Spatrick if os.path.exists(makefilePath): 95061da546Spatrick return 96061da546Spatrick 97061da546Spatrick categories = {} 98061da546Spatrick for f in os.listdir(self.getSourceDir()): 99061da546Spatrick t = source_type(f) 100061da546Spatrick if t: 101061da546Spatrick if t in list(categories.keys()): 102061da546Spatrick categories[t].append(f) 103061da546Spatrick else: 104061da546Spatrick categories[t] = [f] 105061da546Spatrick 106dda28197Spatrick with open(makefilePath, 'w+') as makefile: 107061da546Spatrick for t in list(categories.keys()): 108061da546Spatrick line = t + " := " + " ".join(categories[t]) 109061da546Spatrick makefile.write(line + "\n") 110061da546Spatrick 111dda28197Spatrick if ('OBJCXX_SOURCES' in list(categories.keys())) or \ 112dda28197Spatrick ('OBJC_SOURCES' in list(categories.keys())): 113061da546Spatrick makefile.write( 114061da546Spatrick "LDFLAGS = $(CFLAGS) -lobjc -framework Foundation\n") 115061da546Spatrick 116061da546Spatrick if ('CXX_SOURCES' in list(categories.keys())): 117061da546Spatrick makefile.write("CXXFLAGS += -std=c++11\n") 118061da546Spatrick 119061da546Spatrick makefile.write("include Makefile.rules\n") 120061da546Spatrick 121061da546Spatrick def _test(self): 122061da546Spatrick self.BuildMakefile() 123dda28197Spatrick self.build(dictionary=self._build_dict) 124061da546Spatrick self.do_test() 125061da546Spatrick 126061da546Spatrick def execute_user_command(self, __command): 127061da546Spatrick exec(__command, globals(), locals()) 128061da546Spatrick 129dda28197Spatrick def _get_breakpoint_ids(self, thread): 130dda28197Spatrick ids = set() 131dda28197Spatrick for i in range(0, thread.GetStopReasonDataCount(), 2): 132dda28197Spatrick ids.add(thread.GetStopReasonDataAtIndex(i)) 133dda28197Spatrick self.assertGreater(len(ids), 0) 134dda28197Spatrick return sorted(ids) 135dda28197Spatrick 136061da546Spatrick def do_test(self): 137061da546Spatrick exe = self.getBuildArtifact("a.out") 138061da546Spatrick source_files = [f for f in os.listdir(self.getSourceDir()) 139061da546Spatrick if source_type(f)] 140061da546Spatrick target = self.dbg.CreateTarget(exe) 141061da546Spatrick 142061da546Spatrick parser = CommandParser() 143061da546Spatrick parser.parse_source_files(source_files) 144061da546Spatrick parser.set_breakpoints(target) 145061da546Spatrick 146061da546Spatrick process = target.LaunchSimple(None, None, self.get_process_working_directory()) 147dda28197Spatrick self.assertIsNotNone(process, PROCESS_IS_VALID) 148dda28197Spatrick 149061da546Spatrick hit_breakpoints = 0 150061da546Spatrick 151061da546Spatrick while lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint): 152061da546Spatrick hit_breakpoints += 1 153061da546Spatrick thread = lldbutil.get_stopped_thread( 154061da546Spatrick process, lldb.eStopReasonBreakpoint) 155dda28197Spatrick for bp_id in self._get_breakpoint_ids(thread): 156dda28197Spatrick parser.handle_breakpoint(self, bp_id) 157061da546Spatrick process.Continue() 158061da546Spatrick 159061da546Spatrick self.assertTrue(hit_breakpoints > 0, 160061da546Spatrick "inline test did not hit a single breakpoint") 161061da546Spatrick # Either the process exited or the stepping plan is complete. 162061da546Spatrick self.assertTrue(process.GetState() in [lldb.eStateStopped, 163061da546Spatrick lldb.eStateExited], 164061da546Spatrick PROCESS_EXITED) 165061da546Spatrick 166061da546Spatrick def check_expression(self, expression, expected_result, use_summary=True): 167061da546Spatrick value = self.frame().EvaluateExpression(expression) 168061da546Spatrick self.assertTrue(value.IsValid(), expression + "returned a valid value") 169061da546Spatrick if self.TraceOn(): 170061da546Spatrick print(value.GetSummary()) 171061da546Spatrick print(value.GetValue()) 172061da546Spatrick if use_summary: 173061da546Spatrick answer = value.GetSummary() 174061da546Spatrick else: 175061da546Spatrick answer = value.GetValue() 176061da546Spatrick report_str = "%s expected: %s got: %s" % ( 177061da546Spatrick expression, expected_result, answer) 178061da546Spatrick self.assertTrue(answer == expected_result, report_str) 179061da546Spatrick 180061da546Spatrick 181061da546Spatrickdef ApplyDecoratorsToFunction(func, decorators): 182061da546Spatrick tmp = func 183061da546Spatrick if isinstance(decorators, list): 184061da546Spatrick for decorator in decorators: 185061da546Spatrick tmp = decorator(tmp) 186061da546Spatrick elif hasattr(decorators, '__call__'): 187061da546Spatrick tmp = decorators(tmp) 188061da546Spatrick return tmp 189061da546Spatrick 190061da546Spatrick 191dda28197Spatrickdef MakeInlineTest(__file, __globals, decorators=None, name=None, 192dda28197Spatrick build_dict=None): 193061da546Spatrick # Adjust the filename if it ends in .pyc. We want filenames to 194061da546Spatrick # reflect the source python file, not the compiled variant. 195061da546Spatrick if __file is not None and __file.endswith(".pyc"): 196061da546Spatrick # Strip the trailing "c" 197061da546Spatrick __file = __file[0:-1] 198061da546Spatrick 199dda28197Spatrick if name is None: 200061da546Spatrick # Derive the test name from the current file name 201061da546Spatrick file_basename = os.path.basename(__file) 202dda28197Spatrick name, _ = os.path.splitext(file_basename) 203061da546Spatrick 204061da546Spatrick test_func = ApplyDecoratorsToFunction(InlineTest._test, decorators) 205061da546Spatrick # Build the test case 206dda28197Spatrick test_class = type(name, (InlineTest,), dict(test=test_func, 207dda28197Spatrick name=name, _build_dict=build_dict)) 208061da546Spatrick 209061da546Spatrick # Add the test case to the globals, and hide InlineTest 210dda28197Spatrick __globals.update({name: test_class}) 211061da546Spatrick 212061da546Spatrick # Keep track of the original test filename so we report it 213061da546Spatrick # correctly in test results. 214061da546Spatrick test_class.test_filename = __file 215061da546Spatrick test_class.mydir = TestBase.compute_mydir(__file) 216061da546Spatrick return test_class 217