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