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