1 | //===-- FormatEntityTest.cpp ---------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "lldb/Core/FormatEntity.h" |
10 | #include "lldb/Utility/Status.h" |
11 | #include "lldb/Utility/StreamString.h" |
12 | #include "llvm/ADT/StringRef.h" |
13 | #include "llvm/Support/Error.h" |
14 | #include "llvm/Testing/Support/Error.h" |
15 | #include "gtest/gtest.h" |
16 | |
17 | using namespace lldb_private; |
18 | using namespace llvm; |
19 | |
20 | using Definition = FormatEntity::Entry::Definition; |
21 | using Entry = FormatEntity::Entry; |
22 | |
23 | static Expected<std::string> Format(StringRef format_str) { |
24 | StreamString stream; |
25 | FormatEntity::Entry format; |
26 | Status status = FormatEntity::Parse(format: format_str, entry&: format); |
27 | if (status.Fail()) |
28 | return status.ToError(); |
29 | |
30 | FormatEntity::Format(entry: format, s&: stream, sc: nullptr, exe_ctx: nullptr, addr: nullptr, valobj: nullptr, |
31 | function_changed: false, initial_function: false); |
32 | return stream.GetString().str(); |
33 | } |
34 | |
35 | TEST(FormatEntityTest, DefinitionConstructionNameAndType) { |
36 | Definition d("foo" , FormatEntity::Entry::Type::Invalid); |
37 | |
38 | EXPECT_STREQ(d.name, "foo" ); |
39 | EXPECT_EQ(d.string, nullptr); |
40 | EXPECT_EQ(d.type, FormatEntity::Entry::Type::Invalid); |
41 | EXPECT_EQ(d.data, 0UL); |
42 | EXPECT_EQ(d.num_children, 0UL); |
43 | EXPECT_EQ(d.children, nullptr); |
44 | EXPECT_FALSE(d.keep_separator); |
45 | } |
46 | |
47 | TEST(FormatEntityTest, DefinitionConstructionNameAndString) { |
48 | Definition d("foo" , "string" ); |
49 | |
50 | EXPECT_STREQ(d.name, "foo" ); |
51 | EXPECT_STREQ(d.string, "string" ); |
52 | EXPECT_EQ(d.type, FormatEntity::Entry::Type::EscapeCode); |
53 | EXPECT_EQ(d.data, 0UL); |
54 | EXPECT_EQ(d.num_children, 0UL); |
55 | EXPECT_EQ(d.children, nullptr); |
56 | EXPECT_FALSE(d.keep_separator); |
57 | } |
58 | |
59 | TEST(FormatEntityTest, DefinitionConstructionNameTypeData) { |
60 | Definition d("foo" , FormatEntity::Entry::Type::Invalid, 33); |
61 | |
62 | EXPECT_STREQ(d.name, "foo" ); |
63 | EXPECT_EQ(d.string, nullptr); |
64 | EXPECT_EQ(d.type, FormatEntity::Entry::Type::Invalid); |
65 | EXPECT_EQ(d.data, 33UL); |
66 | EXPECT_EQ(d.num_children, 0UL); |
67 | EXPECT_EQ(d.children, nullptr); |
68 | EXPECT_FALSE(d.keep_separator); |
69 | } |
70 | |
71 | TEST(FormatEntityTest, DefinitionConstructionNameTypeChildren) { |
72 | Definition d("foo" , FormatEntity::Entry::Type::Invalid, 33); |
73 | Definition parent("parent" , FormatEntity::Entry::Type::Invalid, 1, &d); |
74 | EXPECT_STREQ(parent.name, "parent" ); |
75 | EXPECT_STREQ(parent.string, nullptr); |
76 | EXPECT_EQ(parent.type, FormatEntity::Entry::Type::Invalid); |
77 | EXPECT_EQ(parent.num_children, 1UL); |
78 | EXPECT_EQ(parent.children, &d); |
79 | EXPECT_FALSE(parent.keep_separator); |
80 | |
81 | EXPECT_STREQ(parent.children[0].name, "foo" ); |
82 | EXPECT_EQ(parent.children[0].string, nullptr); |
83 | EXPECT_EQ(parent.children[0].type, FormatEntity::Entry::Type::Invalid); |
84 | EXPECT_EQ(parent.children[0].data, 33UL); |
85 | EXPECT_EQ(parent.children[0].num_children, 0UL); |
86 | EXPECT_EQ(parent.children[0].children, nullptr); |
87 | EXPECT_FALSE(d.keep_separator); |
88 | } |
89 | |
90 | constexpr llvm::StringRef lookupStrings[] = { |
91 | "${addr.load}" , |
92 | "${addr.file}" , |
93 | "${ansi.fg.black}" , |
94 | "${ansi.fg.red}" , |
95 | "${ansi.fg.green}" , |
96 | "${ansi.fg.yellow}" , |
97 | "${ansi.fg.blue}" , |
98 | "${ansi.fg.purple}" , |
99 | "${ansi.fg.cyan}" , |
100 | "${ansi.fg.white}" , |
101 | "${ansi.bg.black}" , |
102 | "${ansi.bg.red}" , |
103 | "${ansi.bg.green}" , |
104 | "${ansi.bg.yellow}" , |
105 | "${ansi.bg.blue}" , |
106 | "${ansi.bg.purple}" , |
107 | "${ansi.bg.cyan}" , |
108 | "${ansi.bg.white}" , |
109 | "${file.basename}" , |
110 | "${file.dirname}" , |
111 | "${file.fullpath}" , |
112 | "${frame.index}" , |
113 | "${frame.pc}" , |
114 | "${frame.fp}" , |
115 | "${frame.sp}" , |
116 | "${frame.flags}" , |
117 | "${frame.no-debug}" , |
118 | "${frame.reg.*}" , |
119 | "${frame.is-artificial}" , |
120 | "${function.id}" , |
121 | "${function.name}" , |
122 | "${function.name-without-args}" , |
123 | "${function.name-with-args}" , |
124 | "${function.mangled-name}" , |
125 | "${function.addr-offset}" , |
126 | "${function.concrete-only-addr-offset-no-padding}" , |
127 | "${function.line-offset}" , |
128 | "${function.pc-offset}" , |
129 | "${function.initial-function}" , |
130 | "${function.changed}" , |
131 | "${function.is-optimized}" , |
132 | "${function.is-inlined}" , |
133 | "${line.file.basename}" , |
134 | "${line.file.dirname}" , |
135 | "${line.file.fullpath}" , |
136 | "${line.number}" , |
137 | "${line.column}" , |
138 | "${line.start-addr}" , |
139 | "${line.end-addr}" , |
140 | "${module.file.basename}" , |
141 | "${module.file.dirname}" , |
142 | "${module.file.fullpath}" , |
143 | "${process.id}" , |
144 | "${process.name}" , |
145 | "${process.file.basename}" , |
146 | "${process.file.dirname}" , |
147 | "${process.file.fullpath}" , |
148 | "${script.frame}" , |
149 | "${script.process}" , |
150 | "${script.target}" , |
151 | "${script.thread}" , |
152 | "${script.var}" , |
153 | "${script.svar}" , |
154 | "${script.thread}" , |
155 | "${svar.dummy-svar-to-test-wildcard}" , |
156 | "${thread.id}" , |
157 | "${thread.protocol_id}" , |
158 | "${thread.index}" , |
159 | "${thread.info.*}" , |
160 | "${thread.queue}" , |
161 | "${thread.name}" , |
162 | "${thread.stop-reason}" , |
163 | "${thread.stop-reason-raw}" , |
164 | "${thread.return-value}" , |
165 | "${thread.completed-expression}" , |
166 | "${target.arch}" , |
167 | "${target.file.basename}" , |
168 | "${target.file.dirname}" , |
169 | "${target.file.fullpath}" , |
170 | "${var.dummy-var-to-test-wildcard}" }; |
171 | |
172 | TEST(FormatEntityTest, LookupAllEntriesInTree) { |
173 | for (const llvm::StringRef testString : lookupStrings) { |
174 | Entry e; |
175 | EXPECT_TRUE(FormatEntity::Parse(testString, e).Success()) |
176 | << "Formatting " << testString << " did not succeed" ; |
177 | } |
178 | } |
179 | |
180 | TEST(FormatEntityTest, Scope) { |
181 | // Scope with one alternative. |
182 | EXPECT_THAT_EXPECTED(Format("{${frame.pc}|foo}" ), HasValue("foo" )); |
183 | |
184 | // Scope with multiple alternatives. |
185 | EXPECT_THAT_EXPECTED(Format("{${frame.pc}|${function.name}|foo}" ), |
186 | HasValue("foo" )); |
187 | |
188 | // Escaped pipe inside a scope. |
189 | EXPECT_THAT_EXPECTED(Format("{foo\\|bar}" ), HasValue("foo|bar" )); |
190 | |
191 | // Unescaped pipe outside a scope. |
192 | EXPECT_THAT_EXPECTED(Format("foo|bar" ), HasValue("foo|bar" )); |
193 | |
194 | // Nested scopes. Note that scopes always resolve. |
195 | EXPECT_THAT_EXPECTED(Format("{{${frame.pc}|foo}|{bar}}" ), HasValue("foo" )); |
196 | EXPECT_THAT_EXPECTED(Format("{{${frame.pc}}|{bar}}" ), HasValue("" )); |
197 | |
198 | // Pipe between scopes. |
199 | EXPECT_THAT_EXPECTED(Format("{foo}|{bar}" ), HasValue("foo|bar" )); |
200 | EXPECT_THAT_EXPECTED(Format("{foo}||{bar}" ), HasValue("foo||bar" )); |
201 | |
202 | // Empty space between pipes. |
203 | EXPECT_THAT_EXPECTED(Format("{{foo}||{bar}}" ), HasValue("foo" )); |
204 | EXPECT_THAT_EXPECTED(Format("{${frame.pc}||{bar}}" ), HasValue("" )); |
205 | } |
206 | |