2 Abstract base class of basic types provides a generic type tester method.
8 from lldbsuite
.test
.lldbtest
import *
9 import lldbsuite
.test
.lldbutil
as lldbutil
12 def Msg(var
, val
, using_frame_variable
):
13 return "'%s %s' matches the output (from compiled code): %s" % (
14 "frame variable --show-types" if using_frame_variable
else "expression",
20 class GenericTester(TestBase
):
21 # This is the pattern by design to match the " var = 'value'" output from
22 # printf() stmts (see basic_type.cpp).
23 pattern
= re
.compile(" (\*?a[^=]*) = '([^=]*)'$")
26 DATA_TYPE_GROKKED
= "Data type from expr parser output is parsed correctly"
29 # Call super's setUp().
31 # We'll use the test method name as the exe_name.
32 # There are a bunch of test cases under test/types and we don't want the
33 # module cacheing subsystem to be confused with executable name "a.out"
34 # used for all the test cases.
35 self
.exe_name
= self
.testMethodName
36 golden
= "{}-golden-output.txt".format(self
.testMethodName
)
37 self
.golden_filename
= self
.getBuildArtifact(golden
)
40 """Cleanup the test byproducts."""
41 if os
.path
.exists(self
.golden_filename
):
42 os
.remove(self
.golden_filename
)
43 TestBase
.tearDown(self
)
45 # ==========================================================================#
46 # Functions build_and_run() and build_and_run_expr() are generic functions #
47 # which are called from the Test*Types*.py test cases. The API client is #
48 # responsible for supplying two mandatory arguments: the source file, e.g.,#
49 # 'int.cpp', and the atoms, e.g., set(['unsigned', 'long long']) to the #
50 # functions. There are also three optional keyword arguments of interest, #
53 # bc -> blockCaptured (defaulted to False) #
54 # True: testing vars of various basic types from inside a block #
55 # False: testing vars of various basic types from a function #
56 # qd -> quotedDisplay (defaulted to False) #
57 # True: the output from 'frame var' or 'expr var' contains a pair #
58 # of single quotes around the value #
59 # False: no single quotes are to be found around the value of #
61 # ==========================================================================#
63 def build_and_run(self
, source
, atoms
, bc
=False, qd
=False):
64 self
.build_and_run_with_source_atoms_expr(
65 source
, atoms
, expr
=False, bc
=bc
, qd
=qd
68 def build_and_run_expr(self
, source
, atoms
, bc
=False, qd
=False):
69 self
.build_and_run_with_source_atoms_expr(
70 source
, atoms
, expr
=True, bc
=bc
, qd
=qd
73 def build_and_run_with_source_atoms_expr(
74 self
, source
, atoms
, expr
, bc
=False, qd
=False
76 # See also Makefile and basic_type.cpp:177.
79 "CXX_SOURCES": source
,
81 "CFLAGS_EXTRAS": "-DTEST_BLOCK_CAPTURED_VARS",
84 d
= {"CXX_SOURCES": source
, "EXE": self
.exe_name
}
85 self
.build(dictionary
=d
)
86 self
.setTearDownCleanup(dictionary
=d
)
88 self
.generic_type_expr_tester(
89 self
.exe_name
, atoms
, blockCaptured
=bc
, quotedDisplay
=qd
92 self
.generic_type_tester(
93 self
.exe_name
, atoms
, blockCaptured
=bc
, quotedDisplay
=qd
96 def process_launch_o(self
):
97 # process launch command output redirect always goes to host the
98 # process is running on
99 if lldb
.remote_platform
:
100 # process launch -o requires a path that is valid on the target
101 self
.assertIsNotNone(lldb
.remote_platform
.GetWorkingDirectory())
102 remote_path
= lldbutil
.append_to_process_working_directory(
103 self
, "lldb-stdout-redirect.txt"
105 self
.runCmd("process launch -- {remote}".format(remote
=remote_path
))
106 # copy remote_path to local host
108 'platform get-file {remote} "{local}"'.format(
109 remote
=remote_path
, local
=self
.golden_filename
114 'process launch -o "{local}"'.format(local
=self
.golden_filename
)
117 def get_golden_list(self
, blockCaptured
=False):
118 with
open(self
.golden_filename
, "r") as f
:
122 # Scan the golden output line by line, looking for the pattern:
126 for line
in go
.split(os
.linesep
):
127 # We'll ignore variables of array types from inside a block.
128 if blockCaptured
and "[" in line
:
130 match
= self
.pattern
.search(line
)
132 var
, val
= match
.group(1), match
.group(2)
133 golden_list
.append((var
, val
))
136 def generic_type_tester(
137 self
, exe_name
, atoms
, quotedDisplay
=False, blockCaptured
=False
139 """Test that variables with basic types are displayed correctly."""
140 self
.runCmd("file %s" % self
.getBuildArtifact(exe_name
), CURRENT_EXECUTABLE_SET
)
142 # First, capture the golden output emitted by the oracle, i.e., the
143 # series of printf statements.
144 self
.process_launch_o()
146 # This golden list contains a list of (variable, value) pairs extracted
147 # from the golden output.
148 gl
= self
.get_golden_list(blockCaptured
)
150 # This test uses a #include of "basic_type.cpp" so we need to enable
151 # always setting inlined breakpoints.
152 self
.runCmd("settings set target.inline-breakpoint-strategy always")
154 # Inherit TCC permissions. We can leave this set.
155 self
.runCmd("settings set target.inherit-tcc true")
157 # Kill rather than detach from the inferior if something goes wrong.
158 self
.runCmd("settings set target.detach-on-error false")
160 # And add hooks to restore the settings during tearDown().
161 self
.addTearDownHook(
163 "settings set target.inline-breakpoint-strategy headers"
167 # Bring the program to the point where we can issue a series of
168 # 'frame variable --show-types' command.
170 break_line
= line_number(
171 "basic_type.cpp", "// Break here to test block captured variables."
174 break_line
= line_number(
176 "// Here is the line we will break on to check variables.",
178 lldbutil
.run_break_set_by_file_and_line(
179 self
, "basic_type.cpp", break_line
, num_expected_locations
=1, loc_exact
=True
182 self
.runCmd("run", RUN_SUCCEEDED
)
185 STOPPED_DUE_TO_BREAKPOINT
,
187 "stop reason = breakpoint",
188 " at basic_type.cpp:%d" % break_line
,
192 # self.runCmd("frame variable --show-types")
194 # Now iterate through the golden list, comparing against the output from
195 # 'frame variable --show-types var'.
197 self
.runCmd("frame variable --show-types %s" % var
)
198 output
= self
.res
.GetOutput()
200 # The input type is in a canonical form as a set of named atoms.
201 # The display type string must contain each and every element.
204 # runCmd: frame variable --show-types a_array_bounded[0]
205 # output: (char) a_array_bounded[0] = 'a'
208 dt
= re
.match("^\((.*)\)", output
).group(1)
210 self
.fail(self
.DATA_TYPE_GROKKED
)
212 # Expect the display type string to contain each and every atoms.
215 "Display type: '%s' must contain the type atoms: '%s'" % (dt
, atoms
),
220 # The (var, val) pair must match, too.
221 nv
= ("%s = '%s'" if quotedDisplay
else "%s = %s") % (var
, val
)
222 self
.expect(output
, Msg(var
, val
, True), exe
=False, substrs
=[nv
])
224 def generic_type_expr_tester(
225 self
, exe_name
, atoms
, quotedDisplay
=False, blockCaptured
=False
227 """Test that variable expressions with basic types are evaluated correctly."""
229 self
.runCmd("file %s" % self
.getBuildArtifact(exe_name
), CURRENT_EXECUTABLE_SET
)
231 # First, capture the golden output emitted by the oracle, i.e., the
232 # series of printf statements.
233 self
.process_launch_o()
235 # This golden list contains a list of (variable, value) pairs extracted
236 # from the golden output.
237 gl
= self
.get_golden_list(blockCaptured
)
239 # This test uses a #include of "basic_type.cpp" so we need to enable
240 # always setting inlined breakpoints.
241 self
.runCmd("settings set target.inline-breakpoint-strategy always")
242 # And add hooks to restore the settings during tearDown().
243 self
.addTearDownHook(
245 "settings set target.inline-breakpoint-strategy headers"
249 # Bring the program to the point where we can issue a series of
252 break_line
= line_number(
253 "basic_type.cpp", "// Break here to test block captured variables."
256 break_line
= line_number(
258 "// Here is the line we will break on to check variables.",
260 lldbutil
.run_break_set_by_file_and_line(
261 self
, "basic_type.cpp", break_line
, num_expected_locations
=1, loc_exact
=True
264 self
.runCmd("run", RUN_SUCCEEDED
)
267 STOPPED_DUE_TO_BREAKPOINT
,
268 substrs
=["stop reason = breakpoint", " at basic_type.cpp:%d" % break_line
],
271 # self.runCmd("frame variable --show-types")
273 # Now iterate through the golden list, comparing against the output from
276 self
.runCmd("expression %s" % var
)
277 output
= self
.res
.GetOutput()
279 # The input type is in a canonical form as a set of named atoms.
280 # The display type string must contain each and every element.
284 # output: (double) $0 = 1100.12
287 dt
= re
.match("^\((.*)\) \$[0-9]+ = ", output
).group(1)
289 self
.fail(self
.DATA_TYPE_GROKKED
)
291 # Expect the display type string to contain each and every atoms.
294 "Display type: '%s' must contain the type atoms: '%s'" % (dt
, atoms
),
299 # The val part must match, too.
300 valPart
= ("'%s'" if quotedDisplay
else "%s") % val
301 self
.expect(output
, Msg(var
, val
, False), exe
=False, substrs
=[valPart
])