From fbd37a946bab0c3ab8d644a3f2b537083c3016cf Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 21 May 2006 17:14:11 +0000 Subject: [PATCH] More tests for unordered associative containers. [SVN r2959] --- test/Jamfile.v2 | 1 + test/container/simple_tests.cpp | 111 ++++++ test/exception/Jamfile.v2 | 29 ++ test/exception/assign_tests.cpp | 79 ++++ test/exception/constructor_tests.cpp | 119 ++++++ test/exception/containers.hpp | 28 ++ test/exception/copy_tests.cpp | 51 +++ test/exception/erase_tests.cpp | 55 +++ test/exception/insert_tests.cpp | 168 +++++++++ test/exception/rehash_tests.cpp | 80 ++++ test/exception/swap_tests.cpp | 107 ++++++ test/helpers/equivalent.hpp | 28 +- test/helpers/invariants.hpp | 41 ++- test/helpers/metafunctions.hpp | 2 +- test/helpers/strong.hpp | 47 +++ test/objects/exception.hpp | 522 +++++++++++++++++++++++++++ test/objects/fwd.hpp | 4 +- test/objects/test.hpp | 38 +- test/unordered/Jamfile.v2 | 6 + test/unordered/assign_tests.cpp | 17 +- test/unordered/bucket_tests.cpp | 60 +++ test/unordered/constructor_tests.cpp | 41 ++- test/unordered/copy_tests.cpp | 13 +- test/unordered/erase_tests.cpp | 4 +- test/unordered/insert_tests.cpp | 136 +++++-- test/unordered/load_factor_tests.cpp | 72 ++++ test/unordered/rehash_tests.cpp | 67 ++++ test/unordered/swap_tests.cpp | 135 +++++++ 28 files changed, 1975 insertions(+), 86 deletions(-) create mode 100644 test/container/simple_tests.cpp create mode 100644 test/exception/Jamfile.v2 create mode 100644 test/exception/assign_tests.cpp create mode 100644 test/exception/constructor_tests.cpp create mode 100644 test/exception/containers.hpp create mode 100644 test/exception/copy_tests.cpp create mode 100644 test/exception/erase_tests.cpp create mode 100644 test/exception/insert_tests.cpp create mode 100644 test/exception/rehash_tests.cpp create mode 100644 test/exception/swap_tests.cpp create mode 100644 test/helpers/strong.hpp create mode 100644 test/objects/exception.hpp create mode 100644 test/unordered/bucket_tests.cpp create mode 100644 test/unordered/load_factor_tests.cpp create mode 100644 test/unordered/rehash_tests.cpp create mode 100644 test/unordered/swap_tests.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 122f58d6..e4fcf07d 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -5,3 +5,4 @@ build-project container ; build-project unordered ; +build-project exception ; diff --git a/test/container/simple_tests.cpp b/test/container/simple_tests.cpp new file mode 100644 index 00000000..94c9f79a --- /dev/null +++ b/test/container/simple_tests.cpp @@ -0,0 +1,111 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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) + +// This test checks the runtime requirements of containers. + +#include +#include +#include +#include +#include "../helpers/equivalent.hpp" + +template +void simple_test(X const& a) +{ + test::unordered_equivalence_tester equivalent(a); + + { + X u; + BOOST_TEST(u.size() == 0); + BOOST_TEST(X().size() == 0); + } + + { + BOOST_TEST(equivalent(X(a))); + } + + { + X u(a); + BOOST_TEST(equivalent(u)); + } + + { + X u = a; + BOOST_TEST(equivalent(u)); + } + + { + X b(a); + BOOST_TEST(b.begin() == const_cast(b).cbegin()); + BOOST_TEST(b.end() == const_cast(b).cend()); + } + + { + // TODO: Also test with a random container... + X b(a); + X c; + BOOST_TEST(equivalent(b)); + BOOST_TEST(c.empty()); + b.swap(c); + BOOST_TEST(b.empty()); + BOOST_TEST(equivalent(c)); + b.swap(c); + BOOST_TEST(b.empty()); + BOOST_TEST(equivalent(c)); + } + + { + X u; + X& r(u); + // TODO: I can't actually see a requirement for that assignment + // returns a reference to itself (just that it returns a reference). + BOOST_TEST(&(r = r) == &r); + BOOST_TEST(r.empty()); + BOOST_TEST(&(r = a) == &r); + BOOST_TEST(equivalent(r)); + BOOST_TEST(&(r = r) == &r); + BOOST_TEST(equivalent(r)); + } + + { + BOOST_TEST(a.size() == + (typename X::size_type) std::distance(a.begin(), a.end())); + } + + // TODO: Test max_size against allocator? + + { + BOOST_TEST(a.empty() == (a.size() == 0)); + } + + { + BOOST_TEST(a.empty() == (a.begin() == a.end())); + X u; + BOOST_TEST(u.begin() == u.end()); + } + + // TODO: Test construction with allocator? +} + +int main() +{ + std::cout<<"Test unordered_set.\n"; + boost::unordered_set set; + simple_test(set); + + std::cout<<"Test unordered_multiset.\n"; + boost::unordered_multiset multiset; + simple_test(multiset); + + std::cout<<"Test unordered_map.\n"; + boost::unordered_map map; + simple_test(map); + + std::cout<<"Test unordered_multimap.\n"; + boost::unordered_multimap multimap; + simple_test(multimap); + + return boost::report_errors(); +} diff --git a/test/exception/Jamfile.v2 b/test/exception/Jamfile.v2 new file mode 100644 index 00000000..158eded5 --- /dev/null +++ b/test/exception/Jamfile.v2 @@ -0,0 +1,29 @@ + +# Copyright Daniel James 2006. Use, modification, and distribution are +# subject to 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) + +import testing ; + +alias framework : /boost/test//boost_unit_test_framework/speed ; + +project unordered-test/exception-tests + : requirements + intel-linux:"-strict_ansi -cxxlib-icc" + ; + +test-suite unordered-tests + : + [ run constructor_tests.cpp framework ] + [ run copy_tests.cpp framework ] + [ run assign_tests.cpp framework ] + [ run insert_tests.cpp framework ] + [ run erase_tests.cpp framework ] + [ run rehash_tests.cpp framework ] + [ run swap_tests.cpp framework : : : + BOOST_UNORDERED_SWAP_METHOD=1 : swap_tests1 ] + [ run swap_tests.cpp framework : : : + BOOST_UNORDERED_SWAP_METHOD=2 : swap_tests2 ] + [ run swap_tests.cpp framework : : : + BOOST_UNORDERED_SWAP_METHOD=3 : swap_tests3 ] + ; diff --git a/test/exception/assign_tests.cpp b/test/exception/assign_tests.cpp new file mode 100644 index 00000000..914b9ef3 --- /dev/null +++ b/test/exception/assign_tests.cpp @@ -0,0 +1,79 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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 "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" + +template +struct self_assign_base : public test::exception_base +{ + test::random_values values; + self_assign_base(int count = 0) : values(count) {} + + typedef T data_type; + T init() const { return T(values.begin(), values.end()); } + void run(T& x) const { x = x; } + void check(T const& x) const { test::check_equivalent_keys(x); } +}; + +template +struct self_assign_test1 : self_assign_base {}; + +template +struct self_assign_test2 : self_assign_base +{ + self_assign_test2() : self_assign_base(100) {} +}; + +template +struct assign_base : public test::exception_base +{ + const test::random_values x_values, y_values; + const T x,y; + + assign_base(unsigned int count1, unsigned int count2, int tag1, int tag2) + : x_values(count1), y_values(count2), + x(x_values.begin(), x_values.end(), 0, typename T::hasher(tag1), typename T::key_equal(tag1), typename T::allocator_type(tag1)), + y(y_values.begin(), y_values.end(), 0, typename T::hasher(tag2), typename T::key_equal(tag2), typename T::allocator_type(tag2)) {} + + typedef T data_type; + T init() const { return T(x); } + void run(T& x1) const { x1 = y; } + void check(T const& x1) const { test::check_equivalent_keys(x1); } +}; + +template +struct assign_test1 : assign_base +{ + assign_test1() : assign_base(0, 0, 0, 0) {} +}; + +template +struct assign_test2 : assign_base +{ + assign_test2() : assign_base(60, 0, 0, 0) {} +}; + +template +struct assign_test3 : assign_base +{ + assign_test3() : assign_base(0, 60, 0, 0) {} +}; + +template +struct assign_test4 : assign_base +{ + assign_test4() : assign_base(10, 10, 1, 2) {} +}; + +RUN_EXCEPTION_TESTS( + (self_assign_test1)(self_assign_test2) + (assign_test1)(assign_test2)(assign_test3)(assign_test4), + CONTAINER_SEQ) diff --git a/test/exception/constructor_tests.cpp b/test/exception/constructor_tests.cpp new file mode 100644 index 00000000..ac9206b6 --- /dev/null +++ b/test/exception/constructor_tests.cpp @@ -0,0 +1,119 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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 "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" + +struct objects +{ + test::exception::object obj; + test::exception::hash hash; + test::exception::equal_to equal_to; + test::exception::allocator allocator; +}; + +template +struct construct_test1 : public objects, test::exception_base +{ + void run() const { + T x; + } +}; + +template +struct construct_test2 : public objects, test::exception_base +{ + void run() const { + T x(300); + } +}; + +template +struct construct_test3 : public objects, test::exception_base +{ + void run() const { + T x(0, hash); + } +}; + +template +struct construct_test4 : public objects, test::exception_base +{ + void run() const { + T x(0, hash, equal_to); + } +}; + +template +struct construct_test5 : public objects, test::exception_base +{ + void run() const { + T x(50, hash, equal_to, allocator); + } +}; + +template +struct range : public test::exception_base +{ + test::random_values values; + + range() : values(5) {} + range(unsigned int count) : values(count) {} +}; + +template +struct range_construct_test1 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end()); + } +}; + +template +struct range_construct_test2 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end(), 0); + } +}; + +template +struct range_construct_test3 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end(), 0, hash); + } +}; + +template +struct range_construct_test4 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end(), 100, hash, equal_to); + } +}; + +// Need to run at least one test with a fairly large number +// of objects in case it triggers a rehash. +template +struct range_construct_test5 : public range, objects +{ + range_construct_test5() : range(60) {} + + void run() const { + T x(this->values.begin(), this->values.end(), 0, hash, equal_to, allocator); + } +}; + +// TODO: Write a test using an input iterator. + +RUN_EXCEPTION_TESTS( + (construct_test1)(construct_test2)(construct_test3)(construct_test4)(construct_test5) + (range_construct_test1)(range_construct_test2)(range_construct_test3)(range_construct_test4)(range_construct_test5), + CONTAINER_SEQ) diff --git a/test/exception/containers.hpp b/test/exception/containers.hpp new file mode 100644 index 00000000..62132eb1 --- /dev/null +++ b/test/exception/containers.hpp @@ -0,0 +1,28 @@ +#include +#include +#include "../objects/exception.hpp" + +typedef boost::unordered_set< + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > set; +typedef boost::unordered_multiset< + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > multiset; +typedef boost::unordered_map< + test::exception::object, + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > map; +typedef boost::unordered_multimap< + test::exception::object, + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > multimap; + +#define CONTAINER_SEQ (set)(multiset)(map)(multimap) diff --git a/test/exception/copy_tests.cpp b/test/exception/copy_tests.cpp new file mode 100644 index 00000000..d8b653cd --- /dev/null +++ b/test/exception/copy_tests.cpp @@ -0,0 +1,51 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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 "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" + +template +struct copy_test1 : public test::exception_base +{ + T x; + + void run() const { + T y(x); + } +}; + +template +struct copy_test2 : public test::exception_base +{ + test::random_values values; + T x; + + copy_test2() : values(5), x(values.begin(), values.end()) {} + + void run() const { + T y(x); + } +}; + +template +struct copy_test3 : public test::exception_base +{ + test::random_values values; + T x; + + copy_test3() : values(100), x(values.begin(), values.end()) {} + + void run() const { + T y(x); + } +}; + +RUN_EXCEPTION_TESTS( + (copy_test1)(copy_test2)(copy_test3), + CONTAINER_SEQ) diff --git a/test/exception/erase_tests.cpp b/test/exception/erase_tests.cpp new file mode 100644 index 00000000..dca138d5 --- /dev/null +++ b/test/exception/erase_tests.cpp @@ -0,0 +1,55 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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 "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/helpers.hpp" + +template +struct erase_test_base : public test::exception_base +{ + test::random_values values; + erase_test_base(unsigned int count = 5) : values(count) {} + + typedef T data_type; + + data_type init() const { + return T(values.begin(), values.end()); + } + + void check(T const& x) const { + // TODO: Check that exception was thrown by hash or predicate object? + test::check_equivalent_keys(x); + } +}; + +template +struct erase_by_key_test1 : public erase_test_base +{ + void run(T& x) const + { + typedef typename test::random_values::const_iterator iterator; + + for(iterator it = this->values.begin(), end = this->values.end(); + it != end; ++it) + { + x.erase(test::get_key(*it)); + } + } +}; + +// TODO: More tests... +// Better test by key. +// Test other erase signatures - generally they won't throw, but the standard +// does allow them to. And test clear as well. + +RUN_EXCEPTION_TESTS( + (erase_by_key_test1), + CONTAINER_SEQ) diff --git a/test/exception/insert_tests.cpp b/test/exception/insert_tests.cpp new file mode 100644 index 00000000..7f14c308 --- /dev/null +++ b/test/exception/insert_tests.cpp @@ -0,0 +1,168 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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 "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/strong.hpp" + +#include + +template +struct insert_test_base : public test::exception_base +{ + test::random_values values; + insert_test_base(unsigned int count = 5) : values(count) {} + + typedef T data_type; + typedef test::strong strong_type; + + data_type init() const { + return T(); + } + + void check(T const& x, strong_type const& strong) const { + std::string scope(test::scope); + + if(scope.find_first_of("hash::operator()") == std::string::npos) + strong.test(x); + test::check_equivalent_keys(x); + } +}; + +template +struct insert_test1 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + for(typename test::random_values::const_iterator + it = this->values.begin(), end = this->values.end(); it != end; ++it) + { + strong.store(x); + x.insert(*it); + } + } +}; + +template +struct insert_test2 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + for(typename test::random_values::const_iterator + it = this->values.begin(), end = this->values.end(); it != end; ++it) + { + strong.store(x); + x.insert(x.begin(), *it); + } + } +}; + +template +struct insert_test3 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + // I don't think there's any need for this here. + //strong.store(x); + x.insert(this->values.begin(), this->values.end()); + } + + void check(T const& x) const { + test::check_equivalent_keys(x); + } +}; + +template +struct insert_test4 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + for(typename test::random_values::const_iterator + it = this->values.begin(), end = this->values.end(); it != end; ++it) + { + strong.store(x); + x.insert(it, boost::next(it)); + } + } +}; + +template +struct insert_test_rehash1 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + insert_test_rehash1() : insert_test_base(1000) {} + + T init() const { + typedef typename T::size_type size_type; + + T x; + x.max_load_factor(0.25); + size_type bucket_count = x.bucket_count(); + size_type initial_elements = static_cast( + std::ceil(bucket_count * x.max_load_factor()) - 1); + BOOST_REQUIRE(initial_elements < this->values.size()); + x.insert(this->values.begin(), + boost::next(this->values.begin(), initial_elements)); + BOOST_REQUIRE(bucket_count == x.bucket_count()); + return x; + } + + void run(T& x, strong_type& strong) const { + typename T::size_type bucket_count = x.bucket_count(); + int count = 0; + typename T::const_iterator pos = x.cbegin(); + + for(typename test::random_values::const_iterator + it = boost::next(this->values.begin(), x.size()), end = this->values.end(); + it != end && count < 10; ++it, ++count) + { + strong.store(x); + pos = x.insert(pos, *it); + } + + // This isn't actually a failure, but it means the test isn't doing its + // job. + BOOST_REQUIRE(x.bucket_count() != bucket_count); + } +}; + +template +struct insert_test_rehash2 : public insert_test_rehash1 +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + typename T::size_type bucket_count = x.bucket_count(); + int count = 0; + + for(typename test::random_values::const_iterator + it = boost::next(this->values.begin(), x.size()), end = this->values.end(); + it != end && count < 10; ++it, ++count) + { + strong.store(x); + x.insert(*it); + } + + // This isn't actually a failure, but it means the test isn't doing its + // job. + BOOST_REQUIRE(x.bucket_count() != bucket_count); + } +}; + +RUN_EXCEPTION_TESTS( + (insert_test1)(insert_test2)(insert_test3)(insert_test4) + (insert_test_rehash1)(insert_test_rehash2), + CONTAINER_SEQ) diff --git a/test/exception/rehash_tests.cpp b/test/exception/rehash_tests.cpp new file mode 100644 index 00000000..7f67aaed --- /dev/null +++ b/test/exception/rehash_tests.cpp @@ -0,0 +1,80 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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 "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/strong.hpp" + +#include + +template +struct rehash_test_base : public test::exception_base +{ + test::random_values values; + unsigned int n; + rehash_test_base(unsigned int count = 100, unsigned int n = 0) : values(count), n(n) {} + + typedef T data_type; + typedef test::strong strong_type; + + data_type init() const { + T x(values.begin(), values.end(), n); + return x; + } + + void check(T const& x, strong_type const& strong) const { + std::string scope(test::scope); + + if(scope.find_first_of("hash::operator()") == std::string::npos && + scope.find_first_of("equal_to::operator()") == std::string::npos) + strong.test(x); + test::check_equivalent_keys(x); + } +}; + +template +struct rehash_test0 : rehash_test_base +{ + rehash_test0() : rehash_test_base(0) {} + void run(T& x) const { x.rehash(0); } +}; + +template +struct rehash_test1 : rehash_test_base +{ + rehash_test1() : rehash_test_base(0) {} + void run(T& x) const { x.rehash(200); } +}; + +template +struct rehash_test2 : rehash_test_base +{ + rehash_test2() : rehash_test_base(0, 200) {} + void run(T& x) const { x.rehash(0); } +}; + +template +struct rehash_test3 : rehash_test_base +{ + rehash_test3() : rehash_test_base(10, 0) {} + void run(T& x) const { x.rehash(200); } +}; + +template +struct rehash_test4 : rehash_test_base +{ + rehash_test4() : rehash_test_base(10, 200) {} + void run(T& x) const { x.rehash(0); } +}; + +RUN_EXCEPTION_TESTS( + (rehash_test0)(rehash_test1)(rehash_test2)(rehash_test3)(rehash_test4), + CONTAINER_SEQ) diff --git a/test/exception/swap_tests.cpp b/test/exception/swap_tests.cpp new file mode 100644 index 00000000..4be40fce --- /dev/null +++ b/test/exception/swap_tests.cpp @@ -0,0 +1,107 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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 "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" + +template +struct self_swap_base : public test::exception_base +{ + test::random_values values; + self_swap_base(int count = 0) : values(count) {} + + typedef T data_type; + T init() const { return T(values.begin(), values.end()); } + void run(T& x) const { x.swap(x); } + void check(T const& x) const { + std::string scope(test::scope); + +#if BOOST_UNORDERED_SWAP_METHOD != 2 + BOOST_CHECK( + scope == "hash::operator(hash)" || + scope == "hash::operator=(hash)" || + scope == "equal_to::operator(equal_to)" || + scope == "equal_to::operator=(equal_to)"); +#endif + + test::check_equivalent_keys(x); + } +}; + +template +struct self_swap_test1 : self_swap_base {}; + +template +struct self_swap_test2 : self_swap_base +{ + self_swap_test2() : self_swap_base(100) {} +}; + +template +struct swap_base : public test::exception_base +{ + const test::random_values x_values, y_values; + const T initial_x, initial_y; + + swap_base(unsigned int count1, unsigned int count2, int tag1, int tag2) + : x_values(count1), y_values(count2), + initial_x(x_values.begin(), x_values.end(), 0, typename T::hasher(tag1), + typename T::key_equal(tag1), typename T::allocator_type(tag1)), + initial_y(y_values.begin(), y_values.end(), 0, typename T::hasher(tag2), + typename T::key_equal(tag2), typename T::allocator_type(tag2)) + {} + + struct data_type { + data_type(T const& x, T const& y) + : x(x), y(y) {} + + T x, y; + }; + + data_type init() const { return data_type(initial_x, initial_y); } + void run(data_type& d) const { + try { + d.x.swap(d.y); + } catch (std::runtime_error) {} + } + void check(data_type const& d) const { + test::check_equivalent_keys(d.x); + test::check_equivalent_keys(d.y); + } +}; + +template +struct swap_test1 : swap_base +{ + swap_test1() : swap_base(0, 0, 0, 0) {} +}; + +template +struct swap_test2 : swap_base +{ + swap_test2() : swap_base(60, 0, 0, 0) {} +}; + +template +struct swap_test3 : swap_base +{ + swap_test3() : swap_base(0, 60, 0, 0) {} +}; + +template +struct swap_test4 : swap_base +{ + swap_test4() : swap_base(10, 10, 1, 2) {} +}; + +RUN_EXCEPTION_TESTS( + (self_swap_test1)(self_swap_test2) + (swap_test1)(swap_test2)(swap_test3)(swap_test4), + CONTAINER_SEQ) diff --git a/test/helpers/equivalent.hpp b/test/helpers/equivalent.hpp index 8d44fafd..9b4e6c8e 100644 --- a/test/helpers/equivalent.hpp +++ b/test/helpers/equivalent.hpp @@ -15,20 +15,38 @@ namespace test { template - bool equivalent(T const& x, T const& y) { + bool equivalent_impl(T const& x, T const& y) { return x == y; } template - bool equivalent(boost::hash const&, boost::hash const&) { + bool equivalent_impl(boost::hash const&, boost::hash const&) { return true; } template - bool equivalent(std::equal_to const&, std::equal_to const&) { + bool equivalent_impl(std::equal_to const&, std::equal_to const&) { return true; } + template + bool equivalent_impl(std::pair const& x1, + std::pair const& x2) { + return equivalent_impl(x1.first, x2.first) && + equivalent_impl(x1.second, x2.second); + } + + struct equivalent_type { + template + bool operator()(T1 const& x, T2 const& y) { + return equivalent_impl(x, y); + } + }; + + namespace { + equivalent_type equivalent; + } + template class unordered_equivalence_tester { @@ -56,8 +74,8 @@ namespace test bool operator()(Container const& x) const { if(!((size_ == x.size()) && - (test::equivalent(hasher_, x.hash_function())) && - (test::equivalent(key_equal_, x.key_eq())) && + (test::equivalent_impl(hasher_, x.hash_function())) && + (test::equivalent_impl(key_equal_, x.key_eq())) && (max_load_factor_ == x.max_load_factor()) && (values_.size() == x.size()))) return false; diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index ac37891b..38b8148e 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -51,23 +51,30 @@ namespace test if(x1.count(key) != count) BOOST_ERROR("Incorrect output of count."); - // Check that the keys are in the correct bucket and are adjacent in - // the bucket. - typename X::size_type bucket = x1.bucket(key); - typename X::const_local_iterator lit = x1.begin(bucket), lend = x1.end(bucket); - for(; lit != lend && !eq(get_key(*lit), key); ++lit) continue; - if(lit == lend) - BOOST_ERROR("Unable to find element with a local_iterator"); - unsigned int count2 = 0; - for(; lit != lend && eq(get_key(*lit), key); ++lit) ++count2; - if(count != count2) - BOOST_ERROR("Element count doesn't match local_iterator."); - for(; lit != lend; ++lit) { - if(eq(get_key(*lit), key)) { - BOOST_ERROR("Non-adjacent element with equivalent key in bucket."); - break; - } - } + // I'm not bothering with the following test for now, as the + // previous test is probably more enough to catch the kind of + // errors that this would catch (if an element was in the wrong + // bucket it not be found by the call to count, if elements are not + // adjacent then they would be caught when checking against + // found_. + + // // Check that the keys are in the correct bucket and are + // // adjacent in the bucket. + // typename X::size_type bucket = x1.bucket(key); + // typename X::const_local_iterator lit = x1.begin(bucket), lend = x1.end(bucket); + // for(; lit != lend && !eq(get_key(*lit), key); ++lit) continue; + // if(lit == lend) + // BOOST_ERROR("Unable to find element with a local_iterator"); + // unsigned int count2 = 0; + // for(; lit != lend && eq(get_key(*lit), key); ++lit) ++count2; + // if(count != count2) + // BOOST_ERROR("Element count doesn't match local_iterator."); + // for(; lit != lend; ++lit) { + // if(eq(get_key(*lit), key)) { + // BOOST_ERROR("Non-adjacent element with equivalent key in bucket."); + // break; + // } + // } }; // Finally, check that size matches up. diff --git a/test/helpers/metafunctions.hpp b/test/helpers/metafunctions.hpp index 5f8a8ab9..05f786d5 100644 --- a/test/helpers/metafunctions.hpp +++ b/test/helpers/metafunctions.hpp @@ -1,5 +1,5 @@ -// Copyright Daniel James 2005. Use, modification, and distribution are +// Copyright Daniel James 2005-2006. Use, modification, and distribution are // subject to 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) diff --git a/test/helpers/strong.hpp b/test/helpers/strong.hpp new file mode 100644 index 00000000..46a836e8 --- /dev/null +++ b/test/helpers/strong.hpp @@ -0,0 +1,47 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER + +#include +#include +#include +#include "./metafunctions.hpp" +#include "./equivalent.hpp" +#include "../objects/exception.hpp" + +namespace test +{ + template + class strong + { + typedef std::vector::type> values_type; + values_type values_; + public: + void store(X const& x) { + // TODO: Well, I shouldn't be disabling exceptions here. Instead + // the test runner should provide a method to the test which + // disables exceptions and calls this. + // + // Actually, the test runner could also keep track of how many times + // store is called in a run. Because it knows that in the next run + // the first n-1 stores are unnecessary - since no exceptions will + // be thrown for them. + DISABLE_EXCEPTIONS; + values_.clear(); + values_.reserve(x.size()); + std::copy(x.cbegin(), x.cend(), std::back_inserter(values_)); + } + + void test(X const& x) const { + if(!(x.size() == values_.size() && + std::equal(x.cbegin(), x.cend(), values_.begin(), test::equivalent))) + BOOST_ERROR("Strong exception safety failure."); + } + }; +} + +#endif diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp new file mode 100644 index 00000000..4727ef28 --- /dev/null +++ b/test/objects/exception.hpp @@ -0,0 +1,522 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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) + +#if !defined(BOOST_UNORDERED_TEST_OBJECTS_HEADER) +#define BOOST_UNORDERED_TEST_OBJECTS_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../helpers/fwd.hpp" + +// TODO: +// a) This can only be included in compile unit. +// b) This stuff should be somewhere else. +// but I'm feeling too lazy right now (although sadly not lazy enough to +// avoid reinventing yet another wheel). + +#define RUN_EXCEPTION_TESTS(test_seq, param_seq) \ + BOOST_PP_SEQ_FOR_EACH_PRODUCT(RUN_EXCEPTION_TESTS_OP, (test_seq)(param_seq)) + +#define RUN_EXCEPTION_TESTS_OP(r, product) \ + RUN_EXCEPTION_TESTS_OP2( \ + BOOST_PP_CAT(BOOST_PP_SEQ_ELEM(0, product), \ + BOOST_PP_CAT(_, BOOST_PP_SEQ_ELEM(1, product)) \ + ), \ + BOOST_PP_SEQ_ELEM(0, product), \ + BOOST_PP_SEQ_ELEM(1, product) \ + ) + +#define RUN_EXCEPTION_TESTS_OP2(name, test_func, type) \ + BOOST_AUTO_TEST_CASE(name) \ + { \ + test_func< type > fixture; \ + ::test::exception_safety(fixture, BOOST_STRINGIZE(test_func)); \ + } + +#define SCOPE(scope_name) \ + BOOST_ITEST_SCOPE(scope_name); \ + for(::test::scope_guard unordered_test_guard( \ + BOOST_STRINGIZE(scope_name)); \ + !unordered_test_guard.dismissed(); \ + unordered_test_guard.dismiss()) + +#define EPOINT(name) \ + if(::test::exceptions_enabled) { \ + BOOST_ITEST_EPOINT(name); \ + } + +#define ENABLE_EXCEPTIONS \ + ::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(true) +#define DISABLE_EXCEPTIONS \ + ::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(false) + +namespace test { + static char const* scope = ""; + bool exceptions_enabled = false; + + class scope_guard { + scope_guard& operator=(scope_guard const&); + scope_guard(scope_guard const&); + + char const* old_scope_; + char const* scope_; + bool dismissed_; + public: + scope_guard(char const* name) + : old_scope_(scope), + scope_(name), + dismissed_(false) + { + scope = scope_; + } + + ~scope_guard() { + if(dismissed_) scope = old_scope_; + } + + void dismiss() { + dismissed_ = true; + } + + bool dismissed() const { + return dismissed_; + } + }; + + class exceptions_enable + { + exceptions_enable& operator=(exceptions_enable const&); + exceptions_enable(exceptions_enable const&); + + bool old_value_; + public: + exceptions_enable(bool enable) + : old_value_(exceptions_enabled) + { + exceptions_enabled = enable; + } + + ~exceptions_enable() + { + exceptions_enabled = old_value_; + } + }; + + struct exception_base { + struct data_type {}; + struct strong_type { + template void store(T const&) {} + template void test(T const&) const {} + }; + data_type init() const { return data_type(); } + void check() const {} + }; + + template + inline void call_with_increased_arity(void (T::*fn)() const, T2 const& obj, + P1&, P2&) + { + (obj.*fn)(); + } + + template + inline void call_with_increased_arity(void (T::*fn)(P1&) const, T2 const& obj, + P1& p1, P2&) + { + (obj.*fn)(p1); + } + + template + inline void call_with_increased_arity(void (T::*fn)(P1&, P2&) const, T2 const& obj, + P1& p1, P2& p2) + { + (obj.*fn)(p1, p2); + } + + template + T const& constant(T const& x) { + return x; + } + + template + class test_runner + { + Test const& test_; + public: + test_runner(Test const& t) : test_(t) {} + void operator()() const { + DISABLE_EXCEPTIONS; + typename Test::data_type x(test_.init()); + typename Test::strong_type strong; + strong.store(x); + try { + ENABLE_EXCEPTIONS; + call_with_increased_arity(&Test::run, test_, x, strong); + } + catch(...) { + call_with_increased_arity(&Test::check, test_, + constant(x), constant(strong)); + throw; + } + } + }; + + template + void exception_safety(Test const& f, char const* name) { + test_runner runner(f); + ::boost::itest::exception_safety(runner, name); + } +} + +namespace test +{ +namespace exception +{ + class object; + class hash; + class equal_to; + template class allocator; + + class object + { + public: + int tag1_, tag2_; + + explicit object() : tag1_(0), tag2_(0) + { + SCOPE(object::object()) { + EPOINT("Mock object default constructor."); + } + } + + explicit object(int t1, int t2 = 0) : tag1_(t1), tag2_(t2) + { + SCOPE(object::object(int)) { + EPOINT("Mock object constructor by value."); + } + } + + object(object const& x) + : tag1_(x.tag1_), tag2_(x.tag2_) + { + SCOPE(object::object(object)) { + EPOINT("Mock object copy constructor."); + } + } + + object& operator=(object const& x) + { + SCOPE(object::operator=(object)) { + tag1_ = x.tag1_; + EPOINT("Mock object assign operator 1."); + tag2_ = x.tag2_; + //EPOINT("Mock object assign operator 2."); + } + return *this; + } + + friend bool operator==(object const& x1, object const& x2) { + SCOPE(operator==(object, object)) { + EPOINT("Mock object equality operator."); + } + + return x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_; + } + + friend bool operator!=(object const& x1, object const& x2) { + SCOPE(operator!=(object, object)) { + EPOINT("Mock object inequality operator."); + } + + return !(x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_); + } + + // None of the last few functions are used by the unordered associative + // containers - so there aren't any exception points. + friend bool operator<(object const& x1, object const& x2) { + return x1.tag1_ < x2.tag1_ || + (x1.tag1_ == x2.tag1_ && x1.tag2_ < x2.tag2_); + } + + friend object generate(object const*) { + int* x = 0; + return object(::test::generate(x), ::test::generate(x)); + } + + friend std::ostream& operator<<(std::ostream& out, object const& o) + { + return out<<"("< + class allocator + { + public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind { typedef allocator other; }; + + explicit allocator(int t = 0) + { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator default constructor."); + } + } + + template allocator(allocator const& x) + { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator template copy constructor."); + } + } + + allocator(allocator const&) + { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator copy constructor."); + } + } + + ~allocator() {} + + allocator& operator=(allocator const&) { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator assignment operator."); + } + return *this; + } + + pointer address(reference r) { + // TODO: Is this no throw? Major problems if it isn't. + //SCOPE(allocator::address(reference)) { + // EPOINT("Mock allocator address function."); + //} + return pointer(&r); + } + + const_pointer address(const_reference r) { + // TODO: Is this no throw? Major problems if it isn't. + //SCOPE(allocator::address(const_reference)) { + // EPOINT("Mock allocator const address function."); + //} + return const_pointer(&r); + } + + pointer allocate(size_type n) { + T* ptr = 0; + SCOPE(allocator::allocate(size_type)) { + EPOINT("Mock allocator allocate function."); + + using namespace std; + ptr = (T*) malloc(n * sizeof(T)); + if(!ptr) throw std::bad_alloc(); + } + return pointer(ptr); + + //return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + pointer allocate(size_type n, const_pointer u) + { + T* ptr = 0; + SCOPE(allocator::allocate(size_type, const_pointer)) { + EPOINT("Mock allocator allocate function."); + + using namespace std; + ptr = (T*) malloc(n * sizeof(T)); + if(!ptr) throw std::bad_alloc(); + } + return pointer(ptr); + + //return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + void deallocate(pointer p, size_type n) + { + //::operator delete((void*) p); + if(p) { + using namespace std; + free(p); + } + } + + void construct(pointer p, T const& t) { + SCOPE(allocator::construct(pointer, T)) { + EPOINT("Mock allocator construct function."); + new(p) T(t); + } + } + + void destroy(pointer p) { p->~T(); } + + size_type max_size() const { + SCOPE(allocator::construct(pointer, T)) { + EPOINT("Mock allocator max_size function."); + } + return (std::numeric_limits::max)(); + } + }; + + template + inline bool operator==(allocator const& x, allocator const& y) + { + // TODO: I can't meet the exception requirements for swap if this + // throws. Does the standard specify that allocator comparisons can't + // throw? + // + //SCOPE(operator==(allocator, allocator)) { + // EPOINT("Mock allocator equality operator."); + //} + return true; + } + + template + inline bool operator!=(allocator const& x, allocator const& y) + { + //SCOPE(operator!=(allocator, allocator)) { + // EPOINT("Mock allocator inequality operator."); + //} + return false; + } +} +} + +#endif diff --git a/test/objects/fwd.hpp b/test/objects/fwd.hpp index c055de70..aea15586 100644 --- a/test/objects/fwd.hpp +++ b/test/objects/fwd.hpp @@ -3,8 +3,8 @@ // subject to 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) -#if !defined(BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER) -#define BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER +#if !defined(BOOST_UNORDERED_TEST_OBJECTS_FWD_HEADER) +#define BOOST_UNORDERED_TEST_OBJECTS_FWD_HEADER namespace test { diff --git a/test/objects/test.hpp b/test/objects/test.hpp index aacedfca..15114033 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -147,6 +147,12 @@ namespace test template class allocator { +# ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS + public: +# else + template friend class allocator; +# endif + int tag_; public: typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; @@ -158,9 +164,9 @@ namespace test template struct rebind { typedef allocator other; }; - explicit allocator(int t = 0) {} - template allocator(allocator const& x) {} - allocator(allocator const&) {} + explicit allocator(int t = 0) : tag_(t) {} + template allocator(allocator const& x) : tag_(x.tag_) {} + allocator(allocator const& x) : tag_(x.tag_) {} ~allocator() {} pointer address(reference r) { return pointer(&r); } @@ -183,20 +189,20 @@ namespace test void construct(pointer p, T const& t) { new(p) T(t); } void destroy(pointer p) { p->~T(); } - size_type max_size() const { return 1000; } + size_type max_size() const { + return (std::numeric_limits::max)(); + } + + friend bool operator==(allocator const& x, allocator const& y) + { + return x.tag_ == y.tag_; + } + + friend bool operator!=(allocator const& x, allocator const& y) + { + return x.tag_ != y.tag_; + } }; - - template - inline bool operator==(allocator const& x, allocator const& y) - { - return true; - } - - template - inline bool operator!=(allocator const& x, allocator const& y) - { - return false; - } } #endif diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 index 007f7759..d7c2efc4 100644 --- a/test/unordered/Jamfile.v2 +++ b/test/unordered/Jamfile.v2 @@ -20,4 +20,10 @@ test-suite unordered-tests [ run insert_tests.cpp ] [ run erase_tests.cpp ] [ run find_tests.cpp ] + [ run bucket_tests.cpp ] + [ run load_factor_tests.cpp ] + [ run rehash_tests.cpp ] + [ run swap_tests.cpp : : : BOOST_UNORDERED_SWAP_METHOD=1 : swap_tests1 ] + [ run swap_tests.cpp : : : BOOST_UNORDERED_SWAP_METHOD=2 : swap_tests2 ] + [ run swap_tests.cpp : : : BOOST_UNORDERED_SWAP_METHOD=3 : swap_tests3 ] ; diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp index 91b47371..b7053b97 100644 --- a/test/unordered/assign_tests.cpp +++ b/test/unordered/assign_tests.cpp @@ -53,9 +53,11 @@ void assign_tests2(T* = 0) typename T::hasher hf; typename T::key_equal eq; typename T::hasher hf1(1); - typename T::key_equal eq1(1); typename T::hasher hf2(2); + typename T::key_equal eq1(1); typename T::key_equal eq2(2); + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); std::cerr<<"assign_tests2.1\n"; { @@ -68,6 +70,19 @@ void assign_tests2(T* = 0) BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); check_container(x2, v); } + + std::cerr<<"assign_tests2.2\n"; + { + // TODO: Need to generate duplicates... + test::random_values v1(100), v2(100); + T x1(v1.begin(), v1.end(), 0, hf1, eq1, al1); + T x2(v2.begin(), v2.end(), 0, hf2, eq2, al2); + x2 = x1; + BOOST_TEST(test::equivalent(x2.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x2.get_allocator(), al2)); + check_container(x2, v1); + } } int main() diff --git a/test/unordered/bucket_tests.cpp b/test/unordered/bucket_tests.cpp new file mode 100644 index 00000000..b4b78041 --- /dev/null +++ b/test/unordered/bucket_tests.cpp @@ -0,0 +1,60 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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 +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/helpers.hpp" + +template +void bucket_tests(X* = 0) +{ + typedef typename X::size_type size_type; + typedef typename X::const_local_iterator const_local_iterator; + test::random_values v(1000); + + X x(v.begin(), v.end()); + + BOOST_TEST(x.bucket_count() < x.max_bucket_count()); + std::cerr<::const_iterator + it = v.begin(), end = v.end(); it != end; ++it) + { + size_type bucket = x.bucket(test::get_key(*it)); + + BOOST_TEST(bucket < x.bucket_count()); + if(bucket < x.max_bucket_count()) { + // lit? lend?? I need a new naming scheme. + const_local_iterator lit = x.begin(bucket), lend = x.end(bucket); + while(lit != lend && test::get_key(*it) != test::get_key(*lit)) ++lit; + BOOST_TEST(lit != lend); + } + } + + for(size_type i = 0; i < x.bucket_count(); ++i) { + BOOST_TEST(x.bucket_size(i) == (size_type) std::distance(x.begin(i), x.end(i))); + X const& x_ref(x); + BOOST_TEST(x.bucket_size(i) == (size_type) std::distance(x_ref.begin(i), x_ref.end(i))); + } +} + +int main() +{ + bucket_tests((boost::unordered_set*) 0); + bucket_tests((boost::unordered_multiset*) 0); + bucket_tests((boost::unordered_map*) 0); + bucket_tests((boost::unordered_multimap*) 0); + + bucket_tests((boost::unordered_set >*) 0); + bucket_tests((boost::unordered_multiset >*) 0); + bucket_tests((boost::unordered_map >*) 0); + bucket_tests((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index 5114331c..c8d58dd3 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -18,6 +18,7 @@ void constructor_tests1(T* = 0) { typename T::hasher hf; typename T::key_equal eq; + typename T::allocator_type al; std::cerr<<"Construct 1\n"; { @@ -26,6 +27,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 0); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 2\n"; @@ -35,6 +37,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 100); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 3\n"; @@ -44,6 +47,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 2000); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 4\n"; @@ -52,6 +56,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.empty()); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 5\n"; @@ -61,6 +66,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 10000); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } @@ -71,6 +77,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 10000); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } @@ -81,6 +88,7 @@ void constructor_tests1(T* = 0) BOOST_TEST(x.bucket_count() >= 100); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } @@ -90,6 +98,28 @@ void constructor_tests1(T* = 0) T x(v.begin(), v.end()); BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + check_container(x, v); + } + + std::cerr<<"Construct 9\n"; + { + T x(0, hf, eq, al); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 0); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + } + + std::cerr<<"Construct 10\n"; + { + test::random_values v(1000); + T x(v.begin(), v.end(), 10000, hf, eq, al); + BOOST_TEST(x.bucket_count() >= 10000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } } @@ -103,6 +133,9 @@ void constructor_tests2(T* = 0) typename T::key_equal eq; typename T::key_equal eq1(1); typename T::key_equal eq2(2); + typename T::allocator_type al; + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); std::cerr<<"Construct 1\n"; { @@ -110,6 +143,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(x.bucket_count() >= 10000); BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 2\n"; @@ -119,6 +153,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(x.bucket_count() >= 100); BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); } std::cerr<<"Construct 3\n"; @@ -127,6 +162,7 @@ void constructor_tests2(T* = 0) T x(v.begin(), v.end(), 0, hf1, eq1); BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } @@ -137,6 +173,7 @@ void constructor_tests2(T* = 0) BOOST_TEST(x.bucket_count() >= 1000); BOOST_TEST(test::equivalent(x.hash_function(), hf1)); BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); check_container(x, v); } @@ -144,8 +181,8 @@ void constructor_tests2(T* = 0) std::cerr<<"Construct 5\n"; { test::random_values v(100); - T x(v.begin(), v.end(), 0, hf, eq); - T y(x.begin(), x.end(), 0, hf1, eq1); + T x(v.begin(), v.end(), 0, hf, eq, al1); + T y(x.begin(), x.end(), 0, hf1, eq1, al2); check_container(x, v); check_container(y, x); } diff --git a/test/unordered/copy_tests.cpp b/test/unordered/copy_tests.cpp index 1a72bf9f..202286b0 100644 --- a/test/unordered/copy_tests.cpp +++ b/test/unordered/copy_tests.cpp @@ -17,6 +17,7 @@ void copy_construct_tests1(T* = 0) { typename T::hasher hf; typename T::key_equal eq; + typename T::allocator_type al; { T x; @@ -24,6 +25,7 @@ void copy_construct_tests1(T* = 0) 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(x.max_load_factor() == y.max_load_factor()); test::check_equivalent_keys(y); } @@ -42,15 +44,14 @@ void copy_construct_tests1(T* = 0) // In this test I drop the original containers max load factor, so it // is much lower than the load factor. The hash table is not allowed // to rehash, but the destination container should probably allocate - // enough buckets to decrease the load factor appropriately. Although, - // I don't think it has to. + // enough buckets to decrease the load factor appropriately. test::random_values v(1000); T x(v.begin(), v.end()); x.max_load_factor(x.load_factor() / 4); T y(x); test::unordered_equivalence_tester equivalent(x); equivalent(y); - // I don't think this is guaranteed: + // This isn't guaranteed: BOOST_TEST(y.load_factor() < y.max_load_factor()); test::check_equivalent_keys(y); } @@ -63,14 +64,16 @@ void copy_construct_tests2(T* ptr = 0) typename T::hasher hf(1); typename T::key_equal eq(1); + typename T::allocator_type al(1); { // TODO: I could check how many buckets y has, it should be lower (QOI issue). - T x(10000, hf, eq); + T x(10000, hf, eq, al); T y(x); 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(x.max_load_factor() == y.max_load_factor()); test::check_equivalent_keys(y); } @@ -79,7 +82,7 @@ void copy_construct_tests2(T* ptr = 0) // TODO: Invariant checks are especially important here. test::random_values v(1000); - T x(v.begin(), v.end(), 0, hf, eq); + T x(v.begin(), v.end(), 0, hf, eq, al); T y(x); test::unordered_equivalence_tester equivalent(x); equivalent(y); diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp index 5ca4f641..a920a7a6 100644 --- a/test/unordered/erase_tests.cpp +++ b/test/unordered/erase_tests.cpp @@ -70,7 +70,7 @@ void erase_tests1(Container* = 0) pos = boost::next(prev); } next = boost::next(pos); - typename Container::key_type key = test::get_key(*x.begin()); + typename Container::key_type key = test::get_key(*pos); std::size_t count = x.count(key); BOOST_TEST(next == x.erase(pos)); --size; @@ -137,4 +137,6 @@ int main() erase_tests1((boost::unordered_map >*) 0); std::cerr<<"\nErase unordered_multimap.\n"; erase_tests1((boost::unordered_multimap >*) 0); + + return boost::report_errors(); } diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index 14d98347..1d3be884 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -15,76 +15,94 @@ #include -template -void unique_insert_tests1(Container* = 0) +template +void unique_insert_tests1(X* = 0) { std::cerr<<"insert(value) tests for containers with unique keys.\n"; - Container x; - test::ordered tracker = test::create_ordered(x); + X x; + test::ordered tracker = test::create_ordered(x); - test::random_values v(1000); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { - std::pair r1 = x.insert(*it); - std::pair::iterator, bool> r2 + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + std::pair r1 = x.insert(*it); + std::pair::iterator, bool> r2 = tracker.insert(*it); BOOST_TEST(r1.second == r2.second); BOOST_TEST(*r1.first == *r2.first); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); } -template -void equivalent_insert_tests1(Container* = 0) +template +void equivalent_insert_tests1(X* = 0) { std::cerr<<"insert(value) tests for containers with equivalent keys.\n"; - Container x; - test::ordered tracker = test::create_ordered(x); + X x; + test::ordered tracker = test::create_ordered(x); - test::random_values v(1000); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { - typename Container::iterator r1 = x.insert(*it); - typename test::ordered::iterator r2 = tracker.insert(*it); + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + typename X::iterator r1 = x.insert(*it); + typename test::ordered::iterator r2 = tracker.insert(*it); BOOST_TEST(*r1 == *r2); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); } -template -void insert_tests2(Container* = 0) +template +void insert_tests2(X* = 0) { - typedef typename test::ordered tracker_type; - typedef typename Container::iterator iterator; - typedef typename Container::const_iterator const_iterator; + typedef typename test::ordered tracker_type; + typedef typename X::iterator iterator; + typedef typename X::const_iterator const_iterator; typedef typename tracker_type::iterator tracker_iterator; std::cerr<<"insert(begin(), value) tests.\n"; { - Container x; + X x; tracker_type tracker = test::create_ordered(x); - test::random_values v(1000); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + iterator r1 = x.insert(x.begin(), *it); tracker_iterator r2 = tracker.insert(tracker.begin(), *it); BOOST_TEST(*r1 == *r2); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); @@ -93,18 +111,24 @@ void insert_tests2(Container* = 0) std::cerr<<"insert(end(), value) tests.\n"; { - Container x; - Container const& x_const = x; + X x; + X const& x_const = x; tracker_type tracker = test::create_ordered(x); - test::random_values v(100); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(100); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + const_iterator r1 = x.insert(x_const.end(), *it); tracker_iterator r2 = tracker.insert(tracker.end(), *it); BOOST_TEST(*r1 == *r2); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); @@ -113,18 +137,24 @@ void insert_tests2(Container* = 0) std::cerr<<"insert(pos, value) tests.\n"; { - Container x; + X x; const_iterator pos = x.begin(); tracker_type tracker = test::create_ordered(x); - test::random_values v(1000); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + pos = x.insert(pos, *it); tracker_iterator r2 = tracker.insert(tracker.begin(), *it); BOOST_TEST(*pos == *r2); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); @@ -133,16 +163,22 @@ void insert_tests2(Container* = 0) std::cerr<<"insert single item range tests.\n"; { - Container x; + X x; tracker_type tracker = test::create_ordered(x); - test::random_values v(1000); - for(typename test::random_values::iterator it = v.begin(); + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); it != v.end(); ++it) { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + x.insert(it, boost::next(it)); tracker.insert(*it); tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); } test::check_equivalent_keys(x); @@ -151,10 +187,10 @@ void insert_tests2(Container* = 0) std::cerr<<"insert range tests.\n"; { - Container x; + X x; const_iterator pos = x.begin(); - test::random_values v(1000); + test::random_values v(1000); x.insert(v.begin(), v.end()); check_container(x, v); @@ -162,6 +198,31 @@ void insert_tests2(Container* = 0) } } +template +void map_tests(X* = 0) +{ + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + x[it->first] = it->second; + tracker[it->first] = it->second; + + tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); +} + int main() { unique_insert_tests1((boost::unordered_set*) 0); @@ -184,5 +245,8 @@ int main() insert_tests2((boost::unordered_map >*) 0); insert_tests2((boost::unordered_multimap >*) 0); + map_tests((boost::unordered_map*) 0); + map_tests((boost::unordered_map >*) 0); + return boost::report_errors(); } diff --git a/test/unordered/load_factor_tests.cpp b/test/unordered/load_factor_tests.cpp new file mode 100644 index 00000000..973c8abf --- /dev/null +++ b/test/unordered/load_factor_tests.cpp @@ -0,0 +1,72 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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 +#include +#include +#include +#include "../helpers/random_values.hpp" + +template +void load_factor_tests(X* = 0) +{ + X x; + + BOOST_TEST(x.max_load_factor() == 1.0); + BOOST_TEST(x.load_factor() == 0); + + // A valid implementation could fail these tests, but I think they're + // reasonable. + x.max_load_factor(2.0); BOOST_TEST(x.max_load_factor() == 2.0); + x.max_load_factor(0.5); BOOST_TEST(x.max_load_factor() == 0.5); +} + +template +void insert_test(X*, float mlf) +{ + X x; + x.max_load_factor(mlf); + float b = x.max_load_factor(); + + test::random_values values(1000); + + for(typename test::random_values::const_iterator + it = values.begin(), end = values.end(); it != end; ++it) + { + typename X::size_type old_size = x.size(), + old_bucket_count = x.bucket_count(); + x.insert(*it); + if(old_size + 1 < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } +} + +template +void load_factor_insert_tests(X* ptr = 0) +{ + insert_test(ptr, 1.0); + insert_test(ptr, 0.1); + insert_test(ptr, 100); + + insert_test(ptr, (std::numeric_limits::min)()); + + if(std::numeric_limits::has_infinity) + insert_test(ptr, std::numeric_limits::infinity()); +} + +int main() +{ + load_factor_tests((boost::unordered_set*) 0); + load_factor_tests((boost::unordered_multiset*) 0); + load_factor_tests((boost::unordered_map*) 0); + load_factor_tests((boost::unordered_multimap*) 0); + + load_factor_insert_tests((boost::unordered_set*) 0); + load_factor_insert_tests((boost::unordered_multiset*) 0); + load_factor_insert_tests((boost::unordered_map*) 0); + load_factor_insert_tests((boost::unordered_multimap*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/rehash_tests.cpp b/test/unordered/rehash_tests.cpp new file mode 100644 index 00000000..9892be90 --- /dev/null +++ b/test/unordered/rehash_tests.cpp @@ -0,0 +1,67 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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 +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" + +template +bool postcondition(X const& x, typename X::size_type n) +{ + return x.bucket_count() > x.size() / x.max_load_factor() && x.bucket_count() >= n; +} + +template +void rehash_empty_test1(X* = 0) +{ + X x; + + x.rehash(10000); + BOOST_TEST(postcondition(x, 10000)); + + x.rehash(0); + BOOST_TEST(postcondition(x, 0)); +} + +template +void rehash_test1(X* = 0) +{ + test::random_values v(1000); + test::ordered tracker; + tracker.insert(v.begin(), v.end()); + X x(v.begin(), v.end()); + + x.rehash(0); BOOST_TEST(postcondition(x, 0)); + tracker.compare(x); + + x.max_load_factor(0.25); + x.rehash(0); BOOST_TEST(postcondition(x, 0)); + tracker.compare(x); + + x.max_load_factor(50.0); + x.rehash(0); BOOST_TEST(postcondition(x, 0)); + tracker.compare(x); + + x.rehash(1000); BOOST_TEST(postcondition(x, 1000)); + tracker.compare(x); +} + +template +void rehash_tests(X* ptr = 0) +{ + rehash_empty_test1(ptr); + rehash_test1(ptr); +} + +int main() { + rehash_tests((boost::unordered_set*) 0); + rehash_tests((boost::unordered_multiset*) 0); + rehash_tests((boost::unordered_map*) 0); + rehash_tests((boost::unordered_multimap*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/swap_tests.cpp b/test/unordered/swap_tests.cpp new file mode 100644 index 00000000..7b074283 --- /dev/null +++ b/test/unordered/swap_tests.cpp @@ -0,0 +1,135 @@ + +// Copyright Daniel James 2006. Use, modification, and distribution are +// subject to 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 +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/invariants.hpp" + +template +void swap_test_impl(X& x1, X& x2) +{ + test::ordered tracker1 = test::create_ordered(x1); + test::ordered tracker2 = test::create_ordered(x2); + tracker1.insert(x1.begin(), x1.end()); + tracker2.insert(x2.begin(), x2.end()); + x1.swap(x2); + tracker1.compare(x2); + tracker2.compare(x1); +} + +template +void swap_tests1(X* = 0) +{ + { + X x; + swap_test_impl(x, x); + } + + { + X x,y; + swap_test_impl(x, y); + } + + { + test::random_values v(1000); + X x, y(v.begin(), v.end()); + swap_test_impl(x, y); + swap_test_impl(x, y); + } + + { + test::random_values vx(1000), vy(1000); + X x(vx.begin(), vx.end()), y(vy.begin(), vy.end()); + swap_test_impl(x, y); + swap_test_impl(x, y); + } +} + +template +void swap_tests2(X* ptr = 0) +{ + swap_tests1(ptr); + + typedef typename X::hasher hasher; + typedef typename X::key_equal key_equal; + typedef typename X::allocator_type allocator_type; + + { + X x(0, hasher(1), key_equal(1)); + X y(0, hasher(2), key_equal(2)); + swap_test_impl(x, y); + } + + { + test::random_values v(1000); + X x(v.begin(), v.end(), 0, hasher(1), key_equal(1)); + X y(0, hasher(2), key_equal(2)); + swap_test_impl(x, y); + swap_test_impl(x, y); + } + + { + test::random_values vx(1000), vy(1000); + X x(vx.begin(), vx.end(), 0, hasher(1), key_equal(1)); + X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2)); + swap_test_impl(x, y); + swap_test_impl(x, y); + } + +#if BOOST_UNORDERED_SWAP_METHOD == 1 + { + test::random_values vx(1000), vy(1000); + X x(vx.begin(), vx.end(), 0, hasher(), key_equal(), allocator_type(1)); + X y(vy.begin(), vy.end(), 0, hasher(), key_equal(), allocator_type(2)); + try { + swap_test_impl(x, y); + BOOST_ERROR("Using swap method 1, swapping with unequal allocators didn't throw."); + } catch (std::runtime_error) {} + } +#else + { + test::random_values vx(1000), vy(1000); + X x(vx.begin(), vx.end(), 0, hasher(), key_equal(), allocator_type(1)); + X y(vy.begin(), vy.end(), 0, hasher(), key_equal(), allocator_type(2)); + swap_test_impl(x, y); + swap_test_impl(x, y); + } + + { + test::random_values vx(1000), vy(1000); + X x(vx.begin(), vx.end(), 0, hasher(1), key_equal(1), allocator_type(1)); + X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2), allocator_type(2)); + swap_test_impl(x, y); + swap_test_impl(x, y); + } +#endif +} + +int main() +{ + std::cerr<<"Erase unordered_set.\n"; + swap_tests1((boost::unordered_set*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + swap_tests1((boost::unordered_multiset*) 0); + std::cerr<<"\nErase unordered_map.\n"; + swap_tests1((boost::unordered_map*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + swap_tests1((boost::unordered_multimap*) 0); + + std::cerr<<"\nErase unordered_set.\n"; + swap_tests2((boost::unordered_set >*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + swap_tests2((boost::unordered_multiset >*) 0); + std::cerr<<"\nErase unordered_map.\n"; + swap_tests2((boost::unordered_map >*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + swap_tests2((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +}