diff --git a/lldb/unittests/SymbolFile/NativePDB/UdtRecordCompleterTests.cpp b/lldb/unittests/SymbolFile/NativePDB/UdtRecordCompleterTests.cpp --- a/lldb/unittests/SymbolFile/NativePDB/UdtRecordCompleterTests.cpp +++ b/lldb/unittests/SymbolFile/NativePDB/UdtRecordCompleterTests.cpp @@ -15,38 +15,80 @@ namespace { using Member = UdtRecordCompleter::Member; -using Record = UdtRecordCompleter::Record; using MemberUP = std::unique_ptr; +using Record = UdtRecordCompleter::Record; + +class WrappedMember { +public: + WrappedMember(const Member &obj) : m_obj(obj) {} + +private: + const Member &m_obj; + + friend bool operator==(const WrappedMember &lhs, const WrappedMember &rhs) { + return lhs.m_obj.kind == rhs.m_obj.kind && + lhs.m_obj.name == rhs.m_obj.name && + lhs.m_obj.bit_offset == rhs.m_obj.bit_offset && + lhs.m_obj.bit_size == rhs.m_obj.bit_size && + lhs.m_obj.base_offset == rhs.m_obj.base_offset && + std::equal(lhs.m_obj.fields.begin(), lhs.m_obj.fields.end(), + rhs.m_obj.fields.begin(), rhs.m_obj.fields.end(), + [](const MemberUP &lhs, const MemberUP &rhs) { + return WrappedMember(*lhs) == WrappedMember(*rhs); + }); + } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, + const WrappedMember &w) { + os << llvm::formatv("Member{.kind={0}, .name=\"{1}\", .bit_offset={2}, " + ".bit_size={3}, .base_offset={4}, .fields=[", + w.m_obj.kind, w.m_obj.name, w.m_obj.bit_offset, + w.m_obj.bit_size, w.m_obj.base_offset); + llvm::ListSeparator sep; + for (auto &f : w.m_obj.fields) + os << sep << WrappedMember(*f); + return os << "]}"; + } +}; + +class WrappedRecord { +public: + WrappedRecord(const Record &obj) : m_obj(obj) {} + +private: + const Record &m_obj; + + friend bool operator==(const WrappedRecord &lhs, const WrappedRecord &rhs) { + return lhs.m_obj.start_offset == rhs.m_obj.start_offset && + std::equal( + lhs.m_obj.record.fields.begin(), lhs.m_obj.record.fields.end(), + rhs.m_obj.record.fields.begin(), rhs.m_obj.record.fields.end(), + [](const MemberUP &lhs, const MemberUP &rhs) { + return WrappedMember(*lhs) == WrappedMember(*rhs); + }); + } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, + const WrappedRecord &w) { + os << llvm::formatv("Record{.start_offset={0}, .record.fields=[", + w.m_obj.start_offset); + llvm::ListSeparator sep; + for (const MemberUP &f : w.m_obj.record.fields) + os << sep << WrappedMember(*f); + return os << "]}"; + } +}; class UdtRecordCompleterRecordTests : public testing::Test { +protected: Record record; -public: void SetKind(Member::Kind kind) { record.record.kind = kind; } void CollectMember(StringRef name, uint64_t byte_offset, uint64_t byte_size) { record.CollectMember(name, byte_offset * 8, byte_size * 8, clang::QualType(), lldb::eAccessPublic, 0); } void ConstructRecord() { record.ConstructRecord(); } - - static bool VerifyMembers(const llvm::SmallVector &lhs, - const llvm::SmallVector &rhs) { - if (lhs.size() != rhs.size()) - return false; - for (size_t i = 0; i < lhs.size(); ++i) { - if (lhs[i]->kind != rhs[i]->kind || lhs[i]->name != rhs[i]->name || - lhs[i]->bit_offset != rhs[i]->bit_offset || - lhs[i]->bit_size != rhs[i]->bit_size || - lhs[i]->base_offset != rhs[i]->base_offset || - !VerifyMembers(lhs[i]->fields, rhs[i]->fields)) - return false; - } - return true; - } - bool VerifyRecord(Record &testRecord) { - return record.start_offset == testRecord.start_offset && - VerifyMembers(record.record.fields, testRecord.record.fields); - } }; Member *AddField(Member *member, StringRef name, uint64_t byte_offset, uint64_t byte_size, Member::Kind kind, @@ -84,7 +126,7 @@ AddField(u, "m2", 0, 4, Member::Field); AddField(u, "m3", 0, 1, Member::Field); AddField(u, "m4", 0, 8, Member::Field); - EXPECT_TRUE(VerifyRecord(record)); + EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record)); } TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInUnion) { @@ -107,7 +149,30 @@ AddField(&record.record, "m2", 0, 4, Member::Field); AddField(&record.record, "m3", 0, 1, Member::Field); AddField(&record.record, "m4", 0, 8, Member::Field); - EXPECT_TRUE(VerifyRecord(record)); + EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record)); +} + +TEST_F(UdtRecordCompleterRecordTests, CounterExample) { + SetKind(Member::Kind::Union); + CollectMember("m1", 0, 4); + CollectMember("m2", 4, 4); + CollectMember("m3", 8, 1); + ConstructRecord(); + + // union { + // struct { + // m1; + // m2; + // m3; + // }; + // }; + Record record; + record.start_offset = 0; + Member *s = AddField(&record.record, "", 0, 9, Member::Kind::Struct); + AddField(s, "m1", 0, 4, Member::Field); + AddField(s, "m2", 4, 4, Member::Field); + AddField(s, "m3", 8, 1, Member::Field); + EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record)); } TEST_F(UdtRecordCompleterRecordTests, TestNestedUnionStructInStruct) { @@ -142,7 +207,7 @@ AddField(s1, "m5", 3, 2, Member::Field); AddField(s2, "m3", 0, 2, Member::Field); AddField(s2, "m4", 2, 4, Member::Field); - EXPECT_TRUE(VerifyRecord(record)); + EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record)); } TEST_F(UdtRecordCompleterRecordTests, TestNestedUnionStructInUnion) { @@ -174,5 +239,5 @@ AddField(s1, "m5", 3, 2, Member::Field); AddField(s2, "m3", 0, 2, Member::Field); AddField(s2, "m4", 2, 4, Member::Field); - EXPECT_TRUE(VerifyRecord(record)); + EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record)); }