More tests for unordered associative containers.

[SVN r2959]
This commit is contained in:
Daniel James
2006-05-21 17:14:11 +00:00
parent 822b0c7ffd
commit fbd37a946b
28 changed files with 1975 additions and 86 deletions

View File

@ -5,3 +5,4 @@
build-project container ;
build-project unordered ;
build-project exception ;

View File

@ -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 <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <algorithm>
#include "../helpers/equivalent.hpp"
template <class X>
void simple_test(X const& a)
{
test::unordered_equivalence_tester<X> 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<X const&>(b).cbegin());
BOOST_TEST(b.end() == const_cast<X const&>(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<int> set;
simple_test(set);
std::cout<<"Test unordered_multiset.\n";
boost::unordered_multiset<int> multiset;
simple_test(multiset);
std::cout<<"Test unordered_map.\n";
boost::unordered_map<int, int> map;
simple_test(map);
std::cout<<"Test unordered_multimap.\n";
boost::unordered_multimap<int, int> multimap;
simple_test(multimap);
return boost::report_errors();
}

29
test/exception/Jamfile.v2 Normal file
View File

@ -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/<optimization>speed ;
project unordered-test/exception-tests
: requirements
<toolset>intel-linux:"<cxxflags>-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 : : :
<define>BOOST_UNORDERED_SWAP_METHOD=1 : swap_tests1 ]
[ run swap_tests.cpp framework : : :
<define>BOOST_UNORDERED_SWAP_METHOD=2 : swap_tests2 ]
[ run swap_tests.cpp framework : : :
<define>BOOST_UNORDERED_SWAP_METHOD=3 : swap_tests3 ]
;

View File

@ -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 <boost/test/unit_test.hpp>
#include <boost/test/exception_safety.hpp>
#include "../helpers/random_values.hpp"
#include "../helpers/invariants.hpp"
template <class T>
struct self_assign_base : public test::exception_base
{
test::random_values<T> 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 <class T>
struct self_assign_test1 : self_assign_base<T> {};
template <class T>
struct self_assign_test2 : self_assign_base<T>
{
self_assign_test2() : self_assign_base<T>(100) {}
};
template <class T>
struct assign_base : public test::exception_base
{
const test::random_values<T> 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 <class T>
struct assign_test1 : assign_base<T>
{
assign_test1() : assign_base<T>(0, 0, 0, 0) {}
};
template <class T>
struct assign_test2 : assign_base<T>
{
assign_test2() : assign_base<T>(60, 0, 0, 0) {}
};
template <class T>
struct assign_test3 : assign_base<T>
{
assign_test3() : assign_base<T>(0, 60, 0, 0) {}
};
template <class T>
struct assign_test4 : assign_base<T>
{
assign_test4() : assign_base<T>(10, 10, 1, 2) {}
};
RUN_EXCEPTION_TESTS(
(self_assign_test1)(self_assign_test2)
(assign_test1)(assign_test2)(assign_test3)(assign_test4),
CONTAINER_SEQ)

View File

@ -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 <boost/test/unit_test.hpp>
#include <boost/test/exception_safety.hpp>
#include "../helpers/random_values.hpp"
struct objects
{
test::exception::object obj;
test::exception::hash hash;
test::exception::equal_to equal_to;
test::exception::allocator<test::exception::object> allocator;
};
template <class T>
struct construct_test1 : public objects, test::exception_base
{
void run() const {
T x;
}
};
template <class T>
struct construct_test2 : public objects, test::exception_base
{
void run() const {
T x(300);
}
};
template <class T>
struct construct_test3 : public objects, test::exception_base
{
void run() const {
T x(0, hash);
}
};
template <class T>
struct construct_test4 : public objects, test::exception_base
{
void run() const {
T x(0, hash, equal_to);
}
};
template <class T>
struct construct_test5 : public objects, test::exception_base
{
void run() const {
T x(50, hash, equal_to, allocator);
}
};
template <class T>
struct range : public test::exception_base
{
test::random_values<T> values;
range() : values(5) {}
range(unsigned int count) : values(count) {}
};
template <class T>
struct range_construct_test1 : public range<T>, objects
{
void run() const {
T x(this->values.begin(), this->values.end());
}
};
template <class T>
struct range_construct_test2 : public range<T>, objects
{
void run() const {
T x(this->values.begin(), this->values.end(), 0);
}
};
template <class T>
struct range_construct_test3 : public range<T>, objects
{
void run() const {
T x(this->values.begin(), this->values.end(), 0, hash);
}
};
template <class T>
struct range_construct_test4 : public range<T>, 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 <class T>
struct range_construct_test5 : public range<T>, objects
{
range_construct_test5() : range<T>(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)

View File

@ -0,0 +1,28 @@
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
#include "../objects/exception.hpp"
typedef boost::unordered_set<
test::exception::object,
test::exception::hash,
test::exception::equal_to,
test::exception::allocator<test::exception::object> > set;
typedef boost::unordered_multiset<
test::exception::object,
test::exception::hash,
test::exception::equal_to,
test::exception::allocator<test::exception::object> > multiset;
typedef boost::unordered_map<
test::exception::object,
test::exception::object,
test::exception::hash,
test::exception::equal_to,
test::exception::allocator<test::exception::object> > map;
typedef boost::unordered_multimap<
test::exception::object,
test::exception::object,
test::exception::hash,
test::exception::equal_to,
test::exception::allocator<test::exception::object> > multimap;
#define CONTAINER_SEQ (set)(multiset)(map)(multimap)

View File

@ -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 <boost/test/unit_test.hpp>
#include <boost/test/exception_safety.hpp>
#include "../helpers/random_values.hpp"
template <class T>
struct copy_test1 : public test::exception_base
{
T x;
void run() const {
T y(x);
}
};
template <class T>
struct copy_test2 : public test::exception_base
{
test::random_values<T> values;
T x;
copy_test2() : values(5), x(values.begin(), values.end()) {}
void run() const {
T y(x);
}
};
template <class T>
struct copy_test3 : public test::exception_base
{
test::random_values<T> 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)

View File

@ -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 <boost/test/unit_test.hpp>
#include <boost/test/exception_safety.hpp>
#include "../helpers/random_values.hpp"
#include "../helpers/invariants.hpp"
#include "../helpers/helpers.hpp"
template <class T>
struct erase_test_base : public test::exception_base
{
test::random_values<T> 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 <class T>
struct erase_by_key_test1 : public erase_test_base<T>
{
void run(T& x) const
{
typedef typename test::random_values<T>::const_iterator iterator;
for(iterator it = this->values.begin(), end = this->values.end();
it != end; ++it)
{
x.erase(test::get_key<T>(*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)

View File

@ -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 <boost/test/unit_test.hpp>
#include <boost/test/exception_safety.hpp>
#include <string>
#include "../helpers/random_values.hpp"
#include "../helpers/invariants.hpp"
#include "../helpers/strong.hpp"
#include <cmath>
template <class T>
struct insert_test_base : public test::exception_base
{
test::random_values<T> values;
insert_test_base(unsigned int count = 5) : values(count) {}
typedef T data_type;
typedef test::strong<T> 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 <class T>
struct insert_test1 : public insert_test_base<T>
{
typedef typename insert_test_base<T>::strong_type strong_type;
void run(T& x, strong_type& strong) const {
for(typename test::random_values<T>::const_iterator
it = this->values.begin(), end = this->values.end(); it != end; ++it)
{
strong.store(x);
x.insert(*it);
}
}
};
template <class T>
struct insert_test2 : public insert_test_base<T>
{
typedef typename insert_test_base<T>::strong_type strong_type;
void run(T& x, strong_type& strong) const {
for(typename test::random_values<T>::const_iterator
it = this->values.begin(), end = this->values.end(); it != end; ++it)
{
strong.store(x);
x.insert(x.begin(), *it);
}
}
};
template <class T>
struct insert_test3 : public insert_test_base<T>
{
typedef typename insert_test_base<T>::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 <class T>
struct insert_test4 : public insert_test_base<T>
{
typedef typename insert_test_base<T>::strong_type strong_type;
void run(T& x, strong_type& strong) const {
for(typename test::random_values<T>::const_iterator
it = this->values.begin(), end = this->values.end(); it != end; ++it)
{
strong.store(x);
x.insert(it, boost::next(it));
}
}
};
template <class T>
struct insert_test_rehash1 : public insert_test_base<T>
{
typedef typename insert_test_base<T>::strong_type strong_type;
insert_test_rehash1() : insert_test_base<T>(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<size_type>(
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<T>::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 <class T>
struct insert_test_rehash2 : public insert_test_rehash1<T>
{
typedef typename insert_test_base<T>::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<T>::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)

View File

@ -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 <boost/test/unit_test.hpp>
#include <boost/test/exception_safety.hpp>
#include <string>
#include "../helpers/random_values.hpp"
#include "../helpers/invariants.hpp"
#include "../helpers/strong.hpp"
#include <iostream>
template <class T>
struct rehash_test_base : public test::exception_base
{
test::random_values<T> 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<T> 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 <class T>
struct rehash_test0 : rehash_test_base<T>
{
rehash_test0() : rehash_test_base<T>(0) {}
void run(T& x) const { x.rehash(0); }
};
template <class T>
struct rehash_test1 : rehash_test_base<T>
{
rehash_test1() : rehash_test_base<T>(0) {}
void run(T& x) const { x.rehash(200); }
};
template <class T>
struct rehash_test2 : rehash_test_base<T>
{
rehash_test2() : rehash_test_base<T>(0, 200) {}
void run(T& x) const { x.rehash(0); }
};
template <class T>
struct rehash_test3 : rehash_test_base<T>
{
rehash_test3() : rehash_test_base<T>(10, 0) {}
void run(T& x) const { x.rehash(200); }
};
template <class T>
struct rehash_test4 : rehash_test_base<T>
{
rehash_test4() : rehash_test_base<T>(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)

View File

@ -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 <boost/test/unit_test.hpp>
#include <boost/test/exception_safety.hpp>
#include "../helpers/random_values.hpp"
#include "../helpers/invariants.hpp"
template <class T>
struct self_swap_base : public test::exception_base
{
test::random_values<T> 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 <class T>
struct self_swap_test1 : self_swap_base<T> {};
template <class T>
struct self_swap_test2 : self_swap_base<T>
{
self_swap_test2() : self_swap_base<T>(100) {}
};
template <class T>
struct swap_base : public test::exception_base
{
const test::random_values<T> 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 <class T>
struct swap_test1 : swap_base<T>
{
swap_test1() : swap_base<T>(0, 0, 0, 0) {}
};
template <class T>
struct swap_test2 : swap_base<T>
{
swap_test2() : swap_base<T>(60, 0, 0, 0) {}
};
template <class T>
struct swap_test3 : swap_base<T>
{
swap_test3() : swap_base<T>(0, 60, 0, 0) {}
};
template <class T>
struct swap_test4 : swap_base<T>
{
swap_test4() : swap_base<T>(10, 10, 1, 2) {}
};
RUN_EXCEPTION_TESTS(
(self_swap_test1)(self_swap_test2)
(swap_test1)(swap_test2)(swap_test3)(swap_test4),
CONTAINER_SEQ)

View File

@ -15,20 +15,38 @@
namespace test
{
template <class T>
bool equivalent(T const& x, T const& y) {
bool equivalent_impl(T const& x, T const& y) {
return x == y;
}
template <class T>
bool equivalent(boost::hash<T> const&, boost::hash<T> const&) {
bool equivalent_impl(boost::hash<T> const&, boost::hash<T> const&) {
return true;
}
template <class T>
bool equivalent(std::equal_to<T> const&, std::equal_to<T> const&) {
bool equivalent_impl(std::equal_to<T> const&, std::equal_to<T> const&) {
return true;
}
template <class T1, class T2, class T3, class T4>
bool equivalent_impl(std::pair<T1, T2> const& x1,
std::pair<T3, T4> const& x2) {
return equivalent_impl(x1.first, x2.first) &&
equivalent_impl(x1.second, x2.second);
}
struct equivalent_type {
template <class T1, class T2>
bool operator()(T1 const& x, T2 const& y) {
return equivalent_impl(x, y);
}
};
namespace {
equivalent_type equivalent;
}
template <class Container>
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;

View File

@ -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<X>(*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<X>(*lit), key); ++lit) ++count2;
if(count != count2)
BOOST_ERROR("Element count doesn't match local_iterator.");
for(; lit != lend; ++lit) {
if(eq(get_key<X>(*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<X>(*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<X>(*lit), key); ++lit) ++count2;
// if(count != count2)
// BOOST_ERROR("Element count doesn't match local_iterator.");
// for(; lit != lend; ++lit) {
// if(eq(get_key<X>(*lit), key)) {
// BOOST_ERROR("Non-adjacent element with equivalent key in bucket.");
// break;
// }
// }
};
// Finally, check that size matches up.

View File

@ -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)

47
test/helpers/strong.hpp Normal file
View File

@ -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 <boost/config.hpp>
#include <vector>
#include <iterator>
#include "./metafunctions.hpp"
#include "./equivalent.hpp"
#include "../objects/exception.hpp"
namespace test
{
template <class X>
class strong
{
typedef std::vector<typename non_const_value_type<X>::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

522
test/objects/exception.hpp Normal file
View File

@ -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 <cstddef>
#include <boost/limits.hpp>
#include <boost/test/test_tools.hpp>
#include <boost/test/exception_safety.hpp>
#include <boost/preprocessor/seq/for_each_product.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/cat.hpp>
#include <iostream>
#include <cstdlib>
#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<type>)); \
}
#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 <class T> void store(T const&) {}
template <class T> void test(T const&) const {}
};
data_type init() const { return data_type(); }
void check() const {}
};
template <class T, class P1, class P2, class T2>
inline void call_with_increased_arity(void (T::*fn)() const, T2 const& obj,
P1&, P2&)
{
(obj.*fn)();
}
template <class T, class P1, class P2, class T2>
inline void call_with_increased_arity(void (T::*fn)(P1&) const, T2 const& obj,
P1& p1, P2&)
{
(obj.*fn)(p1);
}
template <class T, class P1, class P2, class T2>
inline void call_with_increased_arity(void (T::*fn)(P1&, P2&) const, T2 const& obj,
P1& p1, P2& p2)
{
(obj.*fn)(p1, p2);
}
template <class T>
T const& constant(T const& x) {
return x;
}
template <class Test>
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 <class Test>
void exception_safety(Test const& f, char const* name) {
test_runner<Test> runner(f);
::boost::itest::exception_safety(runner, name);
}
}
namespace test
{
namespace exception
{
class object;
class hash;
class equal_to;
template <class T> 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<<"("<<o.tag1_<<","<<o.tag2_<<")";
}
};
class hash
{
int tag_;
public:
hash(int t = 0) : tag_(t)
{
SCOPE(hash::object()) {
EPOINT("Mock hash default constructor.");
}
}
hash(hash const& x)
: tag_(x.tag_)
{
SCOPE(hash::hash(hash)) {
EPOINT("Mock hash copy constructor.");
}
}
hash& operator=(hash const& x)
{
SCOPE(hash::operator=(hash)) {
EPOINT("Mock hash assign operator 1.");
tag_ = x.tag_;
EPOINT("Mock hash assign operator 2.");
}
return *this;
}
std::size_t operator()(object const& x) const {
SCOPE(hash::operator()(object)) {
EPOINT("Mock hash function.");
}
switch(tag_) {
case 1:
return x.tag1_;
case 2:
return x.tag2_;
default:
return x.tag1_ + x.tag2_;
}
}
friend bool operator==(hash const& x1, hash const& x2) {
SCOPE(operator==(hash, hash)) {
EPOINT("Mock hash equality function.");
}
return x1.tag_ == x2.tag_;
}
friend bool operator!=(hash const& x1, hash const& x2) {
SCOPE(hash::operator!=(hash, hash)) {
EPOINT("Mock hash inequality function.");
}
return x1.tag_ != x2.tag_;
}
};
class equal_to
{
int tag_;
public:
equal_to(int t = 0) : tag_(t)
{
SCOPE(equal_to::equal_to()) {
EPOINT("Mock equal_to default constructor.");
}
}
equal_to(equal_to const& x)
: tag_(x.tag_)
{
SCOPE(equal_to::equal_to(equal_to)) {
EPOINT("Mock equal_to copy constructor.");
}
}
equal_to& operator=(equal_to const& x)
{
SCOPE(equal_to::operator=(equal_to)) {
EPOINT("Mock equal_to assign operator 1.");
tag_ = x.tag_;
EPOINT("Mock equal_to assign operator 2.");
}
return *this;
}
std::size_t operator()(object const& x1, object const& x2) const {
SCOPE(equal_to::operator()(object, object)) {
EPOINT("Mock equal_to function.");
}
switch(tag_) {
case 1:
return x1.tag1_ == x2.tag1_;
case 2:
return x1.tag2_ == x2.tag2_;
default:
return x1 == x2;
}
}
friend bool operator==(equal_to const& x1, equal_to const& x2) {
SCOPE(operator==(equal_to, equal_to)) {
EPOINT("Mock equal_to equality function.");
}
return x1.tag_ == x2.tag_;
}
friend bool operator!=(equal_to const& x1, equal_to const& x2) {
SCOPE(operator!=(equal_to, equal_to)) {
EPOINT("Mock equal_to inequality function.");
}
return x1.tag_ != x2.tag_;
}
};
// TODO: Need to track that same allocator is used to allocate, construct,
// deconstruct and destroy objects. Also, need to check that constructed
// objects are deconstructed (Boost.Test should take care of memory leaks
// for us).
template <class T>
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 <class U> struct rebind { typedef allocator<U> other; };
explicit allocator(int t = 0)
{
SCOPE(allocator::allocator()) {
EPOINT("Mock allocator default constructor.");
}
}
template <class Y> allocator(allocator<Y> 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<T*>(::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<T*>(::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<std::size_t>::max)();
}
};
template <class T>
inline bool operator==(allocator<T> const& x, allocator<T> 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 <class T>
inline bool operator!=(allocator<T> const& x, allocator<T> const& y)
{
//SCOPE(operator!=(allocator, allocator)) {
// EPOINT("Mock allocator inequality operator.");
//}
return false;
}
}
}
#endif

View File

@ -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
{

View File

@ -147,6 +147,12 @@ namespace test
template <class T>
class allocator
{
# ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
public:
# else
template <class> 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 <class U> struct rebind { typedef allocator<U> other; };
explicit allocator(int t = 0) {}
template <class Y> allocator(allocator<Y> const& x) {}
allocator(allocator const&) {}
explicit allocator(int t = 0) : tag_(t) {}
template <class Y> allocator(allocator<Y> 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<size_type>::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 <class T>
inline bool operator==(allocator<T> const& x, allocator<T> const& y)
{
return true;
}
template <class T>
inline bool operator!=(allocator<T> const& x, allocator<T> const& y)
{
return false;
}
}
#endif

View File

@ -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 : : : <define>BOOST_UNORDERED_SWAP_METHOD=1 : swap_tests1 ]
[ run swap_tests.cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=2 : swap_tests2 ]
[ run swap_tests.cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=3 : swap_tests3 ]
;

View File

@ -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<T> 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()

View File

@ -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 <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <algorithm>
#include "../objects/test.hpp"
#include "../helpers/random_values.hpp"
#include "../helpers/helpers.hpp"
template <class X>
void bucket_tests(X* = 0)
{
typedef typename X::size_type size_type;
typedef typename X::const_local_iterator const_local_iterator;
test::random_values<X> v(1000);
X x(v.begin(), v.end());
BOOST_TEST(x.bucket_count() < x.max_bucket_count());
std::cerr<<x.bucket_count()<<"<"<<x.max_bucket_count()<<"\n";
for(typename test::random_values<X>::const_iterator
it = v.begin(), end = v.end(); it != end; ++it)
{
size_type bucket = x.bucket(test::get_key<X>(*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<X>(*it) != test::get_key<X>(*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<int>*) 0);
bucket_tests((boost::unordered_multiset<int>*) 0);
bucket_tests((boost::unordered_map<int, int>*) 0);
bucket_tests((boost::unordered_multimap<int, int>*) 0);
bucket_tests((boost::unordered_set<test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
bucket_tests((boost::unordered_multiset<test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
bucket_tests((boost::unordered_map<test::object, test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
bucket_tests((boost::unordered_multimap<test::object, test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
return boost::report_errors();
}

View File

@ -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<T> 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<T> 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);
}

View File

@ -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<T> v(1000);
T x(v.begin(), v.end());
x.max_load_factor(x.load_factor() / 4);
T y(x);
test::unordered_equivalence_tester<T> 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<T> 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<T> equivalent(x);
equivalent(y);

View File

@ -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<Container>(*x.begin());
typename Container::key_type key = test::get_key<Container>(*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<test::object, test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
std::cerr<<"\nErase unordered_multimap<test::object,..>.\n";
erase_tests1((boost::unordered_multimap<test::object, test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
return boost::report_errors();
}

View File

@ -15,76 +15,94 @@
#include <iostream>
template <class Container>
void unique_insert_tests1(Container* = 0)
template <class X>
void unique_insert_tests1(X* = 0)
{
std::cerr<<"insert(value) tests for containers with unique keys.\n";
Container x;
test::ordered<Container> tracker = test::create_ordered(x);
X x;
test::ordered<X> tracker = test::create_ordered(x);
test::random_values<Container> v(1000);
for(typename test::random_values<Container>::iterator it = v.begin();
test::random_values<X> v(1000);
for(typename test::random_values<X>::iterator it = v.begin();
it != v.end(); ++it)
{
std::pair<typename Container::iterator, bool> r1 = x.insert(*it);
std::pair<typename test::ordered<Container>::iterator, bool> r2
typename X::size_type old_bucket_count = x.bucket_count();
float b = x.max_load_factor();
std::pair<typename X::iterator, bool> r1 = x.insert(*it);
std::pair<typename test::ordered<X>::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 <class Container>
void equivalent_insert_tests1(Container* = 0)
template <class X>
void equivalent_insert_tests1(X* = 0)
{
std::cerr<<"insert(value) tests for containers with equivalent keys.\n";
Container x;
test::ordered<Container> tracker = test::create_ordered(x);
X x;
test::ordered<X> tracker = test::create_ordered(x);
test::random_values<Container> v(1000);
for(typename test::random_values<Container>::iterator it = v.begin();
test::random_values<X> v(1000);
for(typename test::random_values<X>::iterator it = v.begin();
it != v.end(); ++it)
{
typename Container::iterator r1 = x.insert(*it);
typename test::ordered<Container>::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<X>::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 <class Container>
void insert_tests2(Container* = 0)
template <class X>
void insert_tests2(X* = 0)
{
typedef typename test::ordered<Container> tracker_type;
typedef typename Container::iterator iterator;
typedef typename Container::const_iterator const_iterator;
typedef typename test::ordered<X> 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<Container> v(1000);
for(typename test::random_values<Container>::iterator it = v.begin();
test::random_values<X> v(1000);
for(typename test::random_values<X>::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<Container> v(100);
for(typename test::random_values<Container>::iterator it = v.begin();
test::random_values<X> v(100);
for(typename test::random_values<X>::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<Container> v(1000);
for(typename test::random_values<Container>::iterator it = v.begin();
test::random_values<X> v(1000);
for(typename test::random_values<X>::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<Container> v(1000);
for(typename test::random_values<Container>::iterator it = v.begin();
test::random_values<X> v(1000);
for(typename test::random_values<X>::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<Container> v(1000);
test::random_values<X> v(1000);
x.insert(v.begin(), v.end());
check_container(x, v);
@ -162,6 +198,31 @@ void insert_tests2(Container* = 0)
}
}
template <class X>
void map_tests(X* = 0)
{
X x;
test::ordered<X> tracker = test::create_ordered(x);
test::random_values<X> v(1000);
for(typename test::random_values<X>::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<int>*) 0);
@ -184,5 +245,8 @@ int main()
insert_tests2((boost::unordered_map<test::object, test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
insert_tests2((boost::unordered_multimap<test::object, test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
map_tests((boost::unordered_map<int, int>*) 0);
map_tests((boost::unordered_map<test::object, test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
return boost::report_errors();
}

View File

@ -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 <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <boost/limits.hpp>
#include "../helpers/random_values.hpp"
template <class X>
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 <class X>
void insert_test(X*, float mlf)
{
X x;
x.max_load_factor(mlf);
float b = x.max_load_factor();
test::random_values<X> values(1000);
for(typename test::random_values<X>::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 <class X>
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<float>::min)());
if(std::numeric_limits<float>::has_infinity)
insert_test(ptr, std::numeric_limits<float>::infinity());
}
int main()
{
load_factor_tests((boost::unordered_set<int>*) 0);
load_factor_tests((boost::unordered_multiset<int>*) 0);
load_factor_tests((boost::unordered_map<int, int>*) 0);
load_factor_tests((boost::unordered_multimap<int, int>*) 0);
load_factor_insert_tests((boost::unordered_set<int>*) 0);
load_factor_insert_tests((boost::unordered_multiset<int>*) 0);
load_factor_insert_tests((boost::unordered_map<int, int>*) 0);
load_factor_insert_tests((boost::unordered_multimap<int, int>*) 0);
return boost::report_errors();
}

View File

@ -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 <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include <boost/detail/lightweight_test.hpp>
#include "../helpers/random_values.hpp"
#include "../helpers/tracker.hpp"
template <class X>
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 <class X>
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 <class X>
void rehash_test1(X* = 0)
{
test::random_values<X> v(1000);
test::ordered<X> 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 <class X>
void rehash_tests(X* ptr = 0)
{
rehash_empty_test1(ptr);
rehash_test1(ptr);
}
int main() {
rehash_tests((boost::unordered_set<int>*) 0);
rehash_tests((boost::unordered_multiset<int>*) 0);
rehash_tests((boost::unordered_map<int, int>*) 0);
rehash_tests((boost::unordered_multimap<int, int>*) 0);
return boost::report_errors();
}

View File

@ -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 <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include <boost/detail/lightweight_test.hpp>
#include "../objects/test.hpp"
#include "../helpers/random_values.hpp"
#include "../helpers/tracker.hpp"
#include "../helpers/invariants.hpp"
template <class X>
void swap_test_impl(X& x1, X& x2)
{
test::ordered<X> tracker1 = test::create_ordered(x1);
test::ordered<X> 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 <class X>
void swap_tests1(X* = 0)
{
{
X x;
swap_test_impl(x, x);
}
{
X x,y;
swap_test_impl(x, y);
}
{
test::random_values<X> v(1000);
X x, y(v.begin(), v.end());
swap_test_impl(x, y);
swap_test_impl(x, y);
}
{
test::random_values<X> 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 <class X>
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<X> 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<X> 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<X> 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<X> 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<X> 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<int>.\n";
swap_tests1((boost::unordered_set<int>*) 0);
std::cerr<<"\nErase unordered_multiset<int>.\n";
swap_tests1((boost::unordered_multiset<int>*) 0);
std::cerr<<"\nErase unordered_map<int>.\n";
swap_tests1((boost::unordered_map<int, int>*) 0);
std::cerr<<"\nErase unordered_multimap<int>.\n";
swap_tests1((boost::unordered_multimap<int, int>*) 0);
std::cerr<<"\nErase unordered_set<test::object,..>.\n";
swap_tests2((boost::unordered_set<test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
std::cerr<<"\nErase unordered_multiset<test::object,..>.\n";
swap_tests2((boost::unordered_multiset<test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
std::cerr<<"\nErase unordered_map<test::object,..>.\n";
swap_tests2((boost::unordered_map<test::object, test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
std::cerr<<"\nErase unordered_multimap<test::object,..>.\n";
swap_tests2((boost::unordered_multimap<test::object, test::object, test::hash, test::equal_to, test::allocator<test::object> >*) 0);
return boost::report_errors();
}