Add a test that unnecessary copies aren't made of objects inserted.

Make the exception tests track the allocator usage.
Speed up the exception tests a bit.


[SVN r3490]
This commit is contained in:
Daniel James 2006-12-03 23:08:17 +00:00
parent ec310f7b80
commit 8d8f9e1942
8 changed files with 251 additions and 25 deletions

View File

@ -5,7 +5,8 @@
import testing ;
alias framework : /boost/test//boost_unit_test_framework/<optimization>speed ;
#alias framework : /boost/test//boost_unit_test_framework/<optimization>speed ;
alias framework : /boost/test//boost_unit_test_framework ;
project unordered-test/exception-tests
: requirements

View File

@ -27,7 +27,7 @@ struct erase_test_base : public test::exception_base
void check(T const& x) const {
std::string scope(test::scope);
BOOST_CHECK(scope.find("hash::") != std::string::npos ||
HASH_CHECK(scope.find("hash::") != std::string::npos ||
scope.find("equal_to::") != std::string::npos ||
scope == "operator==(object, object)");

View File

@ -24,7 +24,7 @@ struct self_swap_base : public test::exception_base
std::string scope(test::scope);
#if BOOST_UNORDERED_SWAP_METHOD != 2
BOOST_CHECK(
HASH_CHECK(
scope == "hash::operator(hash)" ||
scope == "hash::operator=(hash)" ||
scope == "equal_to::operator(equal_to)" ||

View File

@ -16,6 +16,7 @@
#include <iostream>
#include <cstdlib>
#include "../helpers/fwd.hpp"
#include <map>
#define RUN_EXCEPTION_TESTS(test_seq, param_seq) \
BOOST_PP_SEQ_FOR_EACH_PRODUCT(RUN_EXCEPTION_TESTS_OP, (test_seq)(param_seq))
@ -37,7 +38,6 @@
}
#define SCOPE(scope_name) \
BOOST_ITEST_SCOPE(scope_name); \
for(::test::scope_guard unordered_test_guard( \
BOOST_STRINGIZE(scope_name)); \
!unordered_test_guard.dismissed(); \
@ -53,6 +53,8 @@
#define DISABLE_EXCEPTIONS \
::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(false)
#define HASH_CHECK(test) if(!(test)) BOOST_ERROR(BOOST_STRINGIZE(test))
namespace test {
static char const* scope = "";
bool exceptions_enabled = false;
@ -175,6 +177,161 @@ namespace test
{
namespace exception
{
namespace detail
{
// This annoymous namespace won't cause ODR violations as I won't
// be linking multiple translation units together. I'll probably
// move this into a cpp file before a full release, but for now it's
// the most convenient way.
namespace
{
template <class T>
struct malloc_allocator
{
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 malloc_allocator<U> other; };
malloc_allocator() {}
template <class Y> malloc_allocator(malloc_allocator<Y> const& x) {}
malloc_allocator(malloc_allocator const& x) {}
pointer address(reference r) { return &r; }
const_pointer address(const_reference r) { return &r; }
pointer allocate(size_type n) {
return static_cast<T*>(malloc(n * sizeof(T)));
}
pointer allocate(size_type n, const_pointer u) { return allocate(n); }
void deallocate(pointer p, size_type n) { free(p); }
void construct(pointer p, T const& t) { new(p) T(t); }
void destroy(pointer p) { p->~T(); }
size_type max_size() const {
return (std::numeric_limits<size_type>::max)();
}
bool operator==(malloc_allocator const& x) const { return true; }
bool operator!=(malloc_allocator const& x) const { return false; }
};
struct memory_area {
void const* start;
void const* end;
memory_area(void const* s, void const* e)
: start(s), end(e)
{
}
// This is a bit dodgy as it defines overlapping
// areas as 'equal', so this isn't a total ordering.
// But it is for non-overlapping memory regions - which
// is what'll be stored.
//
// All searches will be for areas entirely contained by
// a member of the set - so it should find the area that contains
// the region that is searched for.
bool operator<(memory_area const& other) const {
return end < other.start;
}
};
struct memory_track {
explicit memory_track(int tag = -1) :
tag_(tag) {}
int tag_;
};
typedef std::map<memory_area, memory_track, std::less<memory_area>,
malloc_allocator<std::pair<memory_area const, memory_track> > >
allocated_memory_type;
allocated_memory_type allocated_memory;
unsigned int count_allocators = 0;
unsigned int count_allocations = 0;
unsigned int count_constructions = 0;
}
void allocator_ref()
{
if(count_allocators == 0) {
count_allocations = 0;
count_constructions = 0;
allocated_memory.clear();
}
++count_allocators;
}
void allocator_unref()
{
HASH_CHECK(count_allocators > 0);
if(count_allocators > 0) {
--count_allocators;
if(count_allocators == 0) {
bool no_allocations_left = (count_allocations == 0);
bool no_constructions_left = (count_constructions == 0);
bool allocated_memory_empty = allocated_memory.empty();
// Clearing the data before the checks terminate the tests.
count_allocations = 0;
count_constructions = 0;
allocated_memory.clear();
HASH_CHECK(no_allocations_left);
HASH_CHECK(no_constructions_left);
HASH_CHECK(allocated_memory_empty);
}
}
}
void track_allocate(void *ptr, std::size_t n, std::size_t size, int tag)
{
if(n == 0) {
BOOST_ERROR("Allocating 0 length array.");
}
else {
++count_allocations;
allocated_memory[memory_area(ptr, (char*) ptr + n * size)] =
memory_track(tag);
}
}
void track_deallocate(void* ptr, std::size_t n, std::size_t size, int tag)
{
allocated_memory_type::iterator pos
= allocated_memory.find(memory_area(ptr, ptr));
if(pos == allocated_memory.end()) {
BOOST_ERROR("Deallocating unknown pointer.");
} else {
HASH_CHECK(pos->first.start == ptr);
HASH_CHECK(pos->first.end == (char*) ptr + n * size);
HASH_CHECK(pos->second.tag_ == tag);
allocated_memory.erase(pos);
}
HASH_CHECK(count_allocations > 0);
if(count_allocations > 0) --count_allocations;
}
void track_construct(void* ptr, std::size_t /*size*/, int tag)
{
++count_constructions;
}
void track_destroy(void* ptr, std::size_t /*size*/, int tag)
{
HASH_CHECK(count_constructions > 0);
if(count_constructions > 0) --count_constructions;
}
}
class object;
class hash;
class equal_to;
@ -379,6 +536,7 @@ namespace exception
class allocator
{
public:
int tag_;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
@ -389,32 +547,38 @@ namespace exception
template <class U> struct rebind { typedef allocator<U> other; };
explicit allocator(int = 0)
explicit allocator(int t = 0) : tag_(t)
{
SCOPE(allocator::allocator()) {
EPOINT("Mock allocator default constructor.");
}
detail::allocator_ref();
}
template <class Y> allocator(allocator<Y> const&)
template <class Y> allocator(allocator<Y> const& x) : tag_(x.tag_)
{
SCOPE(allocator::allocator()) {
EPOINT("Mock allocator template copy constructor.");
}
detail::allocator_ref();
}
allocator(allocator const&)
allocator(allocator const& x) : tag_(x.tag_)
{
SCOPE(allocator::allocator()) {
EPOINT("Mock allocator copy constructor.");
}
detail::allocator_ref();
}
~allocator() {}
~allocator() {
detail::allocator_unref();
}
allocator& operator=(allocator const&) {
allocator& operator=(allocator const& x) {
SCOPE(allocator::allocator()) {
EPOINT("Mock allocator assignment operator.");
tag_ = x.tag_;
}
return *this;
}
@ -446,6 +610,7 @@ namespace exception
ptr = (T*) malloc(n * sizeof(T));
if(!ptr) throw std::bad_alloc();
}
detail::track_allocate((void*) ptr, n, sizeof(T), tag_);
return pointer(ptr);
//return pointer(static_cast<T*>(::operator new(n * sizeof(T))));
@ -461,15 +626,17 @@ namespace exception
ptr = (T*) malloc(n * sizeof(T));
if(!ptr) throw std::bad_alloc();
}
detail::track_allocate((void*) ptr, n, sizeof(T), tag_);
return pointer(ptr);
//return pointer(static_cast<T*>(::operator new(n * sizeof(T))));
}
void deallocate(pointer p, size_type)
void deallocate(pointer p, size_type n)
{
//::operator delete((void*) p);
if(p) {
detail::track_deallocate((void*) p, n, sizeof(T), tag_);
using namespace std;
free(p);
}
@ -480,9 +647,13 @@ namespace exception
EPOINT("Mock allocator construct function.");
new(p) T(t);
}
detail::track_construct((void*) p, sizeof(T), tag_);
}
void destroy(pointer p) { p->~T(); }
void destroy(pointer p) {
detail::track_destroy((void*) p, sizeof(T), tag_);
p->~T();
}
size_type max_size() const {
SCOPE(allocator::construct(pointer, T)) {
@ -496,12 +667,12 @@ namespace exception
// two can throw. So they don't.
template <class T>
inline bool operator==(allocator<T> const&, allocator<T> const&)
inline bool operator==(allocator<T> const& x, allocator<T> const& y)
{
//SCOPE(operator==(allocator, allocator)) {
// EPOINT("Mock allocator equality operator.");
//}
return true;
return x.tag_ == y.tag_;
}
template <class T>
@ -510,9 +681,10 @@ namespace exception
//SCOPE(operator!=(allocator, allocator)) {
// EPOINT("Mock allocator inequality operator.");
//}
return false;
return x.tag_ != y.tag_;
}
}
}
#endif

View File

@ -154,6 +154,10 @@ namespace test
namespace detail
{
// This annoymous namespace won't cause ODR violations as I won't
// be linking multiple translation units together. I'll probably
// move this into a cpp file before a full release, but for now it's
// the most convenient way.
namespace {
struct memory_area {
void const* start;
@ -186,7 +190,8 @@ namespace test
int tag_;
};
std::map<memory_area, memory_track> allocated_memory;
typedef std::map<memory_area, memory_track> allocated_memory_type;
allocated_memory_type allocated_memory;
unsigned int count_allocators = 0;
unsigned int count_allocations = 0;
unsigned int count_constructions = 0;
@ -238,7 +243,7 @@ namespace test
void track_deallocate(void* ptr, std::size_t n, std::size_t size, int tag)
{
std::map<memory_area, memory_track>::iterator pos
allocated_memory_type::iterator pos
= allocated_memory.find(memory_area(ptr, ptr));
if(pos == allocated_memory.end()) {
BOOST_ERROR("Deallocating unknown pointer.");
@ -255,7 +260,7 @@ namespace test
void track_construct(void* ptr, std::size_t /*size*/, int tag)
{
std::map<memory_area, memory_track>::iterator pos
allocated_memory_type::iterator pos
= allocated_memory.find(memory_area(ptr, ptr));
if(pos == allocated_memory.end())
BOOST_ERROR("Constructing unknown pointer.");
@ -266,7 +271,7 @@ namespace test
void track_destroy(void* ptr, std::size_t /*size*/, int tag)
{
std::map<memory_area, memory_track>::iterator pos
allocated_memory_type::iterator pos
= allocated_memory.find(memory_area(ptr, ptr));
if(pos == allocated_memory.end())
BOOST_ERROR("Destroying unknown pointer.");
@ -349,7 +354,7 @@ namespace test
return tag_ != x.tag_;
}
};
template <class T>
bool equivalent_impl(allocator<T> const& x, allocator<T> const& y, test::derived_type) {
return x == y;

View File

@ -18,6 +18,7 @@ test-suite unordered-tests
[ run copy_tests.cpp ]
[ run assign_tests.cpp ]
[ run insert_tests.cpp ]
[ run unnecessary_copy_tests.cpp ]
[ run erase_tests.cpp ]
[ run erase_equiv_tests.cpp ]
[ run find_tests.cpp ]

View File

@ -74,11 +74,10 @@ void swap_tests2(X* ptr = 0)
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);
test::random_values<X> vx(100), vy(50);
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);
@ -87,7 +86,7 @@ void swap_tests2(X* ptr = 0)
#if BOOST_UNORDERED_SWAP_METHOD == 1
{
test::random_values<X> vx(1000), vy(1000);
test::random_values<X> vx(100), vy(50);
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 {
@ -97,15 +96,14 @@ void swap_tests2(X* ptr = 0)
}
#else
{
test::random_values<X> vx(1000), vy(1000);
test::random_values<X> vx(50), vy(100);
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);
test::random_values<X> vx(100), vy(100);
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);

View File

@ -0,0 +1,49 @@
// Copyright 2006 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include <boost/detail/lightweight_test.hpp>
struct count_copies
{
static int count;
count_copies() { ++count; }
count_copies(count_copies const& x) { ++count; }
private:
count_copies& operator=(count_copies const&);
};
std::size_t hash_value(count_copies const& x) {
return 0;
}
bool operator==(count_copies const& x, count_copies const& y) {
return true;
}
int count_copies::count;
template <class T>
void unnecessary_copy_test(T*)
{
count_copies::count = 0;
T x;
typename T::value_type a;
BOOST_TEST(count_copies::count == 1);
x.insert(a);
BOOST_TEST(count_copies::count == 2);
}
int main()
{
unnecessary_copy_test((boost::unordered_set<count_copies>*) 0);
unnecessary_copy_test((boost::unordered_multiset<count_copies>*) 0);
unnecessary_copy_test((boost::unordered_map<int, count_copies>*) 0);
unnecessary_copy_test((boost::unordered_multimap<int, count_copies>*) 0);
return boost::report_errors();
}