Add some tests for the unordered associative containers.

[SVN r2954]
This commit is contained in:
Daniel James
2006-05-17 17:19:16 +00:00
parent 1301d774e0
commit 1be8ab0d30
21 changed files with 2020 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
// 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)
#if !defined(BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER)
#define BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
#include <vector>
#include <algorithm>
#include "./metafunctions.hpp"
namespace test
{
template <class T>
bool equivalent(T const& x, T const& y) {
return x == y;
}
template <class T>
bool equivalent(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&) {
return true;
}
template <class Container>
class unordered_equivalence_tester
{
typename Container::size_type size_;
typename Container::hasher hasher_;
typename Container::key_equal key_equal_;
float max_load_factor_;
typedef typename non_const_value_type<Container>::type value_type;
std::vector<value_type> values_;
public:
unordered_equivalence_tester(Container const &x)
: size_(x.size()),
hasher_(x.hash_function()), key_equal_(x.key_eq()),
max_load_factor_(x.max_load_factor()),
values_()
{
// Can't initialise values_ straight from x because of Visual C++ 6
values_.reserve(x.size());
std::copy(x.begin(), x.end(), std::back_inserter(values_));
std::sort(values_.begin(), values_.end());
}
bool operator()(Container const& x) const
{
if(!((size_ == x.size()) &&
(test::equivalent(hasher_, x.hash_function())) &&
(test::equivalent(key_equal_, x.key_eq())) &&
(max_load_factor_ == x.max_load_factor()) &&
(values_.size() == x.size()))) return false;
std::vector<value_type> copy;
copy.reserve(x.size());
std::copy(x.begin(), x.end(), std::back_inserter(copy));
std::sort(copy.begin(), copy.end());
return(std::equal(values_.begin(), values_.end(), copy.begin()));
}
private:
unordered_equivalence_tester();
};
}
#endif

20
test/helpers/fwd.hpp Normal file
View File

@@ -0,0 +1,20 @@
// 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_FWD_HEADER)
#define BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER
namespace test
{
int generate(int const*);
char generate(char const*);
std::string generate(std::string*);
float generate(float const*);
template <class T1, class T2>
std::pair<T1, T2> generate(std::pair<T1, T2>*);
}
#endif

View File

@@ -0,0 +1,88 @@
// 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)
// A crude wrapper round Boost.Random to make life easier.
#if !defined(BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER)
#define BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER
#include <string>
#include <utility>
#include <boost/random/inversive_congruential.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/lagged_fibonacci.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/variate_generator.hpp>
#include "./fwd.hpp"
namespace test
{
typedef boost::hellekalek1995 integer_generator_type;
typedef boost::lagged_fibonacci607 real_generator_type;
template <class T>
struct generator
{
typedef T value_type;
value_type operator()()
{
return generate((T const*) 0);
}
};
inline int generate(int const*)
{
static boost::variate_generator<integer_generator_type, boost::uniform_int<> >
vg((integer_generator_type()), boost::uniform_int<>(0, 1000));
return vg();
}
inline char generate(char const*)
{
static boost::variate_generator<integer_generator_type, boost::uniform_int<char> >
vg((integer_generator_type()), boost::uniform_int<char>(32, 128));
return vg();
}
inline std::string generate(std::string const*)
{
using namespace std;
static test::generator<char> char_gen;
std::string result;
int length = rand() % 10;
for(int i = 0; i < length; ++i)
result += char_gen();
//std::generate_n(
// std::back_inserter(result),
// rand() % 10,
// char_gen);
return result;
}
float generate(float const*)
{
static boost::variate_generator<real_generator_type, boost::uniform_real<float> >
vg((real_generator_type()), boost::uniform_real<float>());
return vg();
}
template <class T1, class T2> std::pair<T1, T2> generate(
std::pair<T1, T2> const*)
{
static generator<T1> g1;
static generator<T2> g2;
return std::pair<T1, T2>(g1(), g2());
}
}
#endif

30
test/helpers/helpers.hpp Normal file
View File

@@ -0,0 +1,30 @@
// 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_HEADER)
#define BOOST_UNORDERED_TEST_HELPERS_HEADER
namespace test
{
template <class Container>
inline typename Container::key_type get_key(typename Container::key_type const& x)
{
return x;
}
template <class Container, class T>
inline typename Container::key_type get_key(std::pair<typename Container::key_type const, T> const& x)
{
return x.first;
}
template <class Container, class T>
inline typename Container::key_type get_key(std::pair<typename Container::key_type, T> const& x)
{
return x.first;
}
}
#endif

View File

@@ -0,0 +1,82 @@
// 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 header contains metafunctions/functions to get the equivalent
// associative container for an unordered container, and compare the contents.
#if !defined(BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER)
#define BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER
#include <set>
#include "./metafunctions.hpp"
#include "./helpers.hpp"
namespace test
{
template <class X>
void check_equivalent_keys(X const& x1)
{
typename X::key_equal eq = x1.key_eq();
typedef typename X::key_type key_type;
std::set<key_type> found_;
typename X::const_iterator it = x1.begin(), end = x1.end();
typename X::size_type size = 0;
while(it != end) {
// First test that the current key has not occured before, required
// to test either that keys are unique or that equivalent keys are
// adjacent. (6.3.1/6)
key_type key = get_key<X>(*it);
if(found_.find(key) != found_.end())
BOOST_ERROR("Elements with equivalent keys aren't adjacent.");
found_.insert(key);
// Iterate over equivalent keys, counting them.
unsigned int count = 0;
do {
++it;
++count;
++size;
} while(it != end && eq(get_key<X>(*it), key));
// If the container has unique keys, test that there's only one.
// Since the previous test makes sure that all equivalent keys are
// adjacent, this is all the equivalent keys - so the test is
// sufficient. (6.3.1/6 again).
if(test::has_unique_keys<X>::value && count != 1)
BOOST_ERROR("Non-unique key.");
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;
}
}
};
// Finally, check that size matches up.
if(x1.size() != size)
BOOST_ERROR("x1.size() doesn't match actual size.");
if(static_cast<float>(size) / x1.bucket_count() != x1.load_factor())
BOOST_ERROR("x1.load_factor() doesn't match actual load_factor.");
}
}
#endif

View File

@@ -0,0 +1,103 @@
// Copyright Daniel James 2005. 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_METAFUNCTIONS_HEADER)
#define BOOST_UNORDERED_TEST_HELPERS_METAFUNCTIONS_HEADER
#include <boost/config.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
namespace test
{
/*
struct unordered_set_type { char x[100]; };
struct unordered_multiset_type { char x[200]; };
struct unordered_map_type { char x[300]; };
struct unordered_multimap_type { char x[400]; };
template <class V, class H, class P, class A>
unordered_set_type container_type(
boost::unordered_set<V, H, P, A> const*);
template <class V, class H, class P, class A>
unordered_multiset_type container_type(
boost::unordered_multiset<V, H, P, A> const*);
template <class K, class M, class H, class P, class A>
unordered_map_type container_type(
boost::unordered_map<K, M, H, P, A> const*);
template <class K, class M, class H, class P, class A>
unordered_multimap_type container_type(
boost::unordered_multimap<K, M, H, P, A> const*);
*/
template <class Container>
struct is_set
: public boost::is_same<
typename Container::key_type,
typename Container::value_type> {};
template <class Container>
struct is_map
: public boost::mpl::not_<is_set<Container> > {};
struct yes_type { char x[100]; };
struct no_type { char x[200]; };
template <class V, class H, class P, class A>
yes_type has_unique_key_impl(
boost::unordered_set<V, H, P, A> const*);
template <class V, class H, class P, class A>
no_type has_unique_key_impl(
boost::unordered_multiset<V, H, P, A> const*);
template <class K, class M, class H, class P, class A>
yes_type has_unique_key_impl(
boost::unordered_map<K, M, H, P, A> const*);
template <class K, class M, class H, class P, class A>
no_type has_unique_key_impl(
boost::unordered_multimap<K, M, H, P, A> const*);
template <class Container>
struct has_unique_keys
{
BOOST_STATIC_CONSTANT(bool, value =
sizeof(has_unique_key_impl((Container const*)0))
== sizeof(yes_type));
};
template <class Container>
struct has_equivalent_keys
{
BOOST_STATIC_CONSTANT(bool, value =
sizeof(has_unique_key_impl((Container const*)0))
== sizeof(no_type));
};
// Non Const Value Type
template <class Container>
struct map_non_const_value_type
{
typedef std::pair<
typename Container::key_type,
typename Container::mapped_type> type;
};
template <class Container>
struct non_const_value_type
: boost::mpl::eval_if<is_map<Container>,
map_non_const_value_type<Container>,
boost::mpl::identity<typename Container::value_type> >
{
};
}
#endif

View File

@@ -0,0 +1,29 @@
// 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)
#if !defined(BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER)
#define BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER
#include <vector>
#include <algorithm>
#include "./generators.hpp"
#include "./metafunctions.hpp"
namespace test
{
template <class X>
struct random_values
: public std::vector<typename non_const_value_type<X>::type>
{
random_values(int count) {
typedef typename non_const_value_type<X>::type value_type;
static test::generator<value_type> gen;
this->reserve(count);
std::generate_n(std::back_inserter(*this), count, gen);
}
};
}
#endif

146
test/helpers/tracker.hpp Normal file
View File

@@ -0,0 +1,146 @@
// 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 header contains metafunctions/functions to get the equivalent
// associative container for an unordered container, and compare the contents.
#if !defined(BOOST_UNORDERED_TEST_HELPERS_TRACKER_HEADER)
#define BOOST_UNORDERED_TEST_HELPERS_TRACKER_HEADER
#include <set>
#include <map>
#include <vector>
#include <iterator>
#include <algorithm>
#include <boost/mpl/if.hpp>
#include <boost/mpl/eval_if.hpp>
#include "../objects/fwd.hpp"
#include "./metafunctions.hpp"
#include "./helpers.hpp"
namespace test
{
template <class X>
struct equals_to_compare
{
typedef std::less<typename X::first_argument_type> type;
};
template <>
struct equals_to_compare<test::equal_to>
{
typedef test::less type;
};
template <class X1, class X2>
void compare_range(X1 const& x1, X2 const& x2)
{
typedef typename non_const_value_type<X1>::type value_type;
std::vector<value_type> values1, values2;
values1.reserve(x1.size());
values2.reserve(x2.size());
std::copy(x1.begin(), x1.end(), std::back_inserter(values1));
std::copy(x2.begin(), x2.end(), std::back_inserter(values2));
std::sort(values1.begin(), values1.end());
std::sort(values2.begin(), values2.end());
BOOST_TEST(values1 == values2);
}
template <class X1, class X2, class T>
void compare_pairs(X1 const& x1, X2 const& x2, T*)
{
std::vector<T> values1, values2;
values1.reserve(std::distance(x1.first, x1.second));
values2.reserve(std::distance(x2.first, x2.second));
std::copy(x1.first, x1.second, std::back_inserter(values1));
std::copy(x2.first, x2.second, std::back_inserter(values2));
std::sort(values1.begin(), values1.end());
std::sort(values2.begin(), values2.end());
BOOST_TEST(values1 == values2);
}
template <class X>
struct ordered_set
: public boost::mpl::if_<
test::has_unique_keys<X>,
std::set<typename X::value_type,
typename equals_to_compare<typename X::key_equal>::type>,
std::multiset<typename X::value_type,
typename equals_to_compare<typename X::key_equal>::type>
> {};
template <class X>
struct ordered_map
: public boost::mpl::if_<
test::has_unique_keys<X>,
std::map<typename X::key_type, typename X::mapped_type,
typename equals_to_compare<typename X::key_equal>::type>,
std::multimap<typename X::key_type, typename X::mapped_type,
typename equals_to_compare<typename X::key_equal>::type>
> {};
template <class X>
struct ordered_base
: public boost::mpl::eval_if<
test::is_set<X>,
test::ordered_set<X>,
test::ordered_map<X> >
{
};
template <class X>
class ordered : public ordered_base<X>::type
{
typedef typename ordered_base<X>::type base;
public:
typedef typename base::key_compare key_compare;
ordered()
: base()
{}
explicit ordered(key_compare const& compare)
: base(compare)
{}
void compare(X const& x)
{
compare_range(x, *this);
}
void compare_key(X const& x, typename X::value_type const& val)
{
compare_pairs(
x.equal_range(get_key<X>(val)),
this->equal_range(get_key<X>(val)),
(typename non_const_value_type<X>::type*) 0
);
}
};
template <class Equals>
typename equals_to_compare<Equals>::type create_compare(
Equals equals)
{
return typename equals_to_compare<Equals>::type();
}
template <class X>
ordered<X> create_ordered(X const& container)
{
return ordered<X>(create_compare(container.key_eq()));
}
template <class X1, class X2>
void check_container(X1 const& container, X2 const& values)
{
ordered<X1> tracker = create_ordered(container);
tracker.insert(values.begin(), values.end());
tracker.compare(container);
}
}
#endif