Files
revng-revng/tests/unit/Model.cpp
Alessandro Di Federico a88a9e52c3 Improve model::Segment
2022-03-08 13:06:47 +01:00

299 lines
9.4 KiB
C++

/// \file Model.cpp
/// \brief
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#define BOOST_TEST_MODULE Model
bool init_unit_test();
#include "boost/test/unit_test.hpp"
#include "revng/Model/Binary.h"
#include "revng/Model/Pass/AllPasses.h"
#include "revng/Model/Processing.h"
#include "revng/Support/MetaAddress.h"
#include "revng/Support/MetaAddress/YAMLTraits.h"
#include "revng/Support/YAMLTraits.h"
#include "revng/TupleTree/Introspection.h"
#include "revng/TupleTree/TupleTreeDiff.h"
using namespace model;
auto ARM1000 = MetaAddress::fromString("0x1000:Code_arm");
auto ARM2000 = MetaAddress::fromString("0x2000:Code_arm");
auto ARM3000 = MetaAddress::fromString("0x3000:Code_arm");
BOOST_AUTO_TEST_CASE(TestIntrospection) {
using namespace llvm;
Function TheFunction(MetaAddress::invalid());
// Use get
TheFunction.CustomName = "FunctionName";
revng_check(get<1>(TheFunction) == "FunctionName");
// Test std::tuple_size
static_assert(std::tuple_size<Function>::value >= 2);
// Test TupleLikeTraits
using TLT = TupleLikeTraits<Function>;
static_assert(std::is_same_v<std::tuple_element_t<1, Function>,
decltype(TheFunction.CustomName)>);
revng_check(StringRef(TLT::Name) == "model::Function");
revng_check(StringRef(TLT::FieldsName[1]) == "CustomName");
}
BOOST_AUTO_TEST_CASE(TestPathAccess) {
Binary TheBinary;
using FunctionsType = decltype(TheBinary.Functions);
TupleTreePath Zero;
Zero.push_back(size_t(0));
auto *FirstField = getByPath<FunctionsType>(Zero, TheBinary);
revng_check(FirstField == &TheBinary.Functions);
auto *FunctionsField = getByPath<FunctionsType>("/Functions", TheBinary);
revng_check(FunctionsField == &TheBinary.Functions);
// Test non existing field
revng_check(getByPath<FunctionsType>("/Function", TheBinary) == nullptr);
// Test non existing entry in container
revng_check(getByPath<Function>("/Functions/:Invalid", TheBinary) == nullptr);
// Test existing entry in container
Function &F = TheBinary.Functions[MetaAddress::invalid()];
revng_check(getByPath<Function>("/Functions/:Invalid", TheBinary) == &F);
}
BOOST_AUTO_TEST_CASE(TestCompositeScalar) {
// MetaAddress pair
{
model::Segment::Key BlockKey = { ARM2000, 1000 };
auto BlockKeyName = getNameFromYAMLScalar(BlockKey);
revng_check(BlockKeyName == "0x2000:Code_arm-1000");
}
}
BOOST_AUTO_TEST_CASE(TestStringPathConversion) {
revng_check(stringAsPath<Binary>("/").value() == TupleTreePath{});
TupleTreePath Zero;
Zero.push_back(size_t(0));
revng_check(stringAsPath<Binary>("/Functions").value() == Zero);
TupleTreePath InvalidFunctionPath;
InvalidFunctionPath.push_back(size_t(0));
InvalidFunctionPath.push_back(MetaAddress::invalid());
auto MaybeInvalidFunctionPath = stringAsPath<Binary>("/Functions/:Invalid");
revng_check(MaybeInvalidFunctionPath.value() == InvalidFunctionPath);
TupleTreePath InvalidFunctionNamePath = InvalidFunctionPath;
InvalidFunctionNamePath.push_back(size_t(1));
auto MaybePath = stringAsPath<Binary>("/Functions/:Invalid/CustomName");
revng_check(MaybePath.value() == InvalidFunctionNamePath);
auto CheckRoundTrip = [](const char *String) {
auto Path = stringAsPath<Binary>(String).value();
auto StringAgain = pathAsString<Binary>(Path);
revng_check(StringAgain == String);
};
CheckRoundTrip("/Functions");
CheckRoundTrip("/Functions/:Invalid");
CheckRoundTrip("/Functions/:Invalid/Entry");
CheckRoundTrip("/Functions/0x1000:Code_arm/Entry");
CheckRoundTrip("/Functions/0x1000:Code_arm/CFG/0x2000:Code_arm/Start");
CheckRoundTrip("/Functions/0x1000:Code_arm/CFG/0x2000:Code_arm/Successors"
"/0x2000:Code_arm-DirectBranch/Destination");
}
BOOST_AUTO_TEST_CASE(TestPathMatcher) {
//
// Single matcher
//
{
auto Matcher = PathMatcher::create<Binary>("/Functions/*/Entry").value();
auto ARM1000EntryPath = pathAsString<Binary>(Matcher.apply(ARM1000));
revng_check(ARM1000EntryPath == "/Functions/0x1000:Code_arm/Entry");
auto MaybeToMatch = stringAsPath<Binary>("/Functions/0x1000:Code_arm/"
"Entry");
auto MaybeMatch = Matcher.match<MetaAddress>(MaybeToMatch.value());
revng_check(MaybeMatch);
revng_check(std::get<0>(*MaybeMatch) == ARM1000);
}
//
// Double matcher
//
{
auto MaybeMatcher = PathMatcher::create<Binary>("/Functions/*/CFG/*/Start");
auto Matcher = MaybeMatcher.value();
auto ARM1000EntryPath = Matcher.apply(ARM1000, ARM2000);
auto ARM1000EntryPathAsString = pathAsString<Binary>(ARM1000EntryPath);
const auto *ExpectedName = ("/Functions/0x1000:Code_arm/CFG/"
"0x2000:Code_arm/Start");
revng_check(ARM1000EntryPathAsString == ExpectedName);
auto Match = Matcher.match<MetaAddress, MetaAddress>(ARM1000EntryPath);
revng_check(Match);
revng_check(std::get<0>(*Match) == ARM1000);
revng_check(std::get<1>(*Match) == ARM2000);
{
auto Path = stringAsPath<Binary>("/Functions");
revng_check((not Matcher.match<MetaAddress, MetaAddress>(Path.value())));
}
{
auto Path = stringAsPath<Binary>("/Functions/:Invalid");
revng_check((not Matcher.match<MetaAddress, MetaAddress>(Path.value())));
}
}
}
namespace TestTupleTree {
class Element;
class Root;
} // namespace TestTupleTree
class TestTupleTree::Element {
public:
int Key;
TupleTreeReference<TestTupleTree::Element, TestTupleTree::Root> Self;
};
INTROSPECTION_NS(TestTupleTree, Element, Key, Self)
template<>
struct KeyedObjectTraits<TestTupleTree::Element> {
static int key(const TestTupleTree::Element &Obj) { return Obj.Key; }
static TestTupleTree::Element fromKey(const int &Key) {
return TestTupleTree::Element{ Key, {} };
}
};
class TestTupleTree::Root {
public:
SortedVector<TestTupleTree::Element> Elements;
};
INTROSPECTION_NS(TestTupleTree, Root, Elements)
static_assert(IsTupleLike<TestTupleTree::Root>);
BOOST_AUTO_TEST_CASE(TestTupleTreeReference) {
using namespace TestTupleTree;
using Reference = TupleTreeReference<TestTupleTree::Element,
TestTupleTree::Root>;
TupleTree<Root> TheRoot;
Element &AnElement = TheRoot->Elements[3];
AnElement.Self = Reference::fromString(TheRoot.get(), "/Elements/3");
TheRoot.initializeReferences();
revng_check(AnElement.Self.get() == &AnElement);
}
template<typename T>
static T *createType(model::Binary &Model) {
model::TypePath Path = Model.recordNewType(makeType<T>());
return llvm::cast<T>(Path.get());
}
BOOST_AUTO_TEST_CASE(TestModelDeduplication) {
TupleTree<model::Binary> Model;
auto Dedup = [&Model]() {
int64_t OldTypesCount = Model->Types.size();
deduplicateEquivalentTypes(Model);
int64_t NewTypesCount = Model->Types.size();
return OldTypesCount - NewTypesCount;
};
model::TypePath UInt8 = Model->getPrimitiveType(PrimitiveTypeKind::Generic,
4);
// Two typedefs
{
auto *Typedef1 = createType<TypedefType>(*Model);
Typedef1->UnderlyingType = { UInt8, {} };
auto *Typedef2 = createType<TypedefType>(*Model);
Typedef2->UnderlyingType = { UInt8, {} };
revng_check(Dedup() == 0);
Typedef1->OriginalName = "MyUInt8";
Typedef2->OriginalName = "MyUInt8";
revng_check(Dedup() == 1);
}
// Two structs
{
auto *Struct1 = createType<StructType>(*Model);
Struct1->Fields[0].CustomName = "FirstField";
Struct1->Fields[0].Type = { UInt8, {} };
Struct1->OriginalName = "MyStruct";
auto *Struct2 = createType<StructType>(*Model);
Struct2->Fields[0].CustomName = "DifferentName";
Struct2->Fields[0].Type = { UInt8, {} };
Struct2->OriginalName = "MyStruct";
revng_check(Dedup() == 0);
Struct1->Fields[0].CustomName = Struct2->Fields[0].CustomName;
revng_check(Dedup() == 1);
}
// Two pairs of cross-referencing structs
{
auto PointerQualifier = Qualifier::createPointer(8);
auto *Left1 = createType<StructType>(*Model);
auto *Left2 = createType<StructType>(*Model);
Left1->Fields[0].Type = { Model->getTypePath(Left2), { PointerQualifier } };
Left2->Fields[0].Type = { Model->getTypePath(Left1), { PointerQualifier } };
Left1->OriginalName = "LoopingStructs1";
Left2->OriginalName = "LoopingStructs2";
auto *Right1 = createType<StructType>(*Model);
auto *Right2 = createType<StructType>(*Model);
Right1->Fields[0].Type = { Model->getTypePath(Right2),
{ PointerQualifier } };
Right2->Fields[0].Type = { Model->getTypePath(Right1),
{ PointerQualifier, PointerQualifier } };
Right1->OriginalName = "LoopingStructs1";
Right2->OriginalName = "LoopingStructs2";
revng_check(Dedup() == 0);
Right2->Fields[0].Type = { Model->getTypePath(Right1),
{ PointerQualifier } };
revng_check(Dedup() == 2);
}
}
BOOST_AUTO_TEST_CASE(TestTupleTreeDiff) {
model::Binary Left;
model::Binary Right;
diff(Left, Right).dump();
}
static_assert(std::is_default_constructible_v<TupleTree<TestTupleTree::Root>>);
static_assert(not std::is_copy_assignable_v<TupleTree<TestTupleTree::Root>>);
static_assert(not std::is_copy_constructible_v<TupleTree<TestTupleTree::Root>>);
static_assert(std::is_move_assignable_v<TupleTree<TestTupleTree::Root>>);
static_assert(std::is_move_constructible_v<TupleTree<TestTupleTree::Root>>);