/// \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" #include "revng/TupleTree/VisitsImpl.h" #include "revng/UnitTestHelpers/UnitTestHelpers.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::value >= 2); // Test TupleLikeTraits static_assert(TraitedTupleLike); using TLT = TupleLikeTraits; static_assert(std::is_same_v &, decltype(TheFunction.CustomName())>); revng_check(StringRef(TLT::Name) == "Function"); revng_check(StringRef(TLT::FullName) == "model::Function"); revng_check(StringRef(TLT::FieldNames[1]) == "CustomName"); } BOOST_AUTO_TEST_CASE(TestPathAccess) { Binary TheBinary; using FunctionsType = std::decay_t; TupleTreePath Zero; Zero.push_back(size_t(0)); auto *FirstField = getByPath(Zero, TheBinary); revng_check(FirstField == &TheBinary.Functions()); auto *FunctionsField = getByPath("/Functions", TheBinary); revng_check(FunctionsField == &TheBinary.Functions()); // Test non existing field revng_check(getByPath("/Function", TheBinary) == nullptr); // Test non existing entry in container revng_check(getByPath("/Functions/:Invalid", TheBinary) == nullptr); // Test existing entry in container Function &F = TheBinary.Functions()[MetaAddress::invalid()]; revng_check(getByPath("/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("/").value() == TupleTreePath{}); TupleTreePath Zero; Zero.push_back(size_t(0)); revng_check(stringAsPath("/Functions").value() == Zero); TupleTreePath InvalidFunctionPath; InvalidFunctionPath.push_back(size_t(0)); InvalidFunctionPath.push_back(MetaAddress::invalid()); auto MaybeInvalidFunctionPath = stringAsPath("/Functions/:Invalid"); revng_check(MaybeInvalidFunctionPath.value() == InvalidFunctionPath); TupleTreePath InvalidFunctionNamePath = InvalidFunctionPath; InvalidFunctionNamePath.push_back(size_t(1)); auto MaybePath = stringAsPath("/Functions/:Invalid/CustomName"); revng_check(MaybePath.value() == InvalidFunctionNamePath); auto CheckRoundTrip = [](const char *String) { auto Path = stringAsPath(String).value(); auto StringAgain = pathAsString(Path); revng_check(StringAgain == String); }; CheckRoundTrip("/Functions"); CheckRoundTrip("/Functions/:Invalid"); CheckRoundTrip("/Functions/:Invalid/Entry"); CheckRoundTrip("/Functions/0x1000:Code_arm/Entry"); } BOOST_AUTO_TEST_CASE(TestPathMatcher) { // // Single matcher // { auto Matcher = PathMatcher::create("/Functions/*/Entry").value(); auto ARM1000EntryPath = pathAsString(Matcher.apply(ARM1000)); revng_check(ARM1000EntryPath == "/Functions/0x1000:Code_arm/Entry"); auto MaybeToMatch = stringAsPath("/Functions/0x1000:Code_arm/" "Entry"); auto MaybeMatch = Matcher.match(MaybeToMatch.value()); revng_check(MaybeMatch); revng_check(std::get<0>(*MaybeMatch) == ARM1000); } } template static T *createType(model::Binary &Model) { model::TypePath Path = Model.recordNewType(makeType()); return llvm::cast(Path.get()); } BOOST_AUTO_TEST_CASE(TestModelDeduplication) { TupleTree 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(*Model); Typedef1->UnderlyingType() = { UInt8, {} }; auto *Typedef2 = createType(*Model); Typedef2->UnderlyingType() = { UInt8, {} }; revng_check(Dedup() == 0); Typedef1->OriginalName() = "MyUInt8"; Typedef2->OriginalName() = "MyUInt8"; revng_check(Dedup() == 1); } // Two structs { auto *Struct1 = createType(*Model); Struct1->Fields()[0].CustomName() = "FirstField"; Struct1->Fields()[0].Type() = { UInt8, {} }; Struct1->OriginalName() = "MyStruct"; auto *Struct2 = createType(*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(*Model); auto *Left2 = createType(*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(*Model); auto *Right2 = createType(*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(); } BOOST_AUTO_TEST_CASE(TestTupleTreeDiffSerialization) { model::Binary Left; model::Binary Right; auto Diff = diff(Left, Right); std::string S; llvm::raw_string_ostream Stream(S); serialize(Stream, Diff); } BOOST_AUTO_TEST_CASE(TestTupleTreeDiffDeserialization) { model::Binary Empty; model::Binary New; MetaAddress Address(0x1000, MetaAddressType::Code_aarch64); New.ExtraCodeAddresses().insert(Address); auto Diff = diff(Empty, New); std::string S; llvm::raw_string_ostream Stream(S); serialize(Stream, Diff); Stream.flush(); auto Diff2 = llvm::cantFail(deserialize>(S)); std::string S2; llvm::raw_string_ostream Stream2(S2); serialize(Stream2, Diff2); Stream2.flush(); BOOST_TEST(S == S2); } BOOST_AUTO_TEST_CASE(CABIFunctionTypePathShouldParse) { const char *Path = "/Types/CABIFunctionType-10000"; auto MaybeParsed = stringAsPath(Path); BOOST_TEST(MaybeParsed.has_value()); } BOOST_AUTO_TEST_CASE(CABIFunctionTypeArgumentsPathShouldParse) { const char *Path = "/Types/CABIFunctionType-10000/Arguments"; auto MaybeParsed = stringAsPath(Path); BOOST_TEST(MaybeParsed.has_value()); }