parselink-old/tests/msgpack/test_packer.cpp

323 lines
12 KiB
C++

//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
// Default packer tests.
//
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
#include "parselink/msgpack/core/packer.h"
#include <fmt/format.h>
#include <fmt/ranges.h>
#include "rng.h"
#include <algorithm>
#include <boost/ut.hpp>
#include <chrono>
using namespace boost::ut;
template <>
struct fmt::formatter<msgpack::invalid> : fmt::formatter<std::string_view> {
template <typename FormatContext>
auto format(msgpack::nil const&, FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "{{msgpack::invalid}}");
}
};
template <>
struct fmt::formatter<msgpack::nil> : fmt::formatter<std::string_view> {
template <typename FormatContext>
auto format(msgpack::nil const&, FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "{{msgpack::nil}}");
}
};
namespace {
template <typename... Bytes>
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes&&... bytes) {
return {static_cast<std::byte>(std::forward<Bytes>(bytes))...};
}
constexpr auto equal(auto a, auto b) {
return std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b));
}
template <typename T>
struct test_data {};
template <>
struct test_data<msgpack::nil> {
static constexpr msgpack::nil values[] = {{}};
static constexpr auto payload = make_bytes(0xc0);
};
template <>
struct test_data<msgpack::invalid> {
static constexpr msgpack::invalid values[] = {{}};
static constexpr auto payload = make_bytes(0xc1);
};
template <>
struct test_data<bool> {
static constexpr auto values = std::to_array<bool>({false, true});
static constexpr auto payload = make_bytes(0xc2, 0xc3);
};
template <>
struct test_data<std::uint64_t> {
static constexpr auto values = std::to_array<std::uint64_t>({
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<std::uint64_t>::max();
}
};
template <std::unsigned_integral T>
struct test_data<T> : test_data<std::uint64_t> {
static constexpr auto valid(auto value) noexcept {
return value <= std::numeric_limits<T>::max();
}
};
template <>
struct test_data<std::int64_t> {
static constexpr auto values = std::to_array<std::int64_t>({
-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<std::int64_t>::lowest(), // int64
std::numeric_limits<std::int64_t>::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<std::int64_t>::max()
&& value >= std::numeric_limits<std::int64_t>::lowest();
}
};
template <std::convertible_to<std::string_view> T>
struct test_data<T> {
static constexpr auto values = std::to_array<T>({
"", // 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<std::uint32_t>::max();
}
};
template <>
struct test_data<std::span<std::byte const>> {
static constexpr auto test1 = make_bytes(0x02, 0x03);
static constexpr auto values =
std::to_array<std::span<std::byte const>>({test1});
static constexpr auto payload = make_bytes(0xc4, 0x02, 0x02, 0x03);
static constexpr auto valid(auto value) noexcept {
return value.size() <= std::numeric_limits<std::uint32_t>::max();
}
};
template <std::signed_integral T>
struct test_data<T> : test_data<std::int64_t> {
static constexpr auto valid(auto value) noexcept {
return value <= std::numeric_limits<T>::max()
&& value >= std::numeric_limits<T>::lowest();
}
};
template <typename T>
bool test_deduced() noexcept {
constexpr auto const& expected_payload = test_data<T>::payload;
std::array<std::byte, expected_payload.size()> payload;
msgpack::packer packer(payload);
for (auto const& value : test_data<T>::values) {
if constexpr (requires { test_data<T>::valid(value); }) {
if (!test_data<T>::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 = [] {
"packer::pack<bool>"_test = [] { expect(test_deduced<bool>()); };
"packer::pack<nil>"_test = [] { expect(test_deduced<msgpack::nil>()); };
"packer::pack<invalid>"_test = [] {
expect(test_deduced<msgpack::invalid>());
};
// Unsigned ints
"packer::pack<std::uint8_t>"_test = [] {
expect(test_deduced<std::uint8_t>());
};
"packer::pack<std::uint16_t>"_test = [] {
expect(test_deduced<std::uint16_t>());
};
"packer::pack<std::uint32_t>"_test = [] {
expect(test_deduced<std::uint32_t>());
};
"packer::pack<std::uint64_t>"_test = [] {
expect(test_deduced<std::uint64_t>());
};
// Signed ints
"packer::pack<std::int8_t>"_test = [] {
expect(test_deduced<std::int8_t>());
};
"packer::pack<std::int16_t>"_test = [] {
expect(test_deduced<std::int16_t>());
};
"packer::pack<std::int32_t>"_test = [] {
expect(test_deduced<std::int32_t>());
};
"packer::pack<std::int64_t>"_test = [] {
expect(test_deduced<std::int64_t>());
};
// Strings
"packer::pack<std::string_view>"_test = [] {
expect(test_deduced<std::string_view>());
};
#if 0
"packer::pack<char const*>"_test = [] {
expect(test_deduced<char const*>());
};
#endif
"packer::pack<std::span<std::byte>>"_test = [] {
expect(test_deduced<std::span<std::byte const>>());
};
"packer::pack<std::array<std::byte, N>>"_test = [] {
{
constexpr auto data = make_bytes(0x01, 0x02, 0x03, 0x04);
constexpr auto expected =
make_bytes(0xc4, 0x04, 0x01, 0x02, 0x03, 0x04);
std::array<std::byte, std::size(expected)> payload;
msgpack::packer packer(payload);
expect(!!packer.pack(data));
expect(std::ranges::equal(payload, expected));
}
{
constexpr std::array<std::byte, 256> data{};
constexpr std::array<std::byte, 259> expected{
std::byte(0xc5), std::byte(0x01), std::byte(0x00)};
std::array<std::byte, std::size(expected)> payload;
msgpack::packer packer(payload);
expect(!!packer.pack(data));
expect(std::ranges::equal(payload, expected));
}
{
constexpr std::array<std::byte, 65536> data{};
constexpr std::array<std::byte, std::size(data) + 5> expected{
std::byte(0xc6), std::byte(0x00), std::byte(0x01),
std::byte(0x00), std::byte(0x00)};
std::array<std::byte, std::size(expected)> payload;
msgpack::packer packer(payload);
expect(!!packer.pack(data));
expect(std::ranges::equal(payload, expected));
}
};
};