1 | //===-- UdtRecordCompleterTests.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 "Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h" |
10 | #include "llvm/ADT/StringExtras.h" |
11 | #include "gmock/gmock.h" |
12 | #include "gtest/gtest.h" |
13 | |
14 | using namespace lldb_private::npdb; |
15 | using namespace llvm; |
16 | |
17 | namespace { |
18 | using Member = UdtRecordCompleter::Member; |
19 | using MemberUP = std::unique_ptr<Member>; |
20 | using Record = UdtRecordCompleter::Record; |
21 | |
22 | class WrappedMember { |
23 | public: |
24 | WrappedMember(const Member &obj) : m_obj(obj) {} |
25 | |
26 | private: |
27 | const Member &m_obj; |
28 | |
29 | friend bool operator==(const WrappedMember &lhs, const WrappedMember &rhs) { |
30 | return lhs.m_obj.kind == rhs.m_obj.kind && |
31 | lhs.m_obj.name == rhs.m_obj.name && |
32 | lhs.m_obj.bit_offset == rhs.m_obj.bit_offset && |
33 | lhs.m_obj.bit_size == rhs.m_obj.bit_size && |
34 | lhs.m_obj.base_offset == rhs.m_obj.base_offset && |
35 | std::equal(first1: lhs.m_obj.fields.begin(), last1: lhs.m_obj.fields.end(), |
36 | first2: rhs.m_obj.fields.begin(), last2: rhs.m_obj.fields.end(), |
37 | binary_pred: [](const MemberUP &lhs, const MemberUP &rhs) { |
38 | return WrappedMember(*lhs) == WrappedMember(*rhs); |
39 | }); |
40 | } |
41 | |
42 | friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, |
43 | const WrappedMember &w) { |
44 | os << llvm::formatv(Fmt: "Member{.kind={0}, .name=\"{1}\", .bit_offset={2}, " |
45 | ".bit_size={3}, .base_offset={4}, .fields=[" , |
46 | Vals: w.m_obj.kind, Vals: w.m_obj.name, Vals: w.m_obj.bit_offset, |
47 | Vals: w.m_obj.bit_size, Vals: w.m_obj.base_offset); |
48 | llvm::ListSeparator sep; |
49 | for (auto &f : w.m_obj.fields) |
50 | os << sep << WrappedMember(*f); |
51 | return os << "]}" ; |
52 | } |
53 | }; |
54 | |
55 | class WrappedRecord { |
56 | public: |
57 | WrappedRecord(const Record &obj) : m_obj(obj) {} |
58 | |
59 | private: |
60 | const Record &m_obj; |
61 | |
62 | friend bool operator==(const WrappedRecord &lhs, const WrappedRecord &rhs) { |
63 | return lhs.m_obj.start_offset == rhs.m_obj.start_offset && |
64 | std::equal( |
65 | lhs.m_obj.record.fields.begin(), lhs.m_obj.record.fields.end(), |
66 | rhs.m_obj.record.fields.begin(), rhs.m_obj.record.fields.end(), |
67 | [](const MemberUP &lhs, const MemberUP &rhs) { |
68 | return WrappedMember(*lhs) == WrappedMember(*rhs); |
69 | }); |
70 | } |
71 | |
72 | friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, |
73 | const WrappedRecord &w) { |
74 | os << llvm::formatv(Fmt: "Record{.start_offset={0}, .record.fields=[" , |
75 | Vals: w.m_obj.start_offset); |
76 | llvm::ListSeparator sep; |
77 | for (const MemberUP &f : w.m_obj.record.fields) |
78 | os << sep << WrappedMember(*f); |
79 | return os << "]}" ; |
80 | } |
81 | }; |
82 | |
83 | class UdtRecordCompleterRecordTests : public testing::Test { |
84 | protected: |
85 | Record record; |
86 | |
87 | public: |
88 | void SetKind(Member::Kind kind) { record.record.kind = kind; } |
89 | void CollectMember(StringRef name, uint64_t byte_offset, uint64_t byte_size) { |
90 | record.CollectMember(name, byte_offset * 8, byte_size * 8, |
91 | clang::QualType(), lldb::eAccessPublic, 0); |
92 | } |
93 | void ConstructRecord() { record.ConstructRecord(); } |
94 | }; |
95 | Member *AddField(Member *member, StringRef name, uint64_t byte_offset, |
96 | uint64_t byte_size, Member::Kind kind, |
97 | uint64_t base_offset = 0) { |
98 | auto field = |
99 | std::make_unique<Member>(args&: name, args: byte_offset * 8, args: byte_size * 8, |
100 | args: clang::QualType(), args: lldb::eAccessPublic, args: 0); |
101 | field->kind = kind; |
102 | field->base_offset = base_offset; |
103 | member->fields.push_back(Elt: std::move(field)); |
104 | return member->fields.back().get(); |
105 | } |
106 | } // namespace |
107 | |
108 | TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInStruct) { |
109 | SetKind(Member::Kind::Struct); |
110 | CollectMember("m1" , 0, 4); |
111 | CollectMember("m2" , 0, 4); |
112 | CollectMember("m3" , 0, 1); |
113 | CollectMember("m4" , 0, 8); |
114 | ConstructRecord(); |
115 | |
116 | // struct { |
117 | // union { |
118 | // m1; |
119 | // m2; |
120 | // m3; |
121 | // m4; |
122 | // }; |
123 | // }; |
124 | Record record; |
125 | record.start_offset = 0; |
126 | Member *u = AddField(&record.record, "" , 0, 0, Member::Union); |
127 | AddField(member: u, name: "m1" , byte_offset: 0, byte_size: 4, kind: Member::Field); |
128 | AddField(member: u, name: "m2" , byte_offset: 0, byte_size: 4, kind: Member::Field); |
129 | AddField(member: u, name: "m3" , byte_offset: 0, byte_size: 1, kind: Member::Field); |
130 | AddField(member: u, name: "m4" , byte_offset: 0, byte_size: 8, kind: Member::Field); |
131 | EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record)); |
132 | } |
133 | |
134 | TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInUnion) { |
135 | SetKind(Member::Kind::Union); |
136 | CollectMember("m1" , 0, 4); |
137 | CollectMember("m2" , 0, 4); |
138 | CollectMember("m3" , 0, 1); |
139 | CollectMember("m4" , 0, 8); |
140 | ConstructRecord(); |
141 | |
142 | // union { |
143 | // m1; |
144 | // m2; |
145 | // m3; |
146 | // m4; |
147 | // }; |
148 | Record record; |
149 | record.start_offset = 0; |
150 | AddField(&record.record, "m1" , 0, 4, Member::Field); |
151 | AddField(&record.record, "m2" , 0, 4, Member::Field); |
152 | AddField(&record.record, "m3" , 0, 1, Member::Field); |
153 | AddField(&record.record, "m4" , 0, 8, Member::Field); |
154 | EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record)); |
155 | } |
156 | |
157 | TEST_F(UdtRecordCompleterRecordTests, TestAnonymousStructInUnion) { |
158 | SetKind(Member::Kind::Union); |
159 | CollectMember("m1" , 0, 4); |
160 | CollectMember("m2" , 4, 4); |
161 | CollectMember("m3" , 8, 1); |
162 | ConstructRecord(); |
163 | |
164 | // union { |
165 | // struct { |
166 | // m1; |
167 | // m2; |
168 | // m3; |
169 | // }; |
170 | // }; |
171 | Record record; |
172 | record.start_offset = 0; |
173 | Member *s = AddField(&record.record, "" , 0, 0, Member::Kind::Struct); |
174 | AddField(member: s, name: "m1" , byte_offset: 0, byte_size: 4, kind: Member::Field); |
175 | AddField(member: s, name: "m2" , byte_offset: 4, byte_size: 4, kind: Member::Field); |
176 | AddField(member: s, name: "m3" , byte_offset: 8, byte_size: 1, kind: Member::Field); |
177 | EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record)); |
178 | } |
179 | |
180 | TEST_F(UdtRecordCompleterRecordTests, TestNestedUnionStructInStruct) { |
181 | SetKind(Member::Kind::Struct); |
182 | CollectMember("m1" , 0, 4); |
183 | CollectMember("m2" , 0, 2); |
184 | CollectMember("m3" , 0, 2); |
185 | CollectMember("m4" , 2, 4); |
186 | CollectMember("m5" , 3, 2); |
187 | ConstructRecord(); |
188 | |
189 | // struct { |
190 | // union { |
191 | // m1; |
192 | // struct { |
193 | // m2; |
194 | // m5; |
195 | // }; |
196 | // struct { |
197 | // m3; |
198 | // m4; |
199 | // }; |
200 | // }; |
201 | // }; |
202 | Record record; |
203 | record.start_offset = 0; |
204 | Member *u = AddField(&record.record, "" , 0, 0, Member::Union); |
205 | AddField(member: u, name: "m1" , byte_offset: 0, byte_size: 4, kind: Member::Field); |
206 | Member *s1 = AddField(member: u, name: "" , byte_offset: 0, byte_size: 0, kind: Member::Struct); |
207 | Member *s2 = AddField(member: u, name: "" , byte_offset: 0, byte_size: 0, kind: Member::Struct); |
208 | AddField(member: s1, name: "m2" , byte_offset: 0, byte_size: 2, kind: Member::Field); |
209 | AddField(member: s1, name: "m5" , byte_offset: 3, byte_size: 2, kind: Member::Field); |
210 | AddField(member: s2, name: "m3" , byte_offset: 0, byte_size: 2, kind: Member::Field); |
211 | AddField(member: s2, name: "m4" , byte_offset: 2, byte_size: 4, kind: Member::Field); |
212 | EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record)); |
213 | } |
214 | |
215 | TEST_F(UdtRecordCompleterRecordTests, TestNestedUnionStructInUnion) { |
216 | SetKind(Member::Kind::Union); |
217 | CollectMember("m1" , 0, 4); |
218 | CollectMember("m2" , 0, 2); |
219 | CollectMember("m3" , 0, 2); |
220 | CollectMember("m4" , 2, 4); |
221 | CollectMember("m5" , 3, 2); |
222 | ConstructRecord(); |
223 | |
224 | // union { |
225 | // m1; |
226 | // struct { |
227 | // m2; |
228 | // m5; |
229 | // }; |
230 | // struct { |
231 | // m3; |
232 | // m4; |
233 | // }; |
234 | // }; |
235 | Record record; |
236 | record.start_offset = 0; |
237 | AddField(&record.record, "m1" , 0, 4, Member::Field); |
238 | Member *s1 = AddField(&record.record, "" , 0, 0, Member::Struct); |
239 | Member *s2 = AddField(&record.record, "" , 0, 0, Member::Struct); |
240 | AddField(member: s1, name: "m2" , byte_offset: 0, byte_size: 2, kind: Member::Field); |
241 | AddField(member: s1, name: "m5" , byte_offset: 3, byte_size: 2, kind: Member::Field); |
242 | AddField(member: s2, name: "m3" , byte_offset: 0, byte_size: 2, kind: Member::Field); |
243 | AddField(member: s2, name: "m4" , byte_offset: 2, byte_size: 4, kind: Member::Field); |
244 | EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record)); |
245 | } |
246 | |