// Copyright 2008-2009 Daniel James. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or move at http://www.boost.org/LICENSE_1_0.txt) // clang-format off #include "../helpers/prefix.hpp" #include #include #include "../helpers/postfix.hpp" // clang-format on #include "../helpers/test.hpp" #include "../objects/test.hpp" #include "../objects/cxx11_allocator.hpp" #include "../helpers/random_values.hpp" #include "../helpers/tracker.hpp" #include "../helpers/equivalent.hpp" #include "../helpers/invariants.hpp" #include #include #if defined(BOOST_MSVC) #pragma warning(disable : 4127) // conditional expression is constant #endif namespace move_tests { test::seed_t initialize_seed(98624); #if defined(BOOST_UNORDERED_USE_MOVE) || \ !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #define BOOST_UNORDERED_TEST_MOVING 1 #else #define BOOST_UNORDERED_TEST_MOVING 0 #endif template T empty(T*) { return T(); } template T create(test::random_values const& v, test::object_count& count) { T x(v.begin(), v.end()); count = test::global_object_count; return x; } template T create(test::random_values const& v, test::object_count& count, typename T::hasher hf, typename T::key_equal eq, typename T::allocator_type al, float mlf) { T x(0, hf, eq, al); x.max_load_factor(mlf); x.insert(v.begin(), v.end()); count = test::global_object_count; return x; } template void move_construct_tests1(T* ptr, test::random_generator const& generator) { typename T::hasher hf; typename T::key_equal eq; typename T::allocator_type al; { test::check_instances check_; T y(empty(ptr)); BOOST_TEST(y.empty()); BOOST_TEST(test::equivalent(y.hash_function(), hf)); BOOST_TEST(test::equivalent(y.key_eq(), eq)); BOOST_TEST(test::equivalent(y.get_allocator(), al)); BOOST_TEST(y.max_load_factor() == 1.0); test::check_equivalent_keys(y); #if defined(BOOST_UNORDERED_USE_MOVE) || \ !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); #endif } { test::check_instances check_; test::random_values v(1000, generator); test::object_count count; T y(create(v, count)); #if defined(BOOST_HAS_NRVO) BOOST_TEST(count == test::global_object_count); #endif test::check_container(y, v); test::check_equivalent_keys(y); } } template void move_assign_tests1(T* p, test::random_generator const& generator) { { test::check_instances check_; test::random_values v(500, generator); test::object_count count; T y; y = create(v, count); #if BOOST_UNORDERED_TEST_MOVING && defined(BOOST_HAS_NRVO) BOOST_TEST(count == test::global_object_count); #endif test::check_container(y, v); test::check_equivalent_keys(y); } { test::random_values v; T y; y = empty(p); #if defined(BOOST_UNORDERED_USE_MOVE) || \ !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); #endif test::check_container(y, v); test::check_equivalent_keys(y); } } template void move_construct_tests2(T*, test::random_generator const& generator) { typename T::hasher hf(1); typename T::key_equal eq(1); typename T::allocator_type al(1); typename T::allocator_type al2(2); test::object_count count; { test::check_instances check_; test::random_values v(500, generator); T y(create(v, count, hf, eq, al, 0.5)); #if defined(BOOST_HAS_NRVO) BOOST_TEST(count == test::global_object_count); #endif test::check_container(y, v); BOOST_TEST(test::equivalent(y.hash_function(), hf)); BOOST_TEST(test::equivalent(y.key_eq(), eq)); BOOST_TEST(test::equivalent(y.get_allocator(), al)); BOOST_TEST(y.max_load_factor() == 0.5); // Not necessarily required. test::check_equivalent_keys(y); } { test::check_instances check_; // TODO: To do this correctly requires the fancy new allocator // stuff. test::random_values v(500, generator); T y(create(v, count, hf, eq, al, 2.0), al2); BOOST_TEST(count != test::global_object_count); test::check_container(y, v); BOOST_TEST(test::equivalent(y.hash_function(), hf)); BOOST_TEST(test::equivalent(y.key_eq(), eq)); BOOST_TEST(test::equivalent(y.get_allocator(), al2)); BOOST_TEST(y.max_load_factor() == 2.0); // Not necessarily required. test::check_equivalent_keys(y); } { test::check_instances check_; test::random_values v(25, generator); T y(create(v, count, hf, eq, al, 1.0), al); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) BOOST_TEST(count == test::global_object_count); #elif defined(BOOST_HAS_NRVO) BOOST_TEST( static_cast( test::global_object_count.constructions - count.constructions) <= (test::is_set::value ? 1 : 2) * (test::has_unique_keys::value ? 25 : v.size())); BOOST_TEST(count.instances == test::global_object_count.instances); #else BOOST_TEST( static_cast( test::global_object_count.constructions - count.constructions) <= (test::is_set::value ? 2 : 4) * (test::has_unique_keys::value ? 25 : v.size())); BOOST_TEST(count.instances == test::global_object_count.instances); #endif test::check_container(y, v); BOOST_TEST(test::equivalent(y.hash_function(), hf)); BOOST_TEST(test::equivalent(y.key_eq(), eq)); BOOST_TEST(test::equivalent(y.get_allocator(), al)); BOOST_TEST(y.max_load_factor() == 1.0); // Not necessarily required. test::check_equivalent_keys(y); } } template void move_assign_tests2(T*, test::random_generator const& generator) { typename T::hasher hf(1); typename T::key_equal eq(1); typename T::allocator_type al1(1); typename T::allocator_type al2(2); typedef typename T::allocator_type allocator_type; { test::random_values v(500, generator); test::random_values v2(0, generator); T y(v.begin(), v.end(), 0, hf, eq, al1); test::object_count count; y = create(v2, count, hf, eq, al2, 2.0); BOOST_TEST(y.empty()); test::check_container(y, v2); test::check_equivalent_keys(y); BOOST_TEST(y.max_load_factor() == 2.0); #if defined(BOOST_HAS_NRVO) if (BOOST_UNORDERED_TEST_MOVING ? (bool)allocator_type::is_propagate_on_move : (bool)allocator_type::is_propagate_on_assign) { BOOST_TEST(test::equivalent(y.get_allocator(), al2)); } else { BOOST_TEST(test::equivalent(y.get_allocator(), al1)); } #endif } { test::random_values v(500, generator); test::object_count count; T y(0, hf, eq, al1); y = create(v, count, hf, eq, al2, 0.5); #if defined(BOOST_HAS_NRVO) if (BOOST_UNORDERED_TEST_MOVING && allocator_type::is_propagate_on_move) { BOOST_TEST(count == test::global_object_count); } #endif test::check_container(y, v); test::check_equivalent_keys(y); BOOST_TEST(y.max_load_factor() == 0.5); #if defined(BOOST_HAS_NRVO) if (BOOST_UNORDERED_TEST_MOVING ? (bool)allocator_type::is_propagate_on_move : (bool)allocator_type::is_propagate_on_assign) { BOOST_TEST(test::equivalent(y.get_allocator(), al2)); } else { BOOST_TEST(test::equivalent(y.get_allocator(), al1)); } #endif } { test::random_values v; T y(0, hf, eq, al1); T x(0, hf, eq, al2); x.max_load_factor(0.25); BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); y = boost::move(x); #if defined(BOOST_UNORDERED_USE_MOVE) || \ !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); #endif test::check_container(y, v); test::check_equivalent_keys(y); BOOST_TEST(y.max_load_factor() == 0.25); if (BOOST_UNORDERED_TEST_MOVING ? (bool)allocator_type::is_propagate_on_move : (bool)allocator_type::is_propagate_on_assign) { BOOST_TEST(test::equivalent(y.get_allocator(), al2)); } else { BOOST_TEST(test::equivalent(y.get_allocator(), al1)); } } { test::check_instances check_; test::random_values v(500, generator); T y(0, hf, eq, al1); T x(0, hf, eq, al2); x.max_load_factor(0.25); x.insert(v.begin(), v.end()); test::object_count count = test::global_object_count; y = boost::move(x); if (BOOST_UNORDERED_TEST_MOVING && allocator_type::is_propagate_on_move) { BOOST_TEST(count == test::global_object_count); } test::check_container(y, v); test::check_equivalent_keys(y); BOOST_TEST(y.max_load_factor() == 0.25); if (BOOST_UNORDERED_TEST_MOVING ? (bool)allocator_type::is_propagate_on_move : (bool)allocator_type::is_propagate_on_assign) { BOOST_TEST(test::equivalent(y.get_allocator(), al2)); } else { BOOST_TEST(test::equivalent(y.get_allocator(), al1)); } } { test::check_instances check_; test::random_values v1(1000, generator); test::random_values v2(200, generator); T x(0, hf, eq, al2); x.max_load_factor(0.5); x.insert(v2.begin(), v2.end()); test::object_count count1 = test::global_object_count; T y(v1.begin(), v1.end(), 0, hf, eq, al1); y = boost::move(x); test::object_count count2 = test::global_object_count; if (BOOST_UNORDERED_TEST_MOVING && allocator_type::is_propagate_on_move) { BOOST_TEST(count1.instances == test::global_object_count.instances); BOOST_TEST( count2.constructions == test::global_object_count.constructions); } test::check_container(y, v2); test::check_equivalent_keys(y); BOOST_TEST(y.max_load_factor() == 0.5); if (BOOST_UNORDERED_TEST_MOVING ? (bool)allocator_type::is_propagate_on_move : (bool)allocator_type::is_propagate_on_assign) { BOOST_TEST(test::equivalent(y.get_allocator(), al2)); } else { BOOST_TEST(test::equivalent(y.get_allocator(), al1)); } } } 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)); unsigned num_allocs = test::detail::tracker.count_allocations; (void)num_allocs; 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()); BOOST_TEST_EQ(y.bucket_count(), 0u); BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); #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)); unsigned num_allocs = test::detail::tracker.count_allocations; (void)num_allocs; 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()); BOOST_TEST_EQ(y.bucket_count(), 0u); BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); #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); unsigned num_allocs = test::detail::tracker.count_allocations; (void)num_allocs; 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()); BOOST_TEST_EQ(y.bucket_count(), 0u); BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); #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; boost::unordered_set >* test_set; boost::unordered_multiset >* test_multiset; boost::unordered_map > >* test_map; boost::unordered_multimap > >* test_multimap; boost::unordered_set >* test_set_prop_move; boost::unordered_multiset >* test_multiset_prop_move; boost::unordered_map, test::propagate_move> >* test_map_prop_move; boost::unordered_multimap, test::propagate_move> >* test_multimap_prop_move; boost::unordered_set >* test_set_no_prop_move; boost::unordered_multiset >* test_multiset_no_prop_move; boost::unordered_map, test::no_propagate_move> >* test_map_no_prop_move; boost::unordered_multimap, test::no_propagate_move> >* test_multimap_no_prop_move; using test::default_generator; using test::generate_collisions; using test::limited_range; UNORDERED_TEST(move_construct_tests1, ((test_map_std_alloc)(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))) UNORDERED_TEST(move_assign_tests1, ((test_map_std_alloc)(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))) UNORDERED_TEST(move_construct_tests2, ((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))) UNORDERED_TEST(move_assign_tests2, ((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))) 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()