parselink-old/tests/msgpack/test_writer.cpp

614 lines
23 KiB
C++

#include "parselink/msgpack/core/writer.h"
#include <boost/ut.hpp>
#include <string>
#include <fmt/format.h>
#include <fmt/ranges.h>
using namespace boost::ut;
namespace format = msgpack::format;
namespace {
template <typename... Bytes>
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes&&... bytes) {
return {std::byte(std::forward<Bytes>(bytes))...};
}
template <typename T, std::size_t C = 1024>
struct oversized_array {
std::array<T, C> data;
std::size_t size;
};
constexpr auto to_bytes_array_oversized(auto const& container) {
oversized_array<std::byte> arr;
std::copy(std::begin(container), std::end(container), std::begin(arr.data));
arr.size = std::distance(std::begin(container), std::end(container));
return arr;
}
consteval auto generate_bytes(auto callable) {
constexpr auto oversized = to_bytes_array_oversized(callable());
std::array<std::byte, oversized.size> out;
std::copy(std::begin(oversized.data),
std::next(std::begin(oversized.data), oversized.size),
std::begin(out));
return out;
}
template <std::size_t A, std::size_t B>
consteval auto cat(
std::array<std::byte, A> const& a, std::array<std::byte, B> const& b) {
std::array<std::byte, A + B> out;
std::copy(std::begin(a), std::next(std::begin(a), std::size(a)),
std::begin(out));
std::copy(std::begin(b), std::next(std::begin(b), std::size(b)),
std::next(std::begin(out), std::size(a)));
return out;
}
template <typename T, typename Itr>
constexpr auto zip(T val, Itr begin, Itr end) {
std::vector<T> output;
for (auto itr = begin; itr != end; ++itr) {
output.emplace_back(val);
output.emplace_back(*itr);
}
return output;
}
template <typename T, typename Iterable>
constexpr auto zip(T val, Iterable const& itr) {
return zip(val, std::begin(itr), std::end(itr));
}
constexpr auto from_string_view(std::string_view sv) {
std::vector<std::byte> range;
range.resize(sv.size());
auto itr = range.begin();
for (auto c : sv) {
*itr = std::byte(c);
++itr;
}
return range;
}
constexpr auto make_contiguous_range(std::uint8_t start, std::uint8_t end) {
auto count = std::size_t(end) - std::size_t(start) + 1;
std::vector<std::byte> range;
range.resize(count);
for (auto i = std::size_t(start); i <= std::size_t(end); ++i) {
range[i - std::size_t(start)] = std::byte(i);
}
return range;
}
constexpr auto equal(auto a, auto b) {
return std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b));
}
} // namespace
suite writer = [] {
"writer empty span"_test = [] {
std::array<std::byte, 16> payload;
msgpack::writer writer(payload);
expect(writer.tell() == 0);
};
"writer::write<format::positive_fixint>"_test = [] {
using fmt = format::positive_fixint;
using error = msgpack::error;
std::array<std::byte, 2> payload;
auto constexpr expected = make_bytes(0x32, 0x55);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::uint8_t{0x32}));
expect(writer.tell() == 1);
expect(*writer.subspan().begin() == *expected.begin());
expect(writer.write<fmt>(std::uint8_t{0x82})
== tl::make_unexpected(error::bad_value));
expect(!!writer.write<fmt>(std::uint8_t{0x55}));
expect(writer.tell() == 2);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(std::uint8_t{0x82})
== tl::make_unexpected(error::out_of_space));
expect(writer.write<fmt>(std::uint8_t{0x32})
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::uint8>"_test = [] {
using fmt = format::uint8;
using error = msgpack::error;
std::array<std::byte, 4> payload;
auto constexpr expected = make_bytes(0xcc, 0x32, 0xcc, 0x82);
msgpack::writer writer(payload);
auto result = writer.write<fmt>(std::uint8_t{0x32});
expect(!!result);
expect(writer.tell() == 2);
expect(equal(writer.subspan(), std::span{expected.begin(), 2}));
expect(!!writer.write<fmt>(std::uint8_t{0x82}));
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(std::uint8_t{0x01})
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::uint16>"_test = [] {
using fmt = format::uint16;
using error = msgpack::error;
std::array<std::byte, 6> payload;
auto constexpr expected =
make_bytes(0xcd, 0x32, 0xcc, 0xcd, 0xaa, 0xff);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::uint16_t{0x32cc}));
expect(writer.tell() == 3);
expect(equal(writer.subspan(), std::span{expected.begin(), 3}));
expect(!!writer.write<fmt>(0xaaff));
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::uint32>"_test = [] {
using fmt = format::uint32;
using error = msgpack::error;
std::array<std::byte, 5> payload;
auto constexpr expected = make_bytes(0xce, 0x01, 0x02, 0x03, 0x04);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(0x01020304));
expect(writer.tell() == 5);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::uint64>"_test = [] {
using fmt = format::uint64;
using error = msgpack::error;
std::array<std::byte, 9> payload;
auto constexpr expected = make_bytes(
0xcf, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(0x0102030405060708));
expect(writer.tell() == 9);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::negative_fixint>"_test = [] {
using fmt = format::negative_fixint;
using error = msgpack::error;
std::array<std::byte, 2> payload;
auto constexpr expected = make_bytes(0xff, 0xe0);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(-1));
expect(writer.tell() == 1);
expect(*writer.subspan().begin() == std::byte{0xff});
expect(writer.write<fmt>(-33) == tl::make_unexpected(error::bad_value));
expect(!!writer.write<fmt>(-32));
expect(writer.tell() == 2);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(-5)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::int8>"_test = [] {
using fmt = format::int8;
using error = msgpack::error;
std::array<std::byte, 4> payload;
auto constexpr expected = make_bytes(0xd0, 0x32, 0xd0, 0xfb);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::int8_t{0x32}));
expect(writer.tell() == 2);
expect(equal(writer.subspan(), std::span{expected.begin(), 2}));
expect(!!writer.write<fmt>(std::int8_t{-5}));
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::int16>"_test = [] {
using fmt = format::int16;
using error = msgpack::error;
std::array<std::byte, 6> payload;
auto constexpr expected =
make_bytes(0xd1, 0x32, 0xcc, 0xd1, 0xff, 0xfa);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::int16_t{0x32cc}));
expect(writer.tell() == 3);
expect(equal(writer.subspan(), std::span{expected.begin(), 3}));
expect(!!writer.write<fmt>(-6));
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::int32>"_test = [] {
using fmt = format::int32;
using error = msgpack::error;
std::array<std::byte, 5> payload;
auto constexpr expected = make_bytes(0xd2, 0x01, 0x02, 0x03, 0x04);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(0x01020304));
expect(writer.tell() == 5);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::int64>"_test = [] {
using fmt = format::int64;
using error = msgpack::error;
std::array<std::byte, 9> payload;
auto constexpr expected = make_bytes(
0xd3, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(std::int64_t{0x0102030405060708}));
expect(writer.tell() == 9);
expect(equal(writer.subspan(), expected));
expect(writer.write<fmt>(0x01)
== tl::make_unexpected(error::out_of_space));
};
"writer::write<format::fixstr>"_test = [] {
using fmt = format::fixstr;
std::array<std::byte, 4> payload;
auto constexpr expected = make_bytes(0xa2, 'o', 'h');
msgpack::writer writer(payload);
expect(!!writer.write<fmt>("oh"));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::str8>"_test = [] {
using fmt = format::str8;
std::array<std::byte, 46> payload;
auto constexpr expected = make_bytes(0xd9, 44, 't', 'h', 'e', ' ', 'q',
'u', 'i', 'c', 'k', ' ', 'b', 'r', 'o', 'w', 'n', ' ', 'f', 'o',
'x', ' ', 'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v', 'e', 'r',
' ', 't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o',
'g');
msgpack::writer writer(payload);
std::string_view txt = "the quick brown fox jumped over the lazy dog";
expect(!!writer.write<fmt>(std::move(txt)));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::str16>"_test = [] {
using fmt = format::str16;
std::array<std::byte, 47> payload;
auto constexpr expected = make_bytes(0xda, 0, 44, 't', 'h', 'e', ' ',
'q', 'u', 'i', 'c', 'k', ' ', 'b', 'r', 'o', 'w', 'n', ' ', 'f',
'o', 'x', ' ', 'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v', 'e',
'r', ' ', 't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o',
'g');
msgpack::writer writer(payload);
std::string_view txt = "the quick brown fox jumped over the lazy dog";
expect(!!writer.write<fmt>(std::move(txt)));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::str32>"_test = [] {
using fmt = format::str32;
std::array<std::byte, 49> payload;
auto constexpr expected = make_bytes(0xdb, 0, 0, 0, 44, 't', 'h', 'e',
' ', 'q', 'u', 'i', 'c', 'k', ' ', 'b', 'r', 'o', 'w', 'n', ' ',
'f', 'o', 'x', ' ', 'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v',
'e', 'r', ' ', 't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd',
'o', 'g');
msgpack::writer writer(payload);
std::string_view txt = "the quick brown fox jumped over the lazy dog";
expect(!!writer.write<fmt>(std::move(txt)));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::bin8>"_test = [] {
using fmt = format::bin8;
std::array<std::byte, 12> payload;
auto constexpr expected = make_bytes(
0xc4, 0x07, 0x01, 0x02, 0x03, 0x04, 0xf8, 0xf9, 0xfa);
msgpack::writer writer(payload);
std::span<std::byte const> bv(expected.begin() + 2, expected.end());
expect(!!writer.write<fmt>(std::move(bv)));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::bin16>"_test = [] {
using fmt = format::bin16;
std::array<std::byte, 12> payload;
auto constexpr expected = make_bytes(
0xc5, 0x0, 0x07, 0x01, 0x02, 0x03, 0x04, 0xf8, 0xf9, 0xfa);
msgpack::writer writer(payload);
std::span<std::byte const> bv(expected.begin() + 3, expected.end());
expect(!!writer.write<fmt>(std::move(bv)));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::bin32>"_test = [] {
using fmt = format::bin32;
std::array<std::byte, 12> payload;
auto constexpr expected = make_bytes(0xc6, 0x0, 0x0, 0x0, 0x07, 0x01,
0x02, 0x03, 0x04, 0xf8, 0xf9, 0xfa);
msgpack::writer writer(payload);
std::span<std::byte const> bv(expected.begin() + 5, expected.end());
expect(!!writer.write<fmt>(std::move(bv)));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::fixmap>"_test = [] {
using fmt = format::fixmap;
std::array<std::byte, 1> payload;
auto constexpr expected = make_bytes(0x83);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(msgpack::map_desc{3}));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::map16>"_test = [] {
using fmt = format::map16;
std::array<std::byte, 3> payload;
auto constexpr expected = make_bytes(0xde, 0x01, 0x00);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(msgpack::map_desc{256}));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::map32>"_test = [] {
using fmt = format::map32;
std::array<std::byte, 5> payload;
auto constexpr expected = make_bytes(0xdf, 0x00, 0x01, 0x00, 0x00);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(msgpack::map_desc{0x10000}));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::fixarray>"_test = [] {
using fmt = format::fixarray;
std::array<std::byte, 1> payload;
auto constexpr expected = make_bytes(0x93);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(msgpack::array_desc{3}));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::array16>"_test = [] {
using fmt = format::array16;
std::array<std::byte, 3> payload;
auto constexpr expected = make_bytes(0xdc, 0x01, 0x00);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(msgpack::array_desc{256}));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::array32>"_test = [] {
using fmt = format::array32;
std::array<std::byte, 5> payload;
auto constexpr expected = make_bytes(0xdd, 0x00, 0x01, 0x00, 0x00);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(msgpack::array_desc{0x10000}));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::nil>"_test = [] {
using fmt = format::nil;
std::array<std::byte, 1> payload;
auto constexpr expected = make_bytes(0xc0);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>({}));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::invalid>"_test = [] {
using fmt = format::invalid;
std::array<std::byte, 1> payload;
auto constexpr expected = make_bytes(0xc1);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>({}));
expect(equal(writer.subspan(), expected));
};
"writer::write<format::boolean>"_test = [] {
using fmt = format::boolean;
std::array<std::byte, 2> payload;
auto constexpr expected = make_bytes(0xc2, 0xc3);
msgpack::writer writer(payload);
expect(!!writer.write<fmt>(false));
expect(!!writer.write<fmt>(true));
expect(equal(writer.subspan(), expected));
};
};
// Deduced writer tests
namespace {
template <typename T>
struct test_data {};
template <std::signed_integral T>
struct test_data<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<T>::max()
&& value >= std::numeric_limits<T>::lowest();
}
};
template <std::unsigned_integral T>
struct test_data<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<T>::max();
}
};
template <>
struct test_data<std::string_view> {
static constexpr auto values = std::to_array<std::string_view>({
"", // 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(auto value) noexcept {
return value.size() <= std::numeric_limits<std::uint32_t>::max();
}
};
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::writer writer(payload);
for (auto const& value : test_data<T>::values) {
if (!test_data<T>::valid(value)) break;
expect(!!writer.write2(T(value)));
auto expect = std::span(expected_payload.begin(), writer.tell());
auto correct = equal(writer.subspan(), expect);
if (!correct) {
fmt::print("Deduction failed for '{}'\n", T(value));
fmt::print("\tActual: {::#04x}\n", writer.subspan());
fmt::print("\tExpect: {::#04x}\n", expect);
return false;
}
}
return true;
}
} // anonymous namespace
suite deduced_writer = [] {
"writer::write<std::uint8_t> (deduced + compressed)"_test = [] {
expect(test_deduced<std::uint8_t>());
};
"writer::write<std::uint16_t> (deduced + compressed)"_test = [] {
expect(test_deduced<std::uint16_t>());
};
"writer::write<std::uint32_t> (deduced + compressed)"_test = [] {
expect(test_deduced<std::uint32_t>());
};
"writer::write<std::uint64_t> (deduced + compressed)"_test = [] {
expect(test_deduced<std::uint64_t>());
};
"writer::write<std::int8_t> (deduced + compressed)"_test = [] {
expect(test_deduced<std::int8_t>());
};
"writer::write<std::int16_t> (deduced + compressed)"_test = [] {
expect(test_deduced<std::int16_t>());
};
"writer::write<std::int32_t> (deduced + compressed)"_test = [] {
expect(test_deduced<std::int32_t>());
};
"writer::write<std::int64_t> (deduced + compressed)"_test = [] {
expect(test_deduced<std::int32_t>());
};
"writer::write<std::string_view> (deduced + compressed)"_test = [] {
expect(test_deduced<std::string_view>());
};
};