//----------------------------------------------------------------------------- // ___ __ _ _ // / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __ // / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ / // / ___/ (_| | | \__ \ __/ /__| | | | | < // \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ . // //----------------------------------------------------------------------------- // Author: Kurt Sassenrath // Module: msgpack // // Default packer tests. // // Copyright (c) 2023 Kurt Sassenrath. // // License TBD. //----------------------------------------------------------------------------- #include "parselink/msgpack/core/packer.h" #include #include #include "rng.h" #include #include #include using namespace boost::ut; template <> struct fmt::formatter : fmt::formatter { template auto format(msgpack::nil const&, FormatContext& ctx) const { return fmt::format_to(ctx.out(), "{{msgpack::invalid}}"); } }; template <> struct fmt::formatter : fmt::formatter { template auto format(msgpack::nil const&, FormatContext& ctx) const { return fmt::format_to(ctx.out(), "{{msgpack::nil}}"); } }; template <> struct fmt::formatter : fmt::formatter { template auto format(msgpack::map_desc const& value, FormatContext& ctx) const { return fmt::format_to( ctx.out(), "{{msgpack::map_desc: {}}}", value.count); } }; template <> struct fmt::formatter : fmt::formatter { template auto format(msgpack::array_desc const& value, FormatContext& ctx) const { return fmt::format_to( ctx.out(), "{{msgpack::array_desc: {}}}", value.count); } }; namespace { template constexpr std::array make_bytes(Bytes&&... bytes) { return {static_cast(std::forward(bytes))...}; } constexpr auto equal(auto a, auto b) { return std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b)); } template struct test_data {}; template <> struct test_data { static constexpr msgpack::nil values[] = {{}}; static constexpr auto payload = make_bytes(0xc0); }; template <> struct test_data { static constexpr msgpack::invalid values[] = {{}}; static constexpr auto payload = make_bytes(0xc1); }; template <> struct test_data { static constexpr auto values = std::to_array({false, true}); static constexpr auto payload = make_bytes(0xc2, 0xc3); }; template <> struct test_data { static constexpr auto values = std::to_array({ 0x00, // positive fixint 0x79, // positive fixint 0x80, // uint8 0xff, // uint8 0x100, // uint16 0xffff, // uint16 0x10000, // uint32 0xffffffff, // uint32 0x100000000, // uint64 0xffffffffffffffff // uint64 }); static constexpr auto payload = make_bytes(0x00, // positive fixint 0x79, // positive fixint 0xcc, 0x80, // uint8 0xcc, 0xff, // uint8 0xcd, 0x01, 0x00, // uint16 0xcd, 0xff, 0xff, // uint16 0xce, 0x00, 0x01, 0x00, 0x00, // uint32 0xce, 0xff, 0xff, 0xff, 0xff, // uint32 0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // uint64 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // uint64 ); static constexpr auto valid(auto value) noexcept { return value <= std::numeric_limits::max(); } }; template struct test_data : test_data { static constexpr auto valid(auto value) noexcept { return value <= std::numeric_limits::max(); } }; template <> struct test_data { static constexpr auto values = std::to_array({ -1, // negative fixint -32, // negative fixint -33, // int8 -128, // int8 0, // int8 127, // int8 128, // int16 -129, // int16 -32768, // int16 32767, // int16 -32769, // int32 32768, // int32 -2147483648, // int32 2147483647, // int32 -2147483649, // int64 2147483648, // int64 std::numeric_limits::lowest(), // int64 std::numeric_limits::max(), // int64 }); static constexpr auto payload = make_bytes(0xff, // negative fixint 0xe0, // negative fixint 0xd0, 0xdf, // int8 0xd0, 0x80, // int8 0xd0, 0x0, // int8 0xd0, 0x7f, // int8 0xd1, 0x00, 0x80, // int16 0xd1, 0xff, 0x7f, // int16 0xd1, 0x80, 0x00, // int16 0xd1, 0x7f, 0xff, // int16 0xd2, 0xff, 0xff, 0x7f, 0xff, // int32 0xd2, 0x00, 0x00, 0x80, 0x00, // int32 0xd2, 0x80, 0x00, 0x00, 0x00, // int32 0xd2, 0x7f, 0xff, 0xff, 0xff, // int32 0xd3, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, // int64 0xd3, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, // int64 0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // int64 0xd3, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // int64 ); static constexpr auto valid(auto value) noexcept { return value <= std::numeric_limits::max() && value >= std::numeric_limits::lowest(); } }; template <> struct test_data { static constexpr auto values = std::to_array({ 1, // fixarray 15, // fixarray 16, // array16 65535, // array16 65536 // array32 }); static constexpr auto payload = make_bytes(0x91, // fixarray 0x9f, // fixarray 0xdc, 0x00, 0x10, // array16 0xdc, 0xff, 0xff, // array16 0xdd, 0x00, 0x01, 0x00, 0x00 // array32 ); }; template <> struct test_data { static constexpr auto values = std::to_array({ 1, // fixmap 15, // fixmap 16, // map16 65535, // map16 65536 // map32 }); static constexpr auto payload = make_bytes(0x81, // fixmap 0x8f, // fixmap 0xde, 0x00, 0x10, // map16 0xde, 0xff, 0xff, // map16 0xdf, 0x00, 0x01, 0x00, 0x00 // map32 ); }; template T> struct test_data { static constexpr auto values = std::to_array({ "", // fixstr "0", // fixstr "0123456789abcdef0123456789abcde", // fixstr "0123456789abcdef0123456789abcdef", // str8 }); static constexpr auto payload = make_bytes(0xa0, // fixstr 0xa1, 0x30, // fixstr // fixstr 0xbf, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, // str8 0xd9, 0x20, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66); static constexpr auto valid(T value) noexcept { return std::string_view(value).size() <= std::numeric_limits::max(); } }; template <> struct test_data> { static constexpr auto test1 = make_bytes(0x02, 0x03); static constexpr auto values = std::to_array>({test1}); static constexpr auto payload = make_bytes(0xc4, 0x02, 0x02, 0x03); static constexpr auto valid(auto value) noexcept { return value.size() <= std::numeric_limits::max(); } }; template struct test_data : test_data { static constexpr auto valid(auto value) noexcept { return value <= std::numeric_limits::max() && value >= std::numeric_limits::lowest(); } }; template bool test_deduced() noexcept { constexpr auto const& expected_payload = test_data::payload; std::array payload; msgpack::packer packer(payload); for (auto const& value : test_data::values) { if constexpr (requires { test_data::valid(value); }) { if (!test_data::valid(value)) break; } expect(!!packer.pack(T(value))); auto expect = std::span(expected_payload.begin(), packer.tell()); auto correct = equal(packer.subspan(), expect); if (!correct) { fmt::print("Deduction failed for '{}'\n", T(value)); fmt::print("\tActual: {::#04x}\n", packer.subspan()); fmt::print("\tExpect: {::#04x}\n", expect); return false; } } return true; } } // anonymous namespace suite packer_single_format = [] { "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; // Unsigned ints "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; // Signed ints "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; // Strings "packer::pack"_test = [] { expect(test_deduced()); }; #if 0 "packer::pack"_test = [] { expect(test_deduced()); }; #endif // Byte ranges -> Binary "packer::pack>"_test = [] { expect(test_deduced>()); }; "packer::pack>"_test = [] { { constexpr auto data = make_bytes(0x01, 0x02, 0x03, 0x04); constexpr auto expected = make_bytes(0xc4, 0x04, 0x01, 0x02, 0x03, 0x04); std::array payload; msgpack::packer packer(payload); expect(!!packer.pack(data)); expect(std::ranges::equal(payload, expected)); } { constexpr std::array data{}; constexpr std::array expected{ std::byte(0xc5), std::byte(0x01), std::byte(0x00)}; std::array payload; msgpack::packer packer(payload); expect(!!packer.pack(data)); expect(std::ranges::equal(payload, expected)); } { constexpr std::array data{}; constexpr std::array expected{ std::byte(0xc6), std::byte(0x00), std::byte(0x01), std::byte(0x00), std::byte(0x00)}; std::array payload; msgpack::packer packer(payload); expect(!!packer.pack(data)); expect(std::ranges::equal(payload, expected)); } }; // array_desc - Just the header of the array. "packer::pack"_test = [] { expect(test_deduced()); }; // map_desc - Just the header of the map. "packer::pack"_test = [] { expect(test_deduced()); }; }; suite packer_ranges = [] { "packer::pack>"_test = [] { constexpr auto data_array = std::to_array({5, 10, 15, 20}); constexpr auto expected = make_bytes(0x94, 0xd0, 0x05, 0xd0, 0xa, 0xd0, 0xf, 0xd0, 0x14); { std::array payload; msgpack::packer packer(payload); expect(!!packer.pack(data_array)); expect(std::ranges::equal(payload, expected)); } { std::span data_span{data_array}; std::array payload; msgpack::packer packer(payload); expect(!!packer.pack(data_span)); expect(std::ranges::equal(payload, expected)); } }; };