From c1fa6eddec24d5feeefaa99f7171bc1fcd12bb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Wed, 22 Apr 2026 01:11:42 +0200 Subject: [PATCH] Ported hub api tests to nest --- test/nest_api_test.cpp | 913 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 913 insertions(+) create mode 100644 test/nest_api_test.cpp diff --git a/test/nest_api_test.cpp b/test/nest_api_test.cpp new file mode 100644 index 0000000..03df26d --- /dev/null +++ b/test/nest_api_test.cpp @@ -0,0 +1,913 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2025-2026. 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) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// +// +// C++03-compatible port of the hub test_api.cpp adapted to exercise +// boost::container::nest (API-compatible with boost::container::hub). +// +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::container; + +////////////////////////////////////////////////////////////////////////////// +// +// Utilities +// +////////////////////////////////////////////////////////////////////////////// + +template +std::vector make_range(std::size_t n) +{ + std::vector res; + T i = T(); + while(n--) { + res.push_back(i); + i += T(1); + } + return res; +} + +//! Erases every element whose underlying integer value is a multiple of 7, +//! so the container ends up with a non-trivial punctured bitmask pattern. +template +void puncture(Container& x) +{ + typedef typename Container::iterator iterator; + for(iterator first = x.begin(); first != x.end(); ) { + if(!((int)(*first) % 7)) { + first = x.erase(first); + } + else { + ++first; + } + } +} + +template +void test_equal(const Container1& x, const Container2& y) +{ + BOOST_TEST_EQ(x.size(), (typename Container1::size_type)y.size()); + BOOST_TEST(std::equal(x.begin(), x.end(), y.begin())); +} + +template +void test_traversal(Iterator first, Iterator last, const Mirror& data) +{ + std::size_t n = 0; + for(Iterator it = first; it != last; ++it, ++n) { + BOOST_TEST(*it == data[n]); + BOOST_TEST((first == it) == (0 == n)); + BOOST_TEST((first != it) == (0 != n)); + + Iterator it1 = it; + Iterator it2 = ++it1; + Iterator it3 = --it2; + Iterator it4 = it3++; + Iterator it5 = it3--; + Iterator it_next = it; ++it_next; + BOOST_TEST(it1 == it_next); + BOOST_TEST(it2 == it); + BOOST_TEST(it3 == it); + BOOST_TEST(it4 == it); + BOOST_TEST(it5 == it_next); + } +} + +template +bool is_sorted_cxx03(FwdIt first, FwdIt last) +{ + if(first == last) return true; + FwdIt next = first; + ++next; + for(; next != last; ++first, ++next) { + if(*next < *first) return false; + } + return true; +} + +template +bool is_sorted_cxx03(FwdIt first, FwdIt last, Compare comp) +{ + if(first == last) return true; + FwdIt next = first; + ++next; + for(; next != last; ++first, ++next) { + if(comp(*next, *first)) return false; + } + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Stateful allocator: equal-comparison depends on state. POC* traits are +// left at default (no propagation), so it can trigger unequal-allocator +// code paths during move/swap. +// +////////////////////////////////////////////////////////////////////////////// + +template +class stateful_allocator +{ + public: + typedef T value_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + template + struct rebind { typedef stateful_allocator other; }; + + int state; + + stateful_allocator() : state(0) {} + explicit stateful_allocator(int s) : state(s) {} + + template + stateful_allocator(const stateful_allocator& o) : state(o.state) {} + + T* allocate(std::size_t n) + { return static_cast(::operator new(n * sizeof(T))); } + + void deallocate(T* p, std::size_t) { ::operator delete(p); } + + size_type max_size() const + { return static_cast(-1) / sizeof(T); } + + friend bool operator==(const stateful_allocator& a, const stateful_allocator& b) + { return a.state == b.state; } + friend bool operator!=(const stateful_allocator& a, const stateful_allocator& b) + { return a.state != b.state; } +}; + +template<> +class stateful_allocator +{ + public: + typedef void value_type; + typedef void* pointer; + typedef const void* const_pointer; + + template + struct rebind { typedef stateful_allocator other; }; + + int state; + + stateful_allocator() : state(0) {} + explicit stateful_allocator(int s) : state(s) {} + + template + stateful_allocator(const stateful_allocator& o) : state(o.state) {} +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Rebind helpers: produce nest> from nest> +// +////////////////////////////////////////////////////////////////////////////// + +//! Uses Nest::allocator_type (rather than the 2nd template argument) to cope +//! with nests instantiated with the default allocator argument (which maps +//! to boost::container::new_allocator internally). +template +struct rebind_value_type +{ + private: + typedef typename Nest_::allocator_type allocator_type; + typedef typename allocator_type::template rebind::other other_alloc; + public: + typedef boost::container::nest type; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// tracked: tracks whether the last copy/move operation was copy or move +// +////////////////////////////////////////////////////////////////////////////// + +enum tracked_provenance { ab_ovo = 0, from_copy, from_move }; + +template +struct tracked +{ + BOOST_COPYABLE_AND_MOVABLE(tracked) + + public: + T x; + tracked_provenance origin; + tracked_provenance last_op; + + tracked() + : x(), origin(ab_ovo), last_op(ab_ovo) {} + + explicit tracked(const T& x_) + : x(x_), origin(from_copy), last_op(ab_ovo) {} + + tracked(const tracked& o) + : x(o.x), origin(o.origin), last_op(from_copy) {} + + tracked(BOOST_RV_REF(tracked) o) + : x(o.x), origin(o.origin), last_op(from_move) + {} + + tracked& operator=(BOOST_COPY_ASSIGN_REF(tracked) o) + { + x = o.x; + origin = o.origin; + last_op = from_copy; + return *this; + } + + tracked& operator=(BOOST_RV_REF(tracked) o) + { + x = o.x; + origin = o.origin; + last_op = from_move; + return *this; + } + + friend bool operator==(const tracked& a, const tracked& b) + { return a.x == b.x; } + friend bool operator!=(const tracked& a, const tracked& b) + { return !(a == b); } +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Namespace-scope functors (C++03 does not allow local types as template +// arguments, so everything used with .visit / find_if / erase_if lives here) +// +////////////////////////////////////////////////////////////////////////////// + +template +struct even_pred +{ + bool operator()(const T& v) const { return (int)(v) % 2 == 0; } +}; + +template +struct accum_functor +{ + unsigned int* res; + accum_functor(unsigned int& r) : res(&r) {} + void operator()(T& v) const { *res += (unsigned int)v; } + void operator()(const T& v) const { *res += (unsigned int)v; } +}; + +template +struct bounded_accum_functor +{ + unsigned int* res; + std::size_t* n; + bounded_accum_functor(unsigned int& r, std::size_t& nn) : res(&r), n(&nn) {} + + bool operator()(T& v) const + { + if(*n == 0) return false; + --(*n); + *res += (unsigned int)v; + return true; + } + bool operator()(const T& v) const + { + if(*n == 0) return false; + --(*n); + *res += (unsigned int)v; + return true; + } +}; + +template +struct negated_bounded_accum +{ + bounded_accum_functor f; + negated_bounded_accum(const bounded_accum_functor& f_) : f(f_) {} + bool operator()(T& v) const { return !f(v); } + bool operator()(const T& v) const { return !f(v); } +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Test sub-suites (templated on the nest type) +// +////////////////////////////////////////////////////////////////////////////// + +template +void test_global_erase(const R& rng, const typename Nest::allocator_type& al) +{ + typedef typename Nest::value_type value_type; + typedef typename Nest::size_type size_type; + + Nest x(al); + const value_type* odd_value_ptr = 0; + for(typename R::const_iterator it = rng.begin(); it != rng.end(); ++it) { + if((int)(*it) % 2 != 0) { odd_value_ptr = &*it; break; } + } + BOOST_TEST(odd_value_ptr != 0); + const value_type& odd_value = *odd_value_ptr; + + BOOST_TEST_EQ(erase(x, odd_value), (size_type)0); + BOOST_TEST_EQ(x.size(), (size_type)0); + BOOST_TEST_EQ(erase_if(x, even_pred()), (size_type)0); + BOOST_TEST_EQ(x.size(), (size_type)0); + + x.insert(rng.begin(), rng.end()); + size_type s = x.size(); + size_type n = erase(x, odd_value); + size_type rng_odd_count = + (size_type)std::count(rng.begin(), rng.end(), odd_value); + BOOST_TEST_EQ(n, rng_odd_count); + BOOST_TEST_EQ( + (size_type)std::count(x.begin(), x.end(), odd_value), (size_type)0); + BOOST_TEST_EQ(x.size(), s - n); + + s = x.size(); + n = erase_if(x, even_pred()); + size_type rng_even_count = (size_type)std::count_if( + rng.begin(), rng.end(), even_pred()); + BOOST_TEST_EQ(n, rng_even_count); + BOOST_TEST_EQ((size_type)std::count_if( + x.begin(), x.end(), even_pred()), (size_type)0); + BOOST_TEST_EQ(x.size(), s - n); +} + +template +void test_construct_copy_destroy(const typename Nest::allocator_type& al) +{ + typedef typename Nest::value_type value_type; + typedef typename Nest::size_type size_type; + + std::vector rng = make_range(200); + std::vector zeros(70, value_type()); + std::vector repeated(100, rng[10]); + + { + Nest x(al); + BOOST_TEST(x.empty()); + } + { + Nest x(zeros.size(), al); + test_equal(x, zeros); + } + { + Nest x(repeated.size(), repeated.front(), al); + test_equal(x, repeated); + } + { + // [sequence.reqmts/69.1]: (20, 20) picks the count/value overload + Nest x((size_type)20, (value_type)20, al); + BOOST_TEST_EQ(x.size(), (size_type)20); + } + { + Nest x(rng.begin(), rng.end(), al); + test_equal(x, rng); + } + { + const Nest x(rng.begin(), rng.end(), al); + Nest y(x); + Nest z(y, al); + test_equal(x, y); + test_equal(x, z); + } + { + Nest x(rng.begin(), rng.end(), al); + Nest y(boost::move(x)); + BOOST_TEST(x.empty()); + test_equal(y, rng); + + Nest z(boost::move(y), al); + BOOST_TEST(y.empty()); + test_equal(z, rng); + } + { + Nest x(rng.begin(), rng.end(), al); + Nest y(al); + Nest& ry = (y = x); + BOOST_TEST_EQ(&ry, &y); + test_equal(x, y); + } + { + Nest x(rng.begin(), rng.end(), al); + Nest y(al); + Nest& ry = (y = boost::move(x)); + BOOST_TEST_EQ(&ry, &y); + BOOST_TEST(x.empty()); + test_equal(y, rng); + } + { + Nest x(rng.begin(), rng.begin() + std::ptrdiff_t(rng.size() / 2), al); + puncture(x); + x.assign(rng.begin(), rng.end()); + test_equal(x, rng); + } + { + Nest x(zeros.size(), al); + puncture(x); + x.assign(repeated.size(), repeated[0]); + test_equal(x, repeated); + } + { + const Nest x(al); + BOOST_TEST(x.get_allocator() == al); + } +} + +template +void test_iterators(const typename Nest::allocator_type& al) +{ + typedef typename Nest::value_type value_type; + typedef typename Nest::iterator iterator; + typedef typename Nest::const_iterator const_iterator; + + std::vector rng = make_range(200); + + std::vector data = rng; + Nest x(data.begin(), data.end(), al); + const Nest& cx = x; + puncture(data); + puncture(x); + + BOOST_TEST(x.rbegin().base() == x.end()); + BOOST_TEST(cx.rbegin().base() == cx.end()); + BOOST_TEST(x.rend().base() == x.begin()); + BOOST_TEST(cx.rend().base() == cx.begin()); + BOOST_TEST(cx.cbegin() == cx.begin()); + BOOST_TEST(cx.cend() == cx.end()); + BOOST_TEST(cx.crbegin() == cx.rbegin()); + BOOST_TEST(cx.crend() == cx.rend()); + + iterator it = x.begin(); + iterator it2 = x.end(); + const_iterator cit = it; + BOOST_TEST(cit == it); + cit = it2; + BOOST_TEST(cit == it2); + it = it2; + BOOST_TEST(it == it2); + + test_traversal(x.begin(), x.end(), data); + test_traversal(x.cbegin(), x.cend(), data); +} + +template +void test_capacity(const typename Nest::allocator_type& al) +{ + typedef typename Nest::value_type value_type; + typedef typename Nest::size_type size_type; + + std::vector rng = make_range(200); + + Nest x(al); + const Nest& cx = x; + + x.reserve(1000); + x.insert(rng.begin(), rng.end()); + BOOST_TEST(!cx.empty()); + BOOST_TEST_EQ(cx.size(), (size_type)rng.size()); + BOOST_TEST_GT(cx.max_size(), (size_type)0); + BOOST_TEST_GE(cx.capacity(), (size_type)1000); + + Nest x2 = x; + x.shrink_to_fit(); + test_equal(x, x2); + BOOST_TEST_EQ(cx.size(), (size_type)rng.size()); + BOOST_TEST_GE(cx.capacity(), (size_type)rng.size()); + + size_type c = cx.capacity(); + x.reserve(c + 1000); + x.trim_capacity(c + 500); + BOOST_TEST_LT(cx.capacity(), c + 1000); + BOOST_TEST_GE(cx.capacity(), c + 500); + x.trim_capacity(); + BOOST_TEST_EQ(cx.capacity(), c); + test_equal(x, x2); + + if(cx.max_size() < (size_type)(-1)) { + BOOST_TEST_THROWS(x.reserve(cx.max_size() + 1), boost::container::length_error); + } +} + +template +void test_modifiers_provenance(const typename Nest::allocator_type& al) +{ + typedef typename Nest::value_type value_type; + typedef tracked tracked_value_type; + typedef typename rebind_value_type::type tracked_nest; + typedef typename tracked_nest::iterator tr_iterator; + + typename tracked_nest::allocator_type tal(al); + tracked_nest x(tal); + tracked_value_type v((value_type())); + tr_iterator it; + + it = x.emplace(v); + BOOST_TEST(it->x == v.x); + BOOST_TEST_EQ((int)it->last_op, (int)from_copy); + + v.x += value_type(1); + { + tracked_value_type tmp(v); + it = x.emplace(boost::move(tmp)); + } + BOOST_TEST(it->x == v.x); + BOOST_TEST_EQ((int)it->last_op, (int)from_move); + + v.x += value_type(1); + it = x.emplace_hint(x.cbegin(), v); + BOOST_TEST(it->x == v.x); + BOOST_TEST_EQ((int)it->last_op, (int)from_copy); + + v.x += value_type(1); + { + tracked_value_type tmp(v); + it = x.emplace_hint(x.cbegin(), boost::move(tmp)); + } + BOOST_TEST(it->x == v.x); + BOOST_TEST_EQ((int)it->last_op, (int)from_move); + + v.x += value_type(1); + it = x.insert(v); + BOOST_TEST(it->x == v.x); + BOOST_TEST_EQ((int)it->last_op, (int)from_copy); + + v.x += value_type(1); + { + tracked_value_type tmp(v); + it = x.insert(boost::move(tmp)); + } + BOOST_TEST(it->x == v.x); + BOOST_TEST_EQ((int)it->last_op, (int)from_move); + + v.x += value_type(1); + it = x.insert(x.cbegin(), v); + BOOST_TEST(it->x == v.x); + BOOST_TEST_EQ((int)it->last_op, (int)from_copy); + + v.x += value_type(1); + { + tracked_value_type tmp(v); + it = x.insert(x.cbegin(), boost::move(tmp)); + } + BOOST_TEST(it->x == v.x); + BOOST_TEST_EQ((int)it->last_op, (int)from_move); +} + +template +void test_modifiers(const typename Nest::allocator_type& al) +{ + typedef typename Nest::value_type value_type; + typedef typename Nest::size_type size_type; + typedef typename Nest::iterator iterator; + typedef typename Nest::const_iterator const_iterator; + + std::vector rng = make_range(200); + + test_modifiers_provenance(al); + + { + Nest x(al); + x.insert(rng.begin(), rng.begin()); + BOOST_TEST(x.empty()); + + x.insert(rng.begin(), rng.end()); + test_equal(x, rng); + } + { + Nest x(al); + x.assign((size_type)1, (value_type)1); + x.assign((size_type)0, (value_type)1); + BOOST_TEST_EQ(x.size(), (size_type)0); + + x.assign((size_type)65, (value_type)1); + x.assign((size_type)65, (value_type)1); + BOOST_TEST_EQ(x.size(), (size_type)65); + } + { + Nest x(rng.begin(), rng.end(), al); + + iterator it = x.erase(x.cbegin()); + BOOST_TEST_EQ(x.size(), (size_type)(rng.size() - 1)); + BOOST_TEST(*it == rng[1]); + + it = x.erase(x.cend(), x.cend()); + BOOST_TEST_EQ(x.size(), (size_type)(rng.size() - 1)); + BOOST_TEST(it == x.cend()); + + const_iterator last_pos = x.cend(); + --last_pos; + it = x.erase(last_pos, last_pos); + BOOST_TEST_EQ(x.size(), (size_type)(rng.size() - 1)); + const_iterator last_pos2 = x.cend(); + --last_pos2; + BOOST_TEST(it == last_pos2); + + const_iterator mid = x.cbegin(); + std::advance(mid, (std::ptrdiff_t)(x.size() / 2)); + it = x.erase(mid, x.cend()); + BOOST_TEST_EQ(x.size(), (size_type)((rng.size() - 1) / 2)); + BOOST_TEST(it == x.cend()); + } + { + Nest x0(rng.begin(), rng.end(), al); + Nest y0(rng.begin(), rng.begin() + std::ptrdiff_t(rng.size() / 2), al); + Nest x(x0), y(y0); + + x.swap(x); + test_equal(x, x0); + + swap(x, x); + test_equal(x, x0); + + x.swap(y); + test_equal(x, y0); + test_equal(y, x0); + + swap(x, y); + test_equal(x, x0); + test_equal(y, y0); + } + { + Nest x(rng.begin(), rng.end(), al); + x.clear(); + BOOST_TEST(x.empty()); + x.clear(); + BOOST_TEST(x.empty()); + } +} + +template +void test_hive_operations(const typename Nest::allocator_type& al) +{ + typedef typename Nest::value_type value_type; + typedef typename Nest::size_type size_type; + typedef typename Nest::pointer pointer; (void)sizeof(pointer); + typedef typename Nest::const_pointer const_pointer; + typedef typename Nest::iterator iterator; + typedef typename Nest::const_iterator const_iterator; + + std::vector rng = make_range(200); + + { + Nest x(rng.begin(), rng.end(), al); + Nest y(x); + + iterator it = y.begin(); + y.reserve(y.capacity() + 100); + x.splice(y); + BOOST_TEST_EQ(x.size(), (size_type)(2 * rng.size())); + BOOST_TEST(y.empty()); + BOOST_TEST_GE(y.capacity(), (size_type)100); + BOOST_TEST(*it == rng[0]); + + y.splice(boost::move(x)); + BOOST_TEST(x.empty()); + BOOST_TEST_EQ(y.size(), (size_type)(2 * rng.size())); + BOOST_TEST(*it == rng[0]); + } + { + Nest x(al); + for(typename std::vector::const_iterator it = rng.begin(); + it != rng.end(); ++it) { + x.insert(*it); + x.insert(*it); + } + + size_type removed = x.unique(std::equal_to()); + BOOST_TEST_EQ(removed, (size_type)rng.size()); + BOOST_TEST_EQ(x.size(), (size_type)rng.size()); + } + { + Nest x(al); + x.insert(rng.begin(), rng.end()); + x.insert(rng.begin(), rng.end()); + + x.sort(std::less()); + BOOST_TEST(is_sorted_cxx03(x.begin(), x.end())); + + x.sort(std::greater()); + BOOST_TEST(is_sorted_cxx03(x.rbegin(), x.rend())); + } + { + Nest x(rng.begin(), rng.end(), al); + const Nest& cx = x; + + for(const_iterator it = x.cbegin(); it != x.cend(); ++it) { + const_pointer p = boost::pointer_traits::pointer_to(*it); + BOOST_TEST(x.get_iterator(p) == it); + BOOST_TEST(cx.get_iterator(p) == it); + } + } +} + +template +void test_visitation(const typename Nest::allocator_type& al) +{ + typedef typename Nest::value_type value_type; + typedef typename Nest::iterator iterator; + typedef typename Nest::const_iterator const_iterator; + + std::vector rng = make_range(200); + + // visit / visit_all + { + Nest x(rng.begin(), rng.end(), al); + const Nest& cx = x; + puncture(x); + + for(std::size_t i = 0; i < x.size() / 2; ++i) { + iterator first = x.begin(); + iterator last = x.end(); + std::advance(first, (std::ptrdiff_t)i); + std::advance(last, -(std::ptrdiff_t)i); + + const_iterator cfirst = x.cbegin(); + const_iterator clast = x.cend(); + std::advance(cfirst, (std::ptrdiff_t)i); + std::advance(clast, -(std::ptrdiff_t)i); + + unsigned int res = 0; + x.visit(first, last, accum_functor(res)); + unsigned int res1 = res; + + res = 0; + cx.visit(cfirst, clast, accum_functor(res)); + unsigned int res2 = res; + + res = 0; + std::for_each(first, last, accum_functor(res)); + unsigned int res3 = res; + + BOOST_TEST_EQ(res1, res3); + BOOST_TEST_EQ(res2, res3); + } + + unsigned int res = 0; + x.visit_all(accum_functor(res)); + unsigned int res1 = res; + + res = 0; + cx.visit_all(accum_functor(res)); + unsigned int res2 = res; + + res = 0; + std::for_each(x.begin(), x.end(), accum_functor(res)); + unsigned int res3 = res; + + BOOST_TEST_EQ(res1, res3); + BOOST_TEST_EQ(res2, res3); + } + + // visit_while / visit_all_while + { + Nest x(rng.begin(), rng.end(), al); + const Nest& cx = x; + puncture(x); + + for(std::size_t i = 0; i <= x.size(); ++i) { + iterator first = x.begin(); + const_iterator cfirst = x.cbegin(); + std::advance(first, (std::ptrdiff_t)i); + std::advance(cfirst, (std::ptrdiff_t)i); + + unsigned int res = 0; + std::size_t n = (std::size_t)std::distance(first, x.end()) / 2; + iterator it1 = x.visit_while( + first, x.end(), bounded_accum_functor(res, n)); + unsigned int res1 = res; + + res = 0; + n = (std::size_t)std::distance(first, x.end()) / 2; + const_iterator it2 = cx.visit_while( + cfirst, cx.end(), bounded_accum_functor(res, n)); + unsigned int res2 = res; + + res = 0; + n = (std::size_t)std::distance(first, x.end()) / 2; + bounded_accum_functor baf(res, n); + iterator it3 = std::find_if( + first, x.end(), negated_bounded_accum(baf)); + unsigned int res3 = res; + + BOOST_TEST(it1 == it3); + BOOST_TEST_EQ(res1, res3); + BOOST_TEST(it2 == it3); + BOOST_TEST_EQ(res2, res3); + } + + unsigned int res = 0; + std::size_t n = x.size(); + iterator it1 = x.visit_all_while(bounded_accum_functor(res, n)); + unsigned int res1 = res; + + res = 0; + n = x.size(); + const_iterator it2 = cx.visit_all_while(bounded_accum_functor(res, n)); + unsigned int res2 = res; + + res = 0; + n = x.size(); + bounded_accum_functor baf(res, n); + iterator it3 = std::find_if( + x.begin(), x.end(), negated_bounded_accum(baf)); + unsigned int res3 = res; + + BOOST_TEST(it1 == it3); + BOOST_TEST_EQ(res1, res3); + BOOST_TEST(it2 == it3); + BOOST_TEST_EQ(res2, res3); + } +} + +////////////////////////////////////////////////////////////////////////////// +// +// Generic test driver +// +////////////////////////////////////////////////////////////////////////////// + +template +void test_all(const typename Nest::allocator_type& al) +{ + typedef typename Nest::value_type value_type; + + std::vector rng = make_range(200); + + test_construct_copy_destroy(al); + test_iterators(al); + test_capacity(al); + test_modifiers(al); + test_hive_operations(al); + test_visitation(al); + test_global_erase(rng, al); +} + +template +void test_all_default_alloc() +{ + typename Nest::allocator_type al; + test_all(al); +} + +//! Extra check: move-construction with unequal (stateful) allocators must +//! fall back to element-by-element move. +template +void test_move_with_unequal_allocators() +{ + typedef typename Nest::value_type value_type; + typedef typename Nest::allocator_type allocator_type; + + std::vector rng = make_range(200); + + Nest x(rng.begin(), rng.end(), allocator_type(0)); + Nest y(boost::move(x), allocator_type(1)); + BOOST_TEST_EQ(x.get_allocator().state, 0); + BOOST_TEST(x.empty()); + BOOST_TEST_EQ(y.get_allocator().state, 1); + test_equal(y, rng); +} + +////////////////////////////////////////////////////////////////////////////// +// +// main +// +////////////////////////////////////////////////////////////////////////////// + +int main() +{ + { + typedef nest nest_int_t; + test_all_default_alloc(); + } + { + typedef nest nest_sz_t; + test_all_default_alloc(); + } + { + typedef nest > nest_stateful_t; + test_all(stateful_allocator(42)); + test_move_with_unequal_allocators(); + } + + return boost::report_errors(); +}