parselink-old/tests/msgpack/test_packer_rc.cpp
2024-01-03 15:51:22 -08:00

290 lines
9.2 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>
#include <rapidcheck.h>
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}}");
}
};
template <>
struct fmt::formatter<msgpack::map_desc> : fmt::formatter<std::string_view> {
template <typename FormatContext>
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<msgpack::array_desc> : fmt::formatter<std::string_view> {
template <typename FormatContext>
auto format(msgpack::array_desc const& value, FormatContext& ctx) const {
return fmt::format_to(
ctx.out(), "{{msgpack::array_desc: {}}}", value.count);
}
};
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, typename U>
consteval auto within() noexcept {
return std::numeric_limits<T>::max() <= std::numeric_limits<U>::max();
}
template <typename T>
constexpr auto verify_packed(auto const& packer, auto value) {
std::array<std::byte, std::numeric_limits<T>::digits / 8> raw;
if (packer.tell() != 1 + raw.size()) return false;
auto packed = packer.subspan().subspan(1);
be_to_host(packed.begin(), packed.end(), raw.begin());
auto actual = std::bit_cast<T>(raw);
return actual == value;
}
template <typename T>
auto check_unsigned() {
return rc::check([] {
std::array<std::byte, 16> payload;
msgpack::packer packer(payload);
const auto value = *rc::gen::positive<T>();
if (!packer.pack(value)) return false;
if (value < 0x80) {
// positive_fixint
return packer.tell() == 1
&& payload[0] == static_cast<std::byte>(value);
} else if (value <= std::numeric_limits<std::uint8_t>::max()) {
// uint8
return packer.tell() == 2 && payload[0] == std::byte{0xcc}
&& T(payload[1]) == value;
} else if (value <= std::numeric_limits<std::uint16_t>::max()) {
// uint16
return payload[0] == std::byte{0xcd}
&& verify_packed<std::uint16_t>(packer, value);
} else if (value <= std::numeric_limits<std::uint32_t>::max()) {
// uint32
return payload[0] == std::byte{0xce}
&& verify_packed<std::uint32_t>(packer, value);
} else {
// uint64
return payload[0] == std::byte{0xcf}
&& verify_packed<std::uint64_t>(packer, value);
}
});
}
} // anonymous namespace
suite packer_single_format = [] {
"packer::pack<nil>"_test = [] {
std::array<std::byte, 8> payload;
msgpack::packer packer(payload);
expect(!!packer.pack(msgpack::nil{}));
expect(packer.tell() == 1);
expect(payload[0] == std::byte{0xc0});
};
"packer::pack<invalid>"_test = [] {
std::array<std::byte, 8> payload;
msgpack::packer packer(payload);
expect(!!packer.pack(msgpack::invalid{}));
expect(packer.tell() == 1);
expect(payload[0] == std::byte{0xc1});
};
"packer::pack<bool>"_test = [] {
std::array<std::byte, 8> payload;
msgpack::packer packer(payload);
expect(!!packer.pack(false));
expect(packer.tell() == 1);
expect(payload[0] == std::byte{0xc2});
expect(!!packer.pack(true));
expect(packer.tell() == 2);
expect(payload[1] == std::byte{0xc3});
};
"packer::pack<std::uint8_t>"_test = [] {
expect(check_unsigned<std::uint8_t>());
};
"packer::pack<std::uint16_t>"_test = [] {
expect(check_unsigned<std::uint16_t>());
};
"packer::pack<std::uint32_t>"_test = [] {
expect(check_unsigned<std::uint32_t>());
};
"packer::pack<std::uint64_t>"_test = [] {
expect(check_unsigned<std::uint64_t>());
};
};
#if 0
"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
// Byte ranges -> Binary
"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));
}
};
// array_desc - Just the header of the array.
"packer::pack<msgpack::array_desc>"_test = [] {
expect(test_deduced<msgpack::array_desc>());
};
// map_desc - Just the header of the map.
"packer::pack<msgpack::map_desc>"_test = [] {
expect(test_deduced<msgpack::map_desc>());
};
};
suite packer_ranges = [] {
"packer::pack<std::array/std::span<int>>"_test = [] {
constexpr auto data_array = std::to_array<int>({5, 10, 15, 20});
constexpr auto expected =
make_bytes(0x94, 0xd0, 0x05, 0xd0, 0xa, 0xd0, 0xf, 0xd0, 0x14);
{
std::array<std::byte, std::size(expected)> payload;
msgpack::packer packer(payload);
expect(!!packer.pack(data_array));
expect(std::ranges::equal(payload, expected));
}
{
std::span data_span{data_array};
std::array<std::byte, std::size(expected)> payload;
msgpack::packer packer(payload);
expect(!!packer.pack(data_span));
expect(std::ranges::equal(payload, expected));
}
};
};
#endif