#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include
#include
#include
// ----------------------------------------------------------------------------
// Random generator utilities
// ----------------------------------------------------------------------------
// Function: random_engine
inline std::default_random_engine& random_engine() {
thread_local std::default_random_engine gen{0};
return gen;
}
// Function: random
// Randomly generate a floating value in the given range.
template
std::enable_if_t random(
const T from = -1.0,
const T to = 1.0
) {
return std::uniform_real_distribution(from, to)(random_engine());
}
// Function: random
// Randomly generate an integer value.
template
std::enable_if_t random(
const T from = std::numeric_limits::lowest(),
const T to = std::numeric_limits::max()
) {
return std::uniform_int_distribution(from, to)(random_engine());
}
// Function: random
// Randomly generate a string.
template
std::enable_if_t random(
const std::string::value_type from = ' ',
const std::string::value_type to = '~',
const std::string::size_type len = 16
) {
std::string str(len, ' ');
for(auto& c : str) {
c = random(from, to) % 128;
}
return str;
}
// ----------------------------------------------------------------------------
// Struct: PODs
struct PODs {
unsigned pod_uint32 = random();
int pod_int32 = random();
unsigned long long pod_uint64 = random();
long long pod_int64 = random();
float_t pod_float = random();
double_t pod_double = random();
template
auto save( ArchiverT& ar ) const {
return ar(
pod_uint32,
pod_int32,
pod_uint64,
pod_int64,
pod_float,
pod_double
);
}
template
auto load( ArchiverT& ar ) {
return ar(
pod_uint32,
pod_int32,
pod_uint64,
pod_int64,
pod_float,
pod_double
);
}
bool operator == (const PODs& rhs) const {
return pod_uint32 == rhs.pod_uint32 &&
pod_int32 == rhs.pod_int32 &&
pod_uint64 == rhs.pod_uint64 &&
pod_int64 == rhs.pod_int64 &&
pod_float == rhs.pod_float &&
pod_double == rhs.pod_double;
}
bool operator != (const PODs& rhs) const {
return !(*this == rhs);
}
};
// Procedure: test_pod
// The templated procedure for testing POD. Caller must specify the output
// and input archiver type.
void test_pod() {
// Output stream.
std::ostringstream os;
tf::Serializer oar(os);
const auto o_uint32 = random();
const auto o_int32 = random();
const auto o_uint64 = random();
const auto o_int64 = random();
const auto o_float = random();
const auto o_double = random();
auto o_sz = oar(
o_uint32,
o_int32,
o_uint64,
o_int64,
o_float,
o_double
);
//REQUIRE(o_sz == os.out_avail());
// InputStreamBuffer
std::istringstream is(os.str());
tf::Deserializer iar(is);
auto i_uint32 = random();
auto i_int32 = random();
auto i_uint64 = random();
auto i_int64 = random();
auto i_float = random();
auto i_double = random();
auto i_sz = iar(
i_uint32,
i_int32,
i_uint64,
i_int64,
i_float,
i_double
);
REQUIRE(is.rdbuf()->in_avail() == 0);
REQUIRE(i_sz == o_sz);
REQUIRE(o_uint32 == i_uint32);
REQUIRE(o_int32 == i_int32);
REQUIRE(o_uint64 == i_uint64);
REQUIRE(o_int64 == i_int64);
REQUIRE(o_float == i_float);
REQUIRE(o_double == i_double);
}
// Procedure: test_struct
// The templated procedure for testing POD. Caller must specify the output
// and input archiver type.
void test_struct() {
for(size_t i=0; iin_avail() == 0);
REQUIRE(o_sz == i_sz);
REQUIRE(o_pods == i_pods);
}
}
// Procedure: test_string
// Template for testing basic strings. Caller must specify the output and input archiver type.
template
void test_string() {
for(size_t i=0; iin_avail() == 0);
REQUIRE(o_sz == i_sz);
REQUIRE(o_char_str == i_char_str);
}
}
#define TEST_SEQ_CONT_BODY(container) \
\
for(size_t i=0; iin_avail()); \
\
REQUIRE(o_sz == i_sz); \
REQUIRE(o_int32s == i_int32s); \
REQUIRE(o_int64s == i_int64s); \
REQUIRE(o_chars == i_chars); \
REQUIRE(o_floats == i_floats); \
REQUIRE(o_doubles == i_doubles); \
REQUIRE(o_strings == i_strings); \
}
#define TEST_SET_CONT_BODY(container) \
\
for (size_t i = 0; i < 64; i++) { \
const size_t num_data = random(1, 1024); \
std::ostringstream os; \
tf::Serializer oar(os); \
\
std::container o_int32s; \
std::container o_int64s; \
std::container o_chars; \
std::container o_floats; \
std::container o_doubles; \
std::container o_strings; \
\
for (size_t j = 0; j < num_data; j++) { \
o_int32s.emplace(random()); \
o_int64s.emplace(random()); \
o_chars.emplace(random()); \
o_floats.emplace(random()); \
o_doubles.emplace(random()); \
o_strings.emplace(random()); \
} \
auto o_sz = oar(o_int32s, o_int64s, o_chars, o_floats, o_doubles, o_strings); \
\
std::istringstream is(os.str()); \
tf::Deserializer iar(is); \
\
std::container i_int32s; \
std::container i_int64s; \
std::container i_chars; \
std::container i_floats; \
std::container i_doubles; \
std::container i_strings; \
\
auto i_sz = iar(i_int32s, i_int64s, i_chars, i_floats, i_doubles, i_strings); \
REQUIRE(0 == is.rdbuf()->in_avail()); \
\
REQUIRE(o_sz == i_sz); \
REQUIRE(o_int32s == i_int32s); \
REQUIRE(o_int64s == i_int64s); \
REQUIRE(o_chars == i_chars); \
REQUIRE(o_floats == i_floats); \
REQUIRE(o_doubles == i_doubles); \
REQUIRE(o_strings == i_strings); \
}
// Procedure: test_array
// Template procedure for testing array container.
void test_array() {
for(size_t i=0; iin_avail());
REQUIRE(osz == isz);
REQUIRE(ochar == ichar);
REQUIRE(oint == iint);
REQUIRE(odouble == idouble);
REQUIRE(ostring == istring);
}
}
// Procedure: test_variant
void test_variant() {
for (size_t i = 0; i < 64; i++) {
// Single POD variant.
std::variant opod1 = random();
std::variant ipod1 = random();
// Multiple POD variant
std::variant opod2 = random();
std::variant ipod2 = random();
// Multiple POD variant
std::variant opod3 = random()%2;
std::variant ipod3 = random();
// Mixing float and string
std::variant omix2 = random();
std::variant imix2 = random();
// Recursive variant
std::variant orec2 = omix2;
std::variant irec2 = random();
// Output archiver
std::ostringstream os;
tf::Serializer oar(os);
auto osz = oar(opod1, opod2, opod3, omix2, orec2);
// Input archiver
std::istringstream is(os.str());
tf::Deserializer iar(is);
auto isz = iar(ipod1, ipod2, ipod3, imix2, irec2);
REQUIRE(0 == is.rdbuf()->in_avail());
REQUIRE(osz == isz);
REQUIRE(opod1 == ipod1);
REQUIRE(opod2 == ipod2);
REQUIRE(opod3 == ipod3);
REQUIRE(omix2 == imix2);
REQUIRE(orec2 == irec2);
}
}
// Procedure: test_time_point
void test_time_point() {
for(auto i=0; iin_avail());
REQUIRE(osz == isz);
REQUIRE(o_tpt1 == i_tpt1);
REQUIRE(o_tpt2 == i_tpt2);
REQUIRE(o_tpt3 == i_tpt3);
REQUIRE(o_dur1 == i_dur1);
REQUIRE(o_dur2 == i_dur2);
REQUIRE(o_dur3 == i_dur3);
}
}
// Procedure: test_optional
void test_optional() {
for(auto i=0; iin_avail());
REQUIRE(osz == isz);
REQUIRE(o_nbool == i_nbool);
REQUIRE(o_ybool == i_ybool);
REQUIRE(o_nstr == i_nstr);
REQUIRE(o_ystr == i_ystr);
}
}
// Procedure: test_tuple
void test_tuple() {
for(auto i=0; iin_avail());
REQUIRE(osz == isz);
REQUIRE(o0 == i0);
REQUIRE(o1 == i1);
REQUIRE(o2 == i2);
REQUIRE(o3 == i3);
REQUIRE(o4 == i4);
}
}
// ----------------------------------------------------------------------------
// POD
TEST_CASE("POD" * doctest::timeout(300)) {
test_pod();
}
// POD-struct
TEST_CASE("POD-Struct" * doctest::timeout(300)) {
test_struct();
}
// std::string
TEST_CASE("string" * doctest::timeout(300)) {
test_string();
}
// std::vector
TEST_CASE("vector" * doctest::timeout(300)) {
TEST_SEQ_CONT_BODY(vector)
}
// std::deque
TEST_CASE("deque" * doctest::timeout(300)) {
TEST_SEQ_CONT_BODY(deque)
}
// std::list
TEST_CASE("list" * doctest::timeout(300)) {
TEST_SEQ_CONT_BODY(list)
}
// std::forward_list
TEST_CASE("forward_list" * doctest::timeout(300)) {
TEST_SEQ_CONT_BODY(forward_list)
}
// std::map
TEST_CASE("map" * doctest::timeout(300)) {
TEST_MAP_CONT_BODY(map);
}
// std::unordered_map
TEST_CASE("unordered_map" * doctest::timeout(300)) {
TEST_MAP_CONT_BODY(unordered_map);
}
// std::set
TEST_CASE("set" * doctest::timeout(300)) {
TEST_SET_CONT_BODY(set);
}
// std::unordered_set
TEST_CASE("unordered_set" * doctest::timeout(300)) {
TEST_SET_CONT_BODY(unordered_set);
}
// std::array
TEST_CASE("array" * doctest::timeout(300)) {
test_array();
}
// std::chrono::time_point
TEST_CASE("time_point" * doctest::timeout(300)) {
test_time_point();
}
// std::optional
TEST_CASE("optional" * doctest::timeout(300)) {
test_optional();
}
// std::tuple
TEST_CASE("tuple" * doctest::timeout(300)) {
test_tuple();
}