Rapidcheck component, initial test cases rewritten
This commit is contained in:
parent
0cafb9bcd7
commit
8f4ac703f4
65
BUILD.rapidcheck
Normal file
65
BUILD.rapidcheck
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
cc_library(
|
||||
name = "headers",
|
||||
includes = ["include"],
|
||||
visibility = ["//visibility:private"],
|
||||
copts = ["-DRC_DONT_USE_RTTI"],
|
||||
hdrs = glob([
|
||||
"include/**/*.hpp",
|
||||
"include/**/*.h",
|
||||
]),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "random",
|
||||
deps = [":headers"],
|
||||
visibility = ["//visibility:private"],
|
||||
copts = ["-DRC_DONT_USE_RTTI", "-O3"],
|
||||
srcs = [
|
||||
"src/Random.cpp",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
cc_library(
|
||||
name = "rapidcheck",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":headers", ":random"],
|
||||
copts = ["-DRC_DONT_USE_RTTI"],
|
||||
includes = ["include"],
|
||||
srcs = [
|
||||
"src/BeforeMinimalTestCase.cpp",
|
||||
"src/Check.cpp",
|
||||
"src/Classify.cpp",
|
||||
"src/GenerationFailure.cpp",
|
||||
"src/Log.cpp",
|
||||
"src/Show.cpp",
|
||||
"src/detail/Any.cpp",
|
||||
"src/detail/Assertions.cpp",
|
||||
"src/detail/Base64.cpp",
|
||||
"src/detail/Configuration.cpp",
|
||||
"src/detail/DefaultTestListener.cpp",
|
||||
"src/detail/FrequencyMap.cpp",
|
||||
"src/detail/ImplicitParam.cpp",
|
||||
"src/detail/LogTestListener.cpp",
|
||||
"src/detail/MapParser.cpp",
|
||||
"src/detail/MulticastTestListener.cpp",
|
||||
"src/detail/ParseException.cpp",
|
||||
"src/detail/Platform.cpp",
|
||||
"src/detail/Property.cpp",
|
||||
"src/detail/PropertyContext.cpp",
|
||||
"src/detail/ReproduceListener.cpp",
|
||||
"src/detail/Results.cpp",
|
||||
"src/detail/Serialization.cpp",
|
||||
"src/detail/StringSerialization.cpp",
|
||||
"src/detail/TestMetadata.cpp",
|
||||
"src/detail/TestParams.cpp",
|
||||
"src/detail/Testing.cpp",
|
||||
"src/gen/Numeric.cpp",
|
||||
"src/gen/Text.cpp",
|
||||
"src/gen/detail/ExecHandler.cpp",
|
||||
"src/gen/detail/GenerationHandler.cpp",
|
||||
"src/gen/detail/Recipe.cpp",
|
||||
"src/gen/detail/ScaleInteger.cpp",
|
||||
] + glob(["src/detail/*.h"]),
|
||||
)
|
||||
18
WORKSPACE
18
WORKSPACE
@ -101,7 +101,7 @@ cc_library(
|
||||
)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# ut: Unit test framework.
|
||||
# ut: Testing framework.
|
||||
# TODO(kss): Only if tests are needed?
|
||||
#-------------------------------------------------------------------------------
|
||||
ut_version = "1.1.9"
|
||||
@ -124,6 +124,22 @@ cc_library(
|
||||
strip_prefix = "ut-" + ut_version,
|
||||
)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# rapidcheck: Property-based-testing framework.
|
||||
# TODO(kss): Only if tests are needed?
|
||||
#-------------------------------------------------------------------------------
|
||||
rapidcheck_commit = "ff6af6fc683159deb51c543b065eba14dfcf329b"
|
||||
rapidcheck_base_url = "https://github.com/emil-e/rapidcheck"
|
||||
rapidcheck_branch = "master"
|
||||
|
||||
git_repository(
|
||||
name = "rapidcheck",
|
||||
remote = rapidcheck_base_url,
|
||||
commit = rapidcheck_commit,
|
||||
build_file = "@//:BUILD.rapidcheck"
|
||||
)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Support compile_commands.json generation for LSP.
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
0
bazel/BUILD
Normal file
0
bazel/BUILD
Normal file
@ -33,6 +33,15 @@ cc_test(
|
||||
deps = ["test_deps"],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "packer_rc",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"test_packer_rc.cpp",
|
||||
],
|
||||
deps = ["test_deps", "@rapidcheck"],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "writer",
|
||||
size = "small",
|
||||
|
||||
289
tests/msgpack/test_packer_rc.cpp
Normal file
289
tests/msgpack/test_packer_rc.cpp
Normal file
@ -0,0 +1,289 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// ___ __ _ _
|
||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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
|
||||
Loading…
Reference in New Issue
Block a user