blob: 17284b61b9a6ee1c522115c7ebc985f7859caa13 [file] [log] [blame]
//===-- UdtRecordCompleterTests.cpp ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h"
#include "llvm/ADT/StringExtras.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using namespace lldb_private::npdb;
using namespace llvm;
namespace {
using Member = UdtRecordCompleter::Member;
using MemberUP = std::unique_ptr<Member>;
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(); }
};
Member *AddField(Member *member, StringRef name, uint64_t byte_offset,
uint64_t byte_size, Member::Kind kind,
uint64_t base_offset = 0) {
auto field =
std::make_unique<Member>(name, byte_offset * 8, byte_size * 8,
clang::QualType(), lldb::eAccessPublic, 0);
field->kind = kind;
field->base_offset = base_offset;
member->fields.push_back(std::move(field));
return member->fields.back().get();
}
} // namespace
TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInStruct) {
SetKind(Member::Kind::Struct);
CollectMember("m1", 0, 4);
CollectMember("m2", 0, 4);
CollectMember("m3", 0, 1);
CollectMember("m4", 0, 8);
ConstructRecord();
// struct {
// union {
// m1;
// m2;
// m3;
// m4;
// };
// };
Record record;
record.start_offset = 0;
Member *u = AddField(&record.record, "", 0, 0, Member::Union);
AddField(u, "m1", 0, 4, Member::Field);
AddField(u, "m2", 0, 4, Member::Field);
AddField(u, "m3", 0, 1, Member::Field);
AddField(u, "m4", 0, 8, Member::Field);
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
}
TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInUnion) {
SetKind(Member::Kind::Union);
CollectMember("m1", 0, 4);
CollectMember("m2", 0, 4);
CollectMember("m3", 0, 1);
CollectMember("m4", 0, 8);
ConstructRecord();
// union {
// m1;
// m2;
// m3;
// m4;
// };
Record record;
record.start_offset = 0;
AddField(&record.record, "m1", 0, 4, Member::Field);
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_EQ(WrappedRecord(this->record), WrappedRecord(record));
}
TEST_F(UdtRecordCompleterRecordTests, TestAnonymousStructInUnion) {
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, 0, 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) {
SetKind(Member::Kind::Struct);
CollectMember("m1", 0, 4);
CollectMember("m2", 0, 2);
CollectMember("m3", 0, 2);
CollectMember("m4", 2, 4);
CollectMember("m5", 3, 2);
ConstructRecord();
// struct {
// union {
// m1;
// struct {
// m2;
// m5;
// };
// struct {
// m3;
// m4;
// };
// };
// };
Record record;
record.start_offset = 0;
Member *u = AddField(&record.record, "", 0, 0, Member::Union);
AddField(u, "m1", 0, 4, Member::Field);
Member *s1 = AddField(u, "", 0, 0, Member::Struct);
Member *s2 = AddField(u, "", 0, 0, Member::Struct);
AddField(s1, "m2", 0, 2, Member::Field);
AddField(s1, "m5", 3, 2, Member::Field);
AddField(s2, "m3", 0, 2, Member::Field);
AddField(s2, "m4", 2, 4, Member::Field);
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
}
TEST_F(UdtRecordCompleterRecordTests, TestNestedUnionStructInUnion) {
SetKind(Member::Kind::Union);
CollectMember("m1", 0, 4);
CollectMember("m2", 0, 2);
CollectMember("m3", 0, 2);
CollectMember("m4", 2, 4);
CollectMember("m5", 3, 2);
ConstructRecord();
// union {
// m1;
// struct {
// m2;
// m5;
// };
// struct {
// m3;
// m4;
// };
// };
Record record;
record.start_offset = 0;
AddField(&record.record, "m1", 0, 4, Member::Field);
Member *s1 = AddField(&record.record, "", 0, 0, Member::Struct);
Member *s2 = AddField(&record.record, "", 0, 0, Member::Struct);
AddField(s1, "m2", 0, 2, Member::Field);
AddField(s1, "m5", 3, 2, Member::Field);
AddField(s2, "m3", 0, 2, Member::Field);
AddField(s2, "m4", 2, 4, Member::Field);
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
}