#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::value, 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::value, 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::value, 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; i<64; ++i) { // POD struct. PODs o_pods; PODs i_pods; // Outputstream std::ostringstream os; tf::Serializer oar(os); auto o_sz = oar(o_pods); //REQUIRE(o_sz == os.out_avail()); // Inputstream std::istringstream is(os.str()); tf::Deserializer iar(is); auto i_sz = iar(i_pods); REQUIRE(is.rdbuf()->in_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; i<4096; i++) { // Outputstream std::ostringstream os; tf::Serializer oar(os); T o_char_str = random(); auto o_sz = oar(o_char_str); //REQUIRE(o_sz == os.out_avail()); // Inputstream std::istringstream is(os.str()); tf::Deserializer iar(is); T i_char_str; auto i_sz = iar(i_char_str); REQUIRE(is.rdbuf()->in_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; i<64; i++) { \ const size_t num_data = random(1, 1024); \ std::ostringstream os; \ tf::Serializer oar(os); \ \ std::container o_int32s (num_data); \ std::container o_int64s (num_data); \ std::container o_chars (num_data); \ std::container o_floats (num_data); \ std::container o_doubles (num_data); \ std::container o_strings (num_data); \ std::container o_podses (num_data); \ \ for(auto& v : o_int32s) v = random(); \ for(auto& v : o_int64s) v = random(); \ for(auto& v : o_chars) v = random(); \ for(auto& v : o_floats) v = random(); \ for(auto& v : o_doubles) v = random(); \ for(auto& v : o_strings) v = random(); \ \ auto o_sz = oar(o_int32s, o_int64s, o_chars, o_floats, o_doubles, o_strings, o_podses);\ \ 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; \ std::container i_podses; \ \ auto i_sz = iar(i_int32s, i_int64s, i_chars, i_floats, i_doubles, i_strings, i_podses);\ \ 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); \ REQUIRE(o_podses == i_podses); \ } #define TEST_MAP_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(), random()); \ o_int64s.emplace(random(), random()); \ o_chars.emplace(random(), random()); \ o_floats.emplace(random(), random()); \ o_doubles.emplace(random(), random()); \ o_strings.emplace(random(), 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); \ } #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; i<64; ++i) { // Output std::array ochar; std::array oint; std::array odouble; std::array ostring; for(auto &i : ochar) i = random(); for(auto &i : oint) i = random(); for(auto &i : odouble) i = random(); for(auto &i : ostring) i = random(); std::ostringstream os; tf::Serializer oar(os); auto osz = oar(ochar, oint, odouble, ostring); // Input std::array ichar; std::array iint; std::array idouble; std::array istring; std::istringstream is(os.str()); tf::Deserializer iar(is); auto isz = iar(ichar, iint, idouble, istring); REQUIRE(0 == is.rdbuf()->in_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; i<64; ++i) { auto o_tpt1 = std::chrono::system_clock::now(); auto o_tpt2 = std::chrono::steady_clock::now(); auto o_tpt3 = std::chrono::high_resolution_clock::now(); auto o_dur1 = std::chrono::system_clock::now() - o_tpt1; auto o_dur2 = std::chrono::steady_clock::now() - o_tpt2; auto o_dur3 = std::chrono::high_resolution_clock::now() - o_tpt3; // Output archiver std::ostringstream os; tf::Serializer oar(os); auto osz = oar(o_tpt1, o_tpt2, o_tpt3, o_dur1, o_dur2, o_dur3); decltype(o_tpt1) i_tpt1; decltype(o_tpt2) i_tpt2; decltype(o_tpt3) i_tpt3; decltype(o_dur1) i_dur1; decltype(o_dur2) i_dur2; decltype(o_dur3) i_dur3; // Input archiver std::istringstream is(os.str()); tf::Deserializer iar(is); auto isz = iar(i_tpt1, i_tpt2, i_tpt3, i_dur1, i_dur2, i_dur3); REQUIRE(0 == is.rdbuf()->in_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; i<64; ++i) { std::optional o_nbool, i_nbool{true}; std::optional o_ybool{true}, i_ybool; std::optional o_nstr, i_nstr{random()}; std::optional o_ystr{random()}, i_ystr; // Output archiver std::ostringstream os; tf::Serializer oar(os); auto osz = oar(o_nbool, o_ybool, o_nstr, o_ystr); // Input archiver std::istringstream is(os.str()); tf::Deserializer iar(is); auto isz = iar(i_nbool, i_ybool, i_nstr, i_ystr); REQUIRE(0 == is.rdbuf()->in_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; i<64; ++i) { std::tuple<> o0, i0; std::tuple o1 {'a'}, i1 {'b'}; std::tuple o2 {1, 2.4}, i2 {3, 0.9}; std::tuple, float> o3{"123", {1, 2, 3}, 4.5f}, i3; std::tuple, int> o4 {1, {2, 3}, 4}, i4; // Output archiver std::ostringstream os; tf::Serializer oar(os); auto osz = oar(o0, o1, o2, o3, o4); // Input archiver std::istringstream is(os.str()); tf::Deserializer iar(is); auto isz = iar(i0, i1, i2, i3, i4); REQUIRE(0 == is.rdbuf()->in_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(); }