From 7227cfc68a3366d113ae2a7e99ae015fedb5db97 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Tue, 16 Aug 2022 14:32:30 -0700 Subject: [PATCH] Add post-move tests to verify the container is valid after a move --- test/unordered/move_tests.cpp | 504 ++++++++++++++++++++++++++++++++++ 1 file changed, 504 insertions(+) diff --git a/test/unordered/move_tests.cpp b/test/unordered/move_tests.cpp index 82ba6011..7eadc220 100644 --- a/test/unordered/move_tests.cpp +++ b/test/unordered/move_tests.cpp @@ -18,6 +18,9 @@ #include "../helpers/equivalent.hpp" #include "../helpers/invariants.hpp" +#include +#include + #if defined(BOOST_MSVC) #pragma warning(disable : 4127) // conditional expression is constant #endif @@ -298,6 +301,501 @@ namespace move_tests { } } + template T const& get_key(T const& t) { return t; } + + template K const& get_key(std::pair const& kv) + { + return kv.first; + } + + template T const& get_value(T const& t) { return t; } + + template K const& get_value(std::pair const& kv) + { + return kv.second; + } + + template + static void insert_range(T& y, test::random_values const& v) + { + y.insert(v.begin(), v.end()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void insert_single(T& y, test::random_values const& v) + { + y.insert(*v.begin()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void insert_single_hint(T& y, test::random_values const& v) + { + y.insert(y.end(), *v.begin()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template struct insert_or_assign_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct insert_or_assign_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + + y.insert_or_assign(get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void insert_or_assign(T& y, test::random_values const& v) + { + insert_or_assign_invoker()(y, v); + } + + template struct insert_or_assign_hint_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct insert_or_assign_hint_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y.insert_or_assign(y.end(), get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void insert_or_assign_hint(T& y, test::random_values const& v) + { + insert_or_assign_hint_invoker()(y, v); + } + + template struct try_emplace_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct try_emplace_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y.try_emplace(get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void try_emplace(T& y, test::random_values const& v) + { + try_emplace_invoker()(y, v); + } + + template struct try_emplace_hint_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct try_emplace_hint_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y.try_emplace(y.end(), get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void try_emplace_hint(T& y, test::random_values const& v) + { + try_emplace_hint_invoker()(y, v); + } + + template struct at_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct at_invoker > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + BOOST_TRY { y.at(get_key(*v.begin())); } + BOOST_CATCH(...) {} + BOOST_CATCH_END + } + }; + + template static void at(T& y, test::random_values const& v) + { + at_invoker()(y, v); + } + + template struct index_operator_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct index_operator_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y[get_key(*v.begin())] = get_value(*v.begin()); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void index_operator(T& y, test::random_values const& v) + { + index_operator_invoker()(y, v); + } + + template static void clear(T& y, test::random_values const&) + { + y.clear(); + BOOST_TEST(y.empty()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void capacity(T& y, test::random_values const&) + { + (void)y.empty(); + (void)y.size(); + (void)y.max_size(); + (void)y.load_factor(); + (void)y.max_load_factor(); + (void)y.hash_function(); + (void)y.key_eq(); + (void)y.get_allocator(); + } + + template static void iterators(T& y, test::random_values const&) + { + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void erase_range(T& y, test::random_values const&) + { + y.erase(y.begin(), y.end()); + BOOST_TEST(y.empty()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void erase_key(T& y, test::random_values const& v) + { + y.erase(get_key(*v.begin())); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void lookup(T& y, test::random_values const& v) + { + (void)y.count(get_key(*v.begin())); + (void)y.find(get_key(*v.begin())); + (void)y.contains(get_key(*v.begin())); + (void)y.equal_range(get_key(*v.begin())); + } + + template static void reserve(T& y, test::random_values const&) + { + y.reserve(1337); + BOOST_TEST_GT(y.bucket_count(), 1337u); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void copy_assignment(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + y = x; + BOOST_TEST_EQ(y.size(), x.size()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void move_assignment(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + std::size_t const size = x.size(); + y = boost::move(x); + BOOST_TEST_GE(y.size(), size); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void equal(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + (void)(y == x); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void extract(T& y, test::random_values const& v) + { + (void)y.extract(get_key(*v.begin())); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void merge(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + if (y.get_allocator() == x.get_allocator()) { + y.merge(x); + } + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template bool pred(X const&) { return true; } + + template + static void erase_with_pred(T& y, test::random_values const&) + { + erase_if(y, pred); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void container_swap(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + if (boost::allocator_propagate_on_container_swap< + typename T::allocator_type>::type::value || + x.get_allocator() == y.get_allocator()) { + y.swap(x); + } + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void buckets(T& y, test::random_values const& v) + { + (void)y.begin(0); + (void)y.end(0); + (void)y.bucket_count(); + (void)y.max_bucket_count(); + (void)y.bucket_size(0); + (void)y.bucket(get_key(*v.begin())); + } + + template + static void double_move_construct(T& y, test::random_values const&) + { + T x = boost::move(y); + x.clear(); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + BOOST_TEST_EQ(x.size(), + static_cast(std::distance(x.begin(), x.end()))); + } + + template + static void double_move_assign(T& y, test::random_values const&) + { + T x; + x = boost::move(y); + x.clear(); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + BOOST_TEST_EQ(x.size(), + static_cast(std::distance(x.begin(), x.end()))); + } + + template + static void post_move_tests(T* ptr, test::random_generator const& generator) + { + // clang-format off + void (*fps[])(T&, test::random_values const&) = { + insert_range, + insert_single, + insert_single_hint, + insert_or_assign, + insert_or_assign_hint, + try_emplace, + try_emplace_hint, + at, + index_operator, + clear, + capacity, + iterators, + erase_range, + erase_key, + lookup, + reserve, + copy_assignment, + move_assignment, + equal, + extract, + merge, + erase_with_pred, + container_swap, + buckets, + double_move_construct, + double_move_assign + }; + // clang-format on + + std::size_t const len = (sizeof(fps) / sizeof(*(fps))); + + for (std::size_t i = 0; i < len; ++i) { + test::check_instances check_; + + test::random_values const v(1000, generator); + test::object_count count; + T y(create(v, count)); + T x(boost::move(y)); + +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); +#endif + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + for (std::size_t i = 0; i < len; ++i) { + typename T::hasher hf(1); + typename T::key_equal eq(1); + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); + + test::check_instances check_; + + test::random_values v(1000, generator); + test::object_count count; + T y(v.begin(), v.end(), 0, hf, eq, al1); + T x(boost::move(y), al2); + + BOOST_TEST_NOT(y.empty()); + BOOST_TEST(y.begin() != y.end()); + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + for (std::size_t i = 0; i < len; ++i) { + test::check_instances check_; + + test::random_values v(1000, generator); + test::object_count count; + T y(create(v, count)); + T x(empty(ptr)); + x = boost::move(y); + +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); +#endif + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + for (std::size_t i = 0; i < len; ++i) { + typename T::hasher hf(1); + typename T::key_equal eq(1); + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); + + test::check_instances check_; + + test::random_values v(1000, generator); + test::object_count count; + T y(v.begin(), v.end(), 0, hf, eq, al1); + T x(al2); + x = boost::move(y); + + bool b = boost::allocator_propagate_on_container_move_assignment< + typename T::allocator_type>::type::value; + if (b) { +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); +#else + BOOST_TEST_NOT(y.empty()); + BOOST_TEST(y.begin() != y.end()); + +#endif + } else { + BOOST_TEST_NOT(y.empty()); + BOOST_TEST(y.begin() != y.end()); + } + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + } + boost::unordered_map >* test_map_std_alloc; @@ -367,6 +865,12 @@ namespace move_tests { test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)( test_multimap_no_prop_move))( (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST(post_move_tests, + ((test_set)(test_multiset)(test_map)(test_multimap)(test_set_prop_move)( + test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)( + test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)( + test_multimap_no_prop_move))( + (default_generator)(generate_collisions)(limited_range))) } RUN_TESTS()