Rapidcheck component, initial test cases rewritten

This commit is contained in:
Kurt Sassenrath 2024-01-03 15:51:22 -08:00
parent 0cafb9bcd7
commit 8f4ac703f4
5 changed files with 380 additions and 1 deletions

65
BUILD.rapidcheck Normal file
View 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"]),
)

View File

@ -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
View File

View 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",

View 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