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
14using namespace lldb_private::npdb;
15using namespace llvm;
16
17namespace {
18using Member = UdtRecordCompleter::Member;
19using MemberUP = std::unique_ptr<Member>;
20using Record = UdtRecordCompleter::Record;
21
22class WrappedMember {
23public:
24 WrappedMember(const Member &obj) : m_obj(obj) {}
25
26private:
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
55class WrappedRecord {
56public:
57 WrappedRecord(const Record &obj) : m_obj(obj) {}
58
59private:
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
83class UdtRecordCompleterRecordTests : public testing::Test {
84protected:
85 Record record;
86
87public:
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};
95Member *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
108TEST_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
134TEST_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
157TEST_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
180TEST_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
215TEST_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

source code of lldb/unittests/SymbolFile/NativePDB/UdtRecordCompleterTests.cpp