AbstractBase.py revision c21e523a1a7eb74d74d07990c5c12c16982ad1d9
1"""
2Abstract base class of basic types provides a generic type tester method.
3"""
4
5import os, time
6import re
7import lldb
8from lldbtest import *
9
10def Msg(var, val, using_frame_variable):
11    return "'%s %s' matches the output (from compiled code): %s" % (
12        'frame variable -T' if using_frame_variable else 'expression' ,var, val)
13
14class GenericTester(TestBase):
15
16    # This is the pattern by design to match the " var = 'value'" output from
17    # printf() stmts (see basic_type.cpp).
18    pattern = re.compile(" (\*?a[^=]*) = '([^=]*)'$")
19
20    # Assert message.
21    DATA_TYPE_GROKKED = "Data type from expr parser output is parsed correctly"
22
23    def setUp(self):
24        # Call super's setUp().
25        TestBase.setUp(self)
26        # We'll use the test method name as the exe_name.
27        # There are a bunch of test cases under test/types and we don't want the
28        # module cacheing subsystem to be confused with executable name "a.out"
29        # used for all the test cases.
30        self.exe_name = self.testMethodName
31
32    #==========================================================================#
33    # Functions build_and_run() and build_and_run_expr() are generic functions #
34    # which are called from the Test*Types*.py test cases.  The API client is  #
35    # responsible for supplying two mandatory arguments: the source file, e.g.,#
36    # 'int.cpp', and the atoms, e.g., set(['unsigned', 'long long']) to the    #
37    # functions.  There are also three optional keyword arguments of interest, #
38    # as follows:                                                              #
39    #                                                                          #
40    # dsym -> build for dSYM (defaulted to True)                               #
41    #         True: build dSYM file                                            #
42    #         False: build DWARF map                                           #
43    # bc -> blockCaptured (defaulted to False)                                 #
44    #         True: testing vars of various basic types from isnide a block    #
45    #         False: testing vars of various basic types from a function       #
46    # qd -> quotedDisplay (defaulted to False)                                 #
47    #         True: the output from 'frame var' or 'expr var' contains a pair  #
48    #               of single quotes around the value                          #
49    #         False: no single quotes are to be found around the value of      #
50    #                variable                                                  #
51    #==========================================================================#
52
53    def build_and_run(self, source, atoms, dsym=True, bc=False, qd=False):
54        self.build_and_run_with_source_atoms_expr(source, atoms, expr=False, dsym=dsym, bc=bc, qd=qd)
55
56    def build_and_run_expr(self, source, atoms, dsym=True, bc=False, qd=False):
57        self.build_and_run_with_source_atoms_expr(source, atoms, expr=True, dsym=dsym, bc=bc, qd=qd)
58
59    def build_and_run_with_source_atoms_expr(self, source, atoms, expr, dsym=True, bc=False, qd=False):
60        # See also Makefile and basic_type.cpp:177.
61        if bc:
62            d = {'CXX_SOURCES': source, 'EXE': self.exe_name, 'CFLAGS_EXTRAS': '-DTEST_BLOCK_CAPTURED_VARS'}
63        else:
64            d = {'CXX_SOURCES': source, 'EXE': self.exe_name}
65        if dsym:
66            self.buildDsym(dictionary=d)
67        else:
68            self.buildDwarf(dictionary=d)
69        self.setTearDownCleanup(dictionary=d)
70        if expr:
71            self.generic_type_expr_tester(self.exe_name, atoms, blockCaptured=bc, quotedDisplay=qd)
72        else:
73            self.generic_type_tester(self.exe_name, atoms, blockCaptured=bc, quotedDisplay=qd)
74
75    def generic_type_tester(self, exe_name, atoms, quotedDisplay=False, blockCaptured=False):
76        """Test that variables with basic types are displayed correctly."""
77
78        # First, capture the golden output emitted by the oracle, i.e., the
79        # series of printf statements.
80        go = system("./%s" % exe_name, sender=self)[0]
81        # This golden list contains a list of (variable, value) pairs extracted
82        # from the golden output.
83        gl = []
84
85        # Scan the golden output line by line, looking for the pattern:
86        #
87        #     variable = 'value'
88        #
89        for line in go.split(os.linesep):
90            # We'll ignore variables of array types from inside a block.
91            if blockCaptured and '[' in line:
92                continue
93            match = self.pattern.search(line)
94            if match:
95                var, val = match.group(1), match.group(2)
96                gl.append((var, val))
97        #print "golden list:", gl
98
99        # Bring the program to the point where we can issue a series of
100        # 'frame variable -T' command.
101        self.runCmd("file %s" % exe_name, CURRENT_EXECUTABLE_SET)
102        if blockCaptured:
103            break_line = line_number ("basic_type.cpp", "// Break here to test block captured variables.")
104        else:
105            break_line = line_number ("basic_type.cpp", "// Here is the line we will break on to check variables.")
106        self.expect("breakpoint set -f basic_type.cpp -l %d" % break_line,
107                    BREAKPOINT_CREATED,
108            startstr = "Breakpoint created: 1: file ='basic_type.cpp', line = %d, locations = 1" %
109                        break_line)
110
111        self.runCmd("run", RUN_SUCCEEDED)
112        self.expect("process status", STOPPED_DUE_TO_BREAKPOINT,
113            substrs = [" at basic_type.cpp:%d" % break_line,
114                       "stop reason = breakpoint"])
115
116        #self.runCmd("frame variable -T")
117
118        # Now iterate through the golden list, comparing against the output from
119        # 'frame variable -T var'.
120        for var, val in gl:
121            self.runCmd("frame variable -T %s" % var)
122            output = self.res.GetOutput()
123
124            # The input type is in a canonical form as a set of named atoms.
125            # The display type string must conatin each and every element.
126            #
127            # Example:
128            #     runCmd: frame variable -T a_array_bounded[0]
129            #     output: (char) a_array_bounded[0] = 'a'
130            #
131            try:
132                dt = re.match("^\((.*)\)", output).group(1)
133            except:
134                self.fail(self.DATA_TYPE_GROKKED)
135
136            # Expect the display type string to contain each and every atoms.
137            self.expect(dt,
138                        "Display type: '%s' must contain the type atoms: '%s'" %
139                        (dt, atoms),
140                        exe=False,
141                substrs = list(atoms))
142
143            # The (var, val) pair must match, too.
144            nv = ("%s = '%s'" if quotedDisplay else "%s = %s") % (var, val)
145            self.expect(output, Msg(var, val, True), exe=False,
146                substrs = [nv])
147
148    def generic_type_expr_tester(self, exe_name, atoms, quotedDisplay=False, blockCaptured=False):
149        """Test that variable expressions with basic types are evaluated correctly."""
150
151        # First, capture the golden output emitted by the oracle, i.e., the
152        # series of printf statements.
153        go = system("./%s" % exe_name, sender=self)[0]
154        # This golden list contains a list of (variable, value) pairs extracted
155        # from the golden output.
156        gl = []
157
158        # Scan the golden output line by line, looking for the pattern:
159        #
160        #     variable = 'value'
161        #
162        for line in go.split(os.linesep):
163            # We'll ignore variables of array types from inside a block.
164            if blockCaptured and '[' in line:
165                continue
166            match = self.pattern.search(line)
167            if match:
168                var, val = match.group(1), match.group(2)
169                gl.append((var, val))
170        #print "golden list:", gl
171
172        # Bring the program to the point where we can issue a series of
173        # 'expr' command.
174        self.runCmd("file %s" % exe_name, CURRENT_EXECUTABLE_SET)
175        if blockCaptured:
176            break_line = line_number ("basic_type.cpp", "// Break here to test block captured variables.")
177        else:
178            break_line = line_number ("basic_type.cpp", "// Here is the line we will break on to check variables.")
179        self.expect("breakpoint set -f basic_type.cpp -l %d" % break_line,
180                    BREAKPOINT_CREATED,
181            startstr = "Breakpoint created: 1: file ='basic_type.cpp', line = %d, locations = 1" %
182                        break_line)
183        self.runCmd("run", RUN_SUCCEEDED)
184        self.expect("process status", STOPPED_DUE_TO_BREAKPOINT,
185            substrs = [" at basic_type.cpp:%d" % break_line,
186                       "stop reason = breakpoint"])
187
188        #self.runCmd("frame variable -T")
189
190        # Now iterate through the golden list, comparing against the output from
191        # 'expr var'.
192        for var, val in gl:
193            # Don't overwhelm the expression mechanism.
194            # This slows down the test suite quite a bit, to enable it, define
195            # the environment variable LLDB_TYPES_EXPR_TIME_WAIT.  For example:
196            #
197            #     export LLDB_TYPES_EXPR_TIME_WAIT=0.5
198            #
199            # causes a 0.5 second delay between 'expression' commands.
200            if "LLDB_TYPES_EXPR_TIME_WAIT" in os.environ:
201                time.sleep(float(os.environ["LLDB_TYPES_EXPR_TIME_WAIT"]))
202
203            self.runCmd("expression %s" % var)
204            output = self.res.GetOutput()
205
206            # The input type is in a canonical form as a set of named atoms.
207            # The display type string must conatin each and every element.
208            #
209            # Example:
210            #     runCmd: expr a
211            #     output: (double) $0 = 1100.12
212            #
213            try:
214                dt = re.match("^\((.*)\) \$[0-9]+ = ", output).group(1)
215            except:
216                self.fail(self.DATA_TYPE_GROKKED)
217
218            # Expect the display type string to contain each and every atoms.
219            self.expect(dt,
220                        "Display type: '%s' must contain the type atoms: '%s'" %
221                        (dt, atoms),
222                        exe=False,
223                substrs = list(atoms))
224
225            # The val part must match, too.
226            valPart = ("'%s'" if quotedDisplay else "%s") % val
227            self.expect(output, Msg(var, val, False), exe=False,
228                substrs = [valPart])
229