1"""
2Test lldb data formatter subsystem for std::span
3"""
4
5import lldb
6from lldbsuite.test.decorators import *
7from lldbsuite.test.lldbtest import *
8from lldbsuite.test import lldbutil
9
10class LibcxxSpanDataFormatterTestCase(TestBase):
11
12    def findVariable(self, name):
13        var = self.frame().FindVariable(name)
14        self.assertTrue(var.IsValid())
15        return var
16
17    def check_size(self, var_name, size):
18        var = self.findVariable(var_name)
19        self.assertEqual(var.GetNumChildren(), size)
20
21    def check_numbers(self, var_name):
22        """Helper to check that data formatter sees contents of std::span correctly"""
23
24        expectedSize = 5
25        self.check_size(var_name, expectedSize)
26
27        self.expect_expr(
28                var_name, result_type=f'std::span<int, {expectedSize}>',
29                result_summary=f'size={expectedSize}',
30                result_children=[
31                    ValueCheck(name='[0]', value='1'),
32                    ValueCheck(name='[1]', value='12'),
33                    ValueCheck(name='[2]', value='123'),
34                    ValueCheck(name='[3]', value='1234'),
35                    ValueCheck(name='[4]', value='12345')
36        ])
37
38        # check access-by-index
39        self.expect_var_path(f'{var_name}[0]', type='int', value='1')
40        self.expect_var_path(f'{var_name}[1]', type='int', value='12')
41        self.expect_var_path(f'{var_name}[2]', type='int', value='123')
42        self.expect_var_path(f'{var_name}[3]', type='int', value='1234')
43        self.expect_var_path(f'{var_name}[4]', type='int', value='12345')
44
45    @add_test_categories(['libc++'])
46    @skipIf(compiler='clang', compiler_version=['<', '11.0'])
47    def test_with_run_command(self):
48        """Test that std::span variables are formatted correctly when printed."""
49        self.build()
50        (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
51            self, 'break here', lldb.SBFileSpec('main.cpp', False))
52
53        lldbutil.continue_to_breakpoint(process, bkpt)
54
55        # std::span of std::array with extents known at compile-time
56        self.check_numbers('numbers_span')
57
58        # check access to synthetic children for static spans
59        self.runCmd('type summary add --summary-string "item 0 is ${var[0]}" -x "std::span<" span')
60        self.expect_expr('numbers_span',
61                         result_type='std::span<int, 5>',
62                         result_summary='item 0 is 1')
63
64        self.runCmd('type summary add --summary-string "item 0 is ${svar[0]}" -x "std::span<" span')
65        self.expect_expr('numbers_span',
66                         result_type='std::span<int, 5>',
67                         result_summary='item 0 is 1')
68
69        self.runCmd('type summary delete span')
70
71        # New span with strings
72        lldbutil.continue_to_breakpoint(process, bkpt)
73
74        expectedStringSpanChildren = [ ValueCheck(name='[0]', summary='"smart"'),
75                                       ValueCheck(name='[1]', summary='"!!!"') ]
76
77        self.expect_var_path('strings_span',
78                             summary='size=2',
79                             children=expectedStringSpanChildren)
80
81        # check access to synthetic children for dynamic spans
82        self.runCmd('type summary add --summary-string "item 0 is ${var[0]}" dynamic_string_span')
83        self.expect_var_path('strings_span', summary='item 0 is "smart"')
84
85        self.runCmd('type summary add --summary-string "item 0 is ${svar[0]}" dynamic_string_span')
86        self.expect_var_path('strings_span', summary='item 0 is "smart"')
87
88        self.runCmd('type summary delete dynamic_string_span')
89
90        # test summaries based on synthetic children
91        self.runCmd(
92                'type summary add --summary-string "span has ${svar%#} items" -e dynamic_string_span')
93
94        self.expect_var_path('strings_span', summary='span has 2 items')
95
96        self.expect_var_path('strings_span',
97                             summary='span has 2 items',
98                             children=expectedStringSpanChildren)
99
100        # check access-by-index
101        self.expect_var_path('strings_span[0]', summary='"smart"');
102        self.expect_var_path('strings_span[1]', summary='"!!!"');
103
104        # Newly inserted value not visible to span
105        lldbutil.continue_to_breakpoint(process, bkpt)
106
107        self.expect_expr('strings_span',
108                         result_summary='span has 2 items',
109                         result_children=expectedStringSpanChildren)
110
111        self.runCmd('type summary delete dynamic_string_span')
112
113        lldbutil.continue_to_breakpoint(process, bkpt)
114
115        # Empty spans
116        self.expect_expr('static_zero_span',
117                         result_type='std::span<int, 0>',
118                         result_summary='size=0')
119        self.check_size('static_zero_span', 0)
120
121        self.expect_expr('dynamic_zero_span',
122                         result_summary='size=0')
123        self.check_size('dynamic_zero_span', 0)
124
125        # Nested spans
126        self.expect_expr('nested',
127                         result_summary='size=2',
128                         result_children=[
129                             ValueCheck(name='[0]', summary='size=2',
130                                        children=expectedStringSpanChildren),
131                             ValueCheck(name='[1]', summary='size=2',
132                                        children=expectedStringSpanChildren)
133        ])
134        self.check_size('nested', 2)
135
136    @add_test_categories(['libc++'])
137    @skipIf(compiler='clang', compiler_version=['<', '11.0'])
138    def test_ref_and_ptr(self):
139        """Test that std::span is correctly formatted when passed by ref and ptr"""
140        self.build()
141        (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
142            self, 'Stop here to check by ref', lldb.SBFileSpec('main.cpp', False))
143
144        # The reference should display the same was as the value did
145        self.check_numbers('ref')
146
147        # The pointer should just show the right number of elements:
148
149        ptrAddr = self.findVariable('ptr').GetValue()
150        self.expect_expr('ptr', result_type='std::span<int, 5> *',
151                         result_summary=f'{ptrAddr} size=5')
152