diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 72c09867..25d1cde4 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -62,6 +62,7 @@ test-suite unordered [ run unordered/rehash_tests.cpp ] [ run unordered/equality_tests.cpp ] [ run unordered/swap_tests.cpp ] + [ run unordered/detail_tests.cpp ] [ run unordered/compile_set.cpp : : : BOOST_UNORDERED_USE_MOVE diff --git a/test/unordered/at_tests.cpp b/test/unordered/at_tests.cpp index 11c0aa75..a948aeff 100644 --- a/test/unordered/at_tests.cpp +++ b/test/unordered/at_tests.cpp @@ -19,6 +19,21 @@ UNORDERED_AUTO_TEST(at_tests) BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Create Map" << std::endl; boost::unordered_map x; + boost::unordered_map const& x_const(x); + + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Check empty container" << std::endl; + + try { + x.at("one"); + BOOST_ERROR("Should have thrown."); + } catch (std::out_of_range) { + } + + try { + x_const.at("one"); + BOOST_ERROR("Should have thrown."); + } catch (std::out_of_range) { + } BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Add elements" << std::endl; @@ -29,6 +44,8 @@ UNORDERED_AUTO_TEST(at_tests) BOOST_TEST(x.at("one") == 1); BOOST_TEST(x.at("two") == 2); + BOOST_TEST(x_const.at("one") == 1); + BOOST_TEST(x_const.at("two") == 2); BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Check missing element" << std::endl; @@ -38,6 +55,12 @@ UNORDERED_AUTO_TEST(at_tests) } catch (std::out_of_range) { } + try { + x_const.at("three"); + BOOST_ERROR("Should have thrown."); + } catch (std::out_of_range) { + } + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Finished" << std::endl; } } diff --git a/test/unordered/compile_tests.hpp b/test/unordered/compile_tests.hpp index 12759c16..af913614 100644 --- a/test/unordered/compile_tests.hpp +++ b/test/unordered/compile_tests.hpp @@ -339,10 +339,16 @@ void unordered_map_test(X& r, Key const& k, T const& v) // Calling functions r.insert(std::pair(k, v)); + r.insert(r.begin(), std::pair(k, v)); + std::pair const value(k, v); + r.insert(value); + r.insert(r.end(), value); Key k_lvalue(k); T v_lvalue(v); + // Emplace + r.emplace(k, v); r.emplace(k_lvalue, v_lvalue); r.emplace(rvalue(k), rvalue(v)); @@ -350,6 +356,17 @@ void unordered_map_test(X& r, Key const& k, T const& v) r.emplace(boost::unordered::piecewise_construct, boost::make_tuple(k), boost::make_tuple(v)); + // Emplace with hint + + r.emplace_hint(r.begin(), k, v); + r.emplace_hint(r.begin(), k_lvalue, v_lvalue); + r.emplace_hint(r.begin(), rvalue(k), rvalue(v)); + + r.emplace_hint(r.begin(), boost::unordered::piecewise_construct, + boost::make_tuple(k), boost::make_tuple(v)); + + // Extract + test::check_return_type::equals(r.extract(r.begin())); r.emplace(k, v); @@ -360,7 +377,11 @@ void unordered_map_test(X& r, Key const& k, T const& v) test::check_return_type::equals_ref(n1.key()); test::check_return_type::equals_ref(n1.mapped()); - r.insert(boost::move(n1)); + node_type n2 = boost::move(n1); + r.insert(boost::move(n2)); + r.insert(r.extract(r.begin())); + n2 = r.extract(r.begin()); + r.insert(r.begin(), boost::move(n2)); r.insert(r.end(), r.extract(r.begin())); node_type n = r.extract(r.begin()); @@ -666,6 +687,13 @@ void unordered_copyable_test(X& x, Key& k, T& t, Hash& hf, Pred& eq) sink(X(b, m)); X a9a(b, m); + X b1; + b1.insert(t); + X a9b(b1); + sink(a9b); + X a9c(b1, m); + sink(a9c); + const_iterator q = a.cbegin(); test::check_return_type::equals(a.insert(q, t)); diff --git a/test/unordered/detail_tests.cpp b/test/unordered/detail_tests.cpp new file mode 100644 index 00000000..b87bf943 --- /dev/null +++ b/test/unordered/detail_tests.cpp @@ -0,0 +1,104 @@ + +// Copyright 2017 Daniel James. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +// clang-format off +#include "../helpers/prefix.hpp" +#include +#include +#include "../helpers/postfix.hpp" +// clang-format on + +#include "../helpers/test.hpp" +#include + +// Pretty inefficient, but the test is fast enough. +// Might be too slow if we had larger primes? +bool is_prime(std::size_t x) +{ + if (x == 2) { + return true; + } else if (x == 1 || x % 2 == 0) { + return false; + } else { + // y*y <= x had rounding errors, so instead use y <= (x/y). + for (std::size_t y = 3; y <= (x / y); y += 2) { + if (x % y == 0) { + return false; + break; + } + } + + return true; + } +} + +void test_next_prime(std::size_t value) +{ + std::size_t x = boost::unordered::detail::next_prime(value); + BOOST_TEST(is_prime(x)); + BOOST_TEST(x >= value); +} + +void test_prev_prime(std::size_t value) +{ + std::size_t x = boost::unordered::detail::prev_prime(value); + BOOST_TEST(is_prime(x)); + BOOST_TEST(x <= value); + if (x > value) { + BOOST_LIGHTWEIGHT_TEST_OSTREAM << x << "," << value << std::endl; + } +} + +UNORDERED_AUTO_TEST(next_prime_test) +{ + BOOST_TEST(!is_prime(0)); + BOOST_TEST(!is_prime(1)); + BOOST_TEST(is_prime(2)); + BOOST_TEST(is_prime(3)); + BOOST_TEST(is_prime(13)); + BOOST_TEST(!is_prime(4)); + BOOST_TEST(!is_prime(100)); + + BOOST_TEST(boost::unordered::detail::next_prime(0) > 0); + + // test_prev_prime doesn't work for values less than 17. + // Which should be okay, unless an allocator has a really tiny + // max_size? + const std::size_t min_prime = 17; + + // test_next_prime doesn't work for values greater than this, + // which might be a problem if you've got terrabytes of memory? + // I seriously doubt the container would work well at such sizes + // regardless. + const std::size_t max_prime = 4294967291ul; + + std::size_t i; + + BOOST_TEST(is_prime(min_prime)); + BOOST_TEST(is_prime(max_prime)); + + for (i = 0; i < 10000; ++i) { + if (i < min_prime) { + BOOST_TEST(boost::unordered::detail::prev_prime(i) == min_prime); + } else { + test_prev_prime(i); + } + test_next_prime(i); + } + + std::size_t last = i - 1; + for (; i > last; last = i, i += i / 5) { + if (i > max_prime) { + BOOST_TEST(boost::unordered::detail::next_prime(i) == max_prime); + } else { + test_next_prime(i); + } + test_prev_prime(i); + } +} + +RUN_TESTS() diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index b5c2fffb..f55ca90e 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -35,64 +35,138 @@ void unique_insert_tests1(X*, test::random_generator generator) std::cerr << "insert(value) tests for containers with unique keys.\n"; - X x; - test::ordered tracker = test::create_ordered(x); + { + X x; + test::ordered tracker = test::create_ordered(x); - test::random_values v(1000, generator); + test::random_values v(1000, generator); - for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = v.begin(); - it != v.end(); ++it) { + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { - BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = x.bucket_count(); - float b = x.max_load_factor(); + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); - std::pair r1 = x.insert(*it); - std::pair r2 = - tracker.insert(*it); + std::pair r1 = x.insert(*it); + std::pair r2 = + tracker.insert(*it); - BOOST_TEST(r1.second == r2.second); - BOOST_TEST(*r1.first == *r2.first); + BOOST_TEST(r1.second == r2.second); + BOOST_TEST(*r1.first == *r2.first); - tracker.compare_key(x, *it); + tracker.compare_key(x, *it); - if (static_cast(x.size()) <= - b * static_cast(old_bucket_count)) - BOOST_TEST(x.bucket_count() == old_bucket_count); + if (static_cast(x.size()) <= + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); } - test::check_equivalent_keys(x); + std::cerr << "insert(rvalue) tests for containers with unique keys.\n"; + + { + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); + + typename X::value_type value = *it; + std::pair r1 = x.insert(boost::move(value)); + std::pair r2 = + tracker.insert(*it); + + BOOST_TEST(r1.second == r2.second); + BOOST_TEST(*r1.first == *r2.first); + + tracker.compare_key(x, *it); + + if (static_cast(x.size()) <= + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } } template void equivalent_insert_tests1(X*, test::random_generator generator) { - std::cerr << "insert(value) tests for containers with equivalent keys.\n"; - test::check_instances check_; - X x; - test::ordered tracker = test::create_ordered(x); + std::cerr << "insert(value) tests for containers with equivalent keys.\n"; - test::random_values v(1000, generator); - for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = v.begin(); - it != v.end(); ++it) { - BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = x.bucket_count(); - float b = x.max_load_factor(); + { + X x; + test::ordered tracker = test::create_ordered(x); - BOOST_DEDUCED_TYPENAME X::iterator r1 = x.insert(*it); - BOOST_DEDUCED_TYPENAME test::ordered::iterator r2 = - tracker.insert(*it); + test::random_values v(1000, generator); + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); - BOOST_TEST(*r1 == *r2); + BOOST_DEDUCED_TYPENAME X::iterator r1 = x.insert(*it); + BOOST_DEDUCED_TYPENAME test::ordered::iterator r2 = + tracker.insert(*it); - tracker.compare_key(x, *it); + BOOST_TEST(*r1 == *r2); - if (static_cast(x.size()) <= - b * static_cast(old_bucket_count)) - BOOST_TEST(x.bucket_count() == old_bucket_count); + tracker.compare_key(x, *it); + + if (static_cast(x.size()) <= + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); } - test::check_equivalent_keys(x); + std::cerr << "insert(rvalue) tests for containers with equivalent keys.\n"; + + { + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); + + typename X::value_type value = *it; + BOOST_DEDUCED_TYPENAME X::iterator r1 = + x.insert(boost::move(value)); + BOOST_DEDUCED_TYPENAME test::ordered::iterator r2 = + tracker.insert(*it); + + BOOST_TEST(*r1 == *r2); + + tracker.compare_key(x, *it); + + if (static_cast(x.size()) <= + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } } template void insert_tests2(X*, test::random_generator generator) @@ -194,6 +268,38 @@ template void insert_tests2(X*, test::random_generator generator) test::check_equivalent_keys(x); } + std::cerr << "insert(pos, rvalue) tests.\n"; + + { + test::check_instances check_; + + X x; + const_iterator pos = x.begin(); + tracker_type tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); + + typename X::value_type value = *it; + pos = x.insert(pos, boost::move(value)); + tracker_iterator r2 = tracker.insert(tracker.begin(), *it); + BOOST_TEST(*pos == *r2); + tracker.compare_key(x, *it); + + if (static_cast(x.size()) <= + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + tracker.compare(x); + test::check_equivalent_keys(x); + } + std::cerr << "insert single item range tests.\n"; {