/// \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/Support/MetaAddress/KeyTraits.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.Name = "FunctionName"; revng_check(get<1>(TheFunction) == "FunctionName"); // Test std::tuple_size static_assert(std::tuple_size::value >= 2); // Test TupleLikeTraits using TLT = TupleLikeTraits; static_assert(std::is_same_v, decltype(TheFunction.Name)>); revng_check(StringRef(TLT::name()) == "model::Function"); revng_check(StringRef(TLT::fieldName<1>()) == "Name"); } BOOST_AUTO_TEST_CASE(TestPathAccess) { Binary TheBinary; using FunctionsType = decltype(TheBinary.Functions); auto *FirstField = getByPath(KeyIntVector{ 0 }, 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); } template<> struct llvm::yaml::ScalarTraits> : CompositeScalar, '-'> {}; BOOST_AUTO_TEST_CASE(TestCompositeScalar) { // MetaAddress pair { using BlockKeyPair = std::pair; BlockKeyPair BlockKey = { ARM2000, ARM3000 }; auto BlockKeyName = getNameFromYAMLScalar(BlockKey); revng_check(BlockKeyName == "0x2000:Code_arm-0x3000:Code_arm"); } } BOOST_AUTO_TEST_CASE(TestStringPathConversion) { revng_check(stringAsPath("/").value() == KeyIntVector{}); revng_check(stringAsPath("/Functions").value() == KeyIntVector{ 0 }); KeyIntVector FiveZeros{ 0, 0, 0, 0, 0 }; auto MaybeInvalidFunctionPath = stringAsPath("/Functions/:Invalid"); revng_check(MaybeInvalidFunctionPath.value() == FiveZeros); KeyIntVector FiveZerosAndOne{ 0, 0, 0, 0, 0, 1 }; auto MaybePath = stringAsPath("/Functions/:Invalid/Name"); revng_check(MaybePath.value() == FiveZerosAndOne); 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"); 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("/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); } // // Double matcher // { auto MaybeMatcher = PathMatcher::create("/Functions/*/CFG/*/Start"); auto Matcher = MaybeMatcher.value(); auto ARM1000EntryPath = Matcher.apply(ARM1000, ARM2000); auto ARM1000EntryPathAsString = pathAsString(ARM1000EntryPath); auto ExpectedName = ("/Functions/0x1000:Code_arm/CFG/" "0x2000:Code_arm/Start"); revng_check(ARM1000EntryPathAsString == ExpectedName); auto Match = Matcher.match(ARM1000EntryPath); revng_check(Match); revng_check(std::get<0>(*Match) == ARM1000); revng_check(std::get<1>(*Match) == ARM2000); { auto Path = stringAsPath("/Functions"); revng_check((not Matcher.match(Path.value()))); } { auto Path = stringAsPath("/Functions/:Invalid"); revng_check((not Matcher.match(Path.value()))); } } }