Files
rippled/src/test/beast/aged_associative_container_test.cpp

1864 lines
51 KiB
C++

#include <xrpl/beast/clock/manual_clock.h>
#include <xrpl/beast/container/aged_map.h>
#include <xrpl/beast/container/aged_multimap.h>
#include <xrpl/beast/container/aged_multiset.h>
#include <xrpl/beast/container/aged_set.h>
#include <xrpl/beast/container/aged_unordered_map.h>
#include <xrpl/beast/container/aged_unordered_multimap.h>
#include <xrpl/beast/container/aged_unordered_multiset.h>
#include <xrpl/beast/container/aged_unordered_set.h>
#include <xrpl/beast/unit_test.h>
#include <list>
#include <vector>
#ifndef BEAST_AGED_UNORDERED_NO_ALLOC_DEFAULTCTOR
#ifdef _MSC_VER
#define BEAST_AGED_UNORDERED_NO_ALLOC_DEFAULTCTOR 0
#else
#define BEAST_AGED_UNORDERED_NO_ALLOC_DEFAULTCTOR 1
#endif
#endif
#ifndef BEAST_CONTAINER_EXTRACT_NOREF
#ifdef _MSC_VER
#define BEAST_CONTAINER_EXTRACT_NOREF 1
#else
#define BEAST_CONTAINER_EXTRACT_NOREF 1
#endif
#endif
namespace beast {
class aged_associative_container_test_base : public unit_test::suite
{
public:
template <class T>
struct CompT
{
explicit CompT(int)
{
}
CompT(CompT const&)
{
}
bool
operator()(T const& lhs, T const& rhs) const
{
return m_less(lhs, rhs);
}
private:
CompT() = delete;
std::less<T> m_less;
};
template <class T>
class HashT
{
public:
explicit HashT(int)
{
}
std::size_t
operator()(T const& t) const
{
return m_hash(t);
}
private:
HashT() = delete;
std::hash<T> m_hash;
};
template <class T>
struct EqualT
{
public:
explicit EqualT(int)
{
}
bool
operator()(T const& lhs, T const& rhs) const
{
return m_eq(lhs, rhs);
}
private:
EqualT() = delete;
std::equal_to<T> m_eq;
};
template <class T>
struct AllocT
{
using value_type = T;
// using std::true_type::type = propagate_on_container_swap :;
template <class U>
struct rebind
{
using other = AllocT<U>;
};
explicit AllocT(int)
{
}
AllocT(AllocT const&) = default;
template <class U>
AllocT(AllocT<U> const&)
{
}
template <class U>
bool
operator==(AllocT<U> const&) const
{
return true;
}
template <class U>
bool
operator!=(AllocT<U> const& o) const
{
return !(*this == o);
}
T*
allocate(std::size_t n, T const* = 0)
{
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void
deallocate(T* p, std::size_t)
{
::operator delete(p);
}
#if !BEAST_AGED_UNORDERED_NO_ALLOC_DEFAULTCTOR
AllocT()
{
}
#else
private:
AllocT() = delete;
#endif
};
//--------------------------------------------------------------------------
// ordered
template <class Base, bool IsUnordered>
class MaybeUnordered : public Base
{
public:
using Comp = std::less<typename Base::Key>;
using MyComp = CompT<typename Base::Key>;
protected:
static std::string
name_ordered_part()
{
return "";
}
};
// unordered
template <class Base>
class MaybeUnordered<Base, true> : public Base
{
public:
using Hash = std::hash<typename Base::Key>;
using Equal = std::equal_to<typename Base::Key>;
using MyHash = HashT<typename Base::Key>;
using MyEqual = EqualT<typename Base::Key>;
protected:
static std::string
name_ordered_part()
{
return "unordered_";
}
};
// unique
template <class Base, bool IsMulti>
class MaybeMulti : public Base
{
public:
protected:
static std::string
name_multi_part()
{
return "";
}
};
// multi
template <class Base>
class MaybeMulti<Base, true> : public Base
{
public:
protected:
static std::string
name_multi_part()
{
return "multi";
}
};
// set
template <class Base, bool IsMap>
class MaybeMap : public Base
{
public:
using T = void;
using Value = typename Base::Key;
using Values = std::vector<Value>;
static typename Base::Key const&
extract(Value const& value)
{
return value; // NOLINT(bugprone-return-const-ref-from-parameter)
}
static Values
values()
{
Values v{
"apple",
"banana",
"cherry",
"grape",
"orange",
};
return v;
}
protected:
static std::string
name_map_part()
{
return "set";
}
};
// map
template <class Base>
class MaybeMap<Base, true> : public Base
{
public:
using T = int;
using Value = std::pair<typename Base::Key, T>;
using Values = std::vector<Value>;
static typename Base::Key const&
extract(Value const& value)
{
return value.first;
}
static Values
values()
{
Values v{
std::make_pair("apple", 1),
std::make_pair("banana", 2),
std::make_pair("cherry", 3),
std::make_pair("grape", 4),
std::make_pair("orange", 5)};
return v;
}
protected:
static std::string
name_map_part()
{
return "map";
}
};
//--------------------------------------------------------------------------
// ordered
template <class Base, bool IsUnordered = Base::is_unordered::value>
struct ContType
{
template <
class Compare = std::less<typename Base::Key>,
class Allocator = std::allocator<typename Base::Value>>
using Cont = detail::aged_ordered_container<
Base::is_multi::value,
Base::is_map::value,
typename Base::Key,
typename Base::T,
typename Base::Clock,
Compare,
Allocator>;
};
// unordered
template <class Base>
struct ContType<Base, true>
{
template <
class Hash = std::hash<typename Base::Key>,
class KeyEqual = std::equal_to<typename Base::Key>,
class Allocator = std::allocator<typename Base::Value>>
using Cont = detail::aged_unordered_container<
Base::is_multi::value,
Base::is_map::value,
typename Base::Key,
typename Base::T,
typename Base::Clock,
Hash,
KeyEqual,
Allocator>;
};
//--------------------------------------------------------------------------
struct TestTraitsBase
{
using Key = std::string;
using Clock = std::chrono::steady_clock;
using ManualClock = manual_clock<Clock>;
};
template <bool IsUnordered, bool IsMulti, bool IsMap>
struct TestTraitsHelper
: MaybeUnordered<MaybeMulti<MaybeMap<TestTraitsBase, IsMap>, IsMulti>, IsUnordered>
{
private:
using Base =
MaybeUnordered<MaybeMulti<MaybeMap<TestTraitsBase, IsMap>, IsMulti>, IsUnordered>;
public:
using typename Base::Key;
using is_unordered = std::integral_constant<bool, IsUnordered>;
using is_multi = std::integral_constant<bool, IsMulti>;
using is_map = std::integral_constant<bool, IsMap>;
using Alloc = std::allocator<typename Base::Value>;
using MyAlloc = AllocT<typename Base::Value>;
static std::string
name()
{
return std::string("aged_") + Base::name_ordered_part() + Base::name_multi_part() +
Base::name_map_part();
}
};
template <bool IsUnordered, bool IsMulti, bool IsMap>
struct TestTraits : TestTraitsHelper<IsUnordered, IsMulti, IsMap>,
ContType<TestTraitsHelper<IsUnordered, IsMulti, IsMap>>
{
};
template <class Cont>
static std::string
name(Cont const&)
{
return TestTraits<Cont::is_unordered, Cont::is_multi, Cont::is_map>::name();
}
template <class Traits>
struct equal_value
{
bool
operator()(typename Traits::Value const& lhs, typename Traits::Value const& rhs)
{
return Traits::extract(lhs) == Traits::extract(rhs);
}
};
template <class Cont>
static std::vector<typename Cont::value_type>
make_list(Cont const& c)
{
return std::vector<typename Cont::value_type>(c.begin(), c.end());
}
//--------------------------------------------------------------------------
template <class Container, class Values>
typename std::enable_if<Container::is_map::value && !Container::is_multi::value>::type
checkMapContents(Container& c, Values const& v);
template <class Container, class Values>
typename std::enable_if<!(Container::is_map::value && !Container::is_multi::value)>::type
checkMapContents(Container, Values const&)
{
}
// unordered
template <class C, class Values>
typename std::enable_if<std::remove_reference<C>::type::is_unordered::value>::type
checkUnorderedContentsRefRef(C&& c, Values const& v);
template <class C, class Values>
typename std::enable_if<!std::remove_reference<C>::type::is_unordered::value>::type
checkUnorderedContentsRefRef(C&&, Values const&)
{
}
template <class C, class Values>
void
checkContentsRefRef(C&& c, Values const& v);
template <class Cont, class Values>
void
checkContents(Cont& c, Values const& v);
template <class Cont>
void
checkContents(Cont& c);
//--------------------------------------------------------------------------
// ordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
testConstructEmpty();
// unordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsUnordered>::type
testConstructEmpty();
// ordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
testConstructRange();
// unordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsUnordered>::type
testConstructRange();
// ordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
testConstructInitList();
// unordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsUnordered>::type
testConstructInitList();
//--------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
testCopyMove();
//--------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
testIterator();
// Unordered containers don't have reverse iterators
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
testReverseIterator();
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsUnordered>::type
testReverseIterator()
{
}
//--------------------------------------------------------------------------
template <class Container, class Values>
void
checkInsertCopy(Container& c, Values const& v);
template <class Container, class Values>
void
checkInsertMove(Container& c, Values const& v);
template <class Container, class Values>
void
checkInsertHintCopy(Container& c, Values const& v);
template <class Container, class Values>
void
checkInsertHintMove(Container& c, Values const& v);
template <class Container, class Values>
void
checkEmplace(Container& c, Values const& v);
template <class Container, class Values>
void
checkEmplaceHint(Container& c, Values const& v);
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
testModifiers();
//--------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
testChronological();
//--------------------------------------------------------------------------
// map, unordered_map
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsMap && !IsMulti>::type
testArrayCreate();
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!(IsMap && !IsMulti)>::type
testArrayCreate()
{
}
//--------------------------------------------------------------------------
// Helpers for erase tests
template <class Container, class Values>
void
reverseFillAgedContainer(Container& c, Values const& v);
template <class Iter>
Iter
nextToEndIter(Iter const beginIter, Iter const endItr);
//--------------------------------------------------------------------------
template <class Container, class Iter>
bool
doElementErase(Container& c, Iter const beginItr, Iter const endItr);
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
testElementErase();
//--------------------------------------------------------------------------
template <class Container, class BeginEndSrc>
void
doRangeErase(Container& c, BeginEndSrc const& beginEndSrc);
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
testRangeErase();
//--------------------------------------------------------------------------
// ordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
testCompare();
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsUnordered>::type
testCompare()
{
}
//--------------------------------------------------------------------------
// ordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
testObservers();
// unordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsUnordered>::type
testObservers();
//--------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
testMaybeUnorderedMultiMap();
template <bool IsUnordered, bool IsMulti>
void
testMaybeUnorderedMulti();
template <bool IsUnordered>
void
testMaybeUnordered();
};
//------------------------------------------------------------------------------
// Check contents via at() and operator[]
// map, unordered_map
template <class Container, class Values>
typename std::enable_if<Container::is_map::value && !Container::is_multi::value>::type
aged_associative_container_test_base::checkMapContents(Container& c, Values const& v)
{
if (v.empty())
{
BEAST_EXPECT(c.empty());
BEAST_EXPECT(c.size() == 0);
return;
}
try
{
// Make sure no exception is thrown
for (auto const& e : v)
c.at(e.first);
for (auto const& e : v)
BEAST_EXPECT(c.operator[](e.first) == e.second);
}
catch (std::out_of_range const&)
{
fail("caught exception");
}
}
// unordered
template <class C, class Values>
typename std::enable_if<std::remove_reference<C>::type::is_unordered::value>::type
aged_associative_container_test_base::checkUnorderedContentsRefRef(C&& c, Values const& v)
{
using Cont = typename std::remove_reference<C>::type;
using Traits =
TestTraits<Cont::is_unordered::value, Cont::is_multi::value, Cont::is_map::value>;
using size_type = typename Cont::size_type;
auto const hash(c.hash_function());
auto const key_eq(c.key_eq());
for (size_type i(0); i < c.bucket_count(); ++i)
{
auto const last(c.end(i));
for (auto iter(c.begin(i)); iter != last; ++iter)
{
auto const match(
std::find_if(v.begin(), v.end(), [iter](typename Values::value_type const& e) {
return Traits::extract(*iter) == Traits::extract(e);
}));
BEAST_EXPECT(match != v.end());
BEAST_EXPECT(key_eq(Traits::extract(*iter), Traits::extract(*match)));
BEAST_EXPECT(hash(Traits::extract(*iter)) == hash(Traits::extract(*match)));
}
}
}
template <class C, class Values>
void
aged_associative_container_test_base::checkContentsRefRef(C&& c, Values const& v)
{
using Cont = typename std::remove_reference<C>::type;
using size_type = typename Cont::size_type;
BEAST_EXPECT(c.size() == v.size());
BEAST_EXPECT(size_type(std::distance(c.begin(), c.end())) == v.size());
BEAST_EXPECT(size_type(std::distance(c.cbegin(), c.cend())) == v.size());
BEAST_EXPECT(
size_type(std::distance(c.chronological.begin(), c.chronological.end())) == v.size());
BEAST_EXPECT(
size_type(std::distance(c.chronological.cbegin(), c.chronological.cend())) == v.size());
BEAST_EXPECT(
size_type(std::distance(c.chronological.rbegin(), c.chronological.rend())) == v.size());
BEAST_EXPECT(
size_type(std::distance(c.chronological.crbegin(), c.chronological.crend())) == v.size());
checkUnorderedContentsRefRef(c, v);
}
template <class Cont, class Values>
void
aged_associative_container_test_base::checkContents(Cont& c, Values const& v)
{
checkContentsRefRef(c, v);
checkContentsRefRef(const_cast<Cont const&>(c), v);
checkMapContents(c, v);
}
template <class Cont>
void
aged_associative_container_test_base::checkContents(Cont& c)
{
using Traits =
TestTraits<Cont::is_unordered::value, Cont::is_multi::value, Cont::is_map::value>;
using Values = typename Traits::Values;
checkContents(c, Values());
}
//------------------------------------------------------------------------------
//
// Construction
//
//------------------------------------------------------------------------------
// ordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
aged_associative_container_test_base::testConstructEmpty()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
using Comp = typename Traits::Comp;
using Alloc = typename Traits::Alloc;
using MyComp = typename Traits::MyComp;
using MyAlloc = typename Traits::MyAlloc;
typename Traits::ManualClock clock;
// testcase (Traits::name() + " empty");
testcase("empty");
{
typename Traits::template Cont<Comp, Alloc> c(clock);
checkContents(c);
}
{
typename Traits::template Cont<MyComp, Alloc> c(clock, MyComp(1));
checkContents(c);
}
{
typename Traits::template Cont<Comp, MyAlloc> c(clock, MyAlloc(1));
checkContents(c);
}
{
typename Traits::template Cont<MyComp, MyAlloc> c(clock, MyComp(1), MyAlloc(1));
checkContents(c);
}
}
// unordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsUnordered>::type
aged_associative_container_test_base::testConstructEmpty()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
using Hash = typename Traits::Hash;
using Equal = typename Traits::Equal;
using Alloc = typename Traits::Alloc;
using MyHash = typename Traits::MyHash;
using MyEqual = typename Traits::MyEqual;
using MyAlloc = typename Traits::MyAlloc;
typename Traits::ManualClock clock;
// testcase (Traits::name() + " empty");
testcase("empty");
{
typename Traits::template Cont<Hash, Equal, Alloc> c(clock);
checkContents(c);
}
{
typename Traits::template Cont<MyHash, Equal, Alloc> c(clock, MyHash(1));
checkContents(c);
}
{
typename Traits::template Cont<Hash, MyEqual, Alloc> c(clock, MyEqual(1));
checkContents(c);
}
{
typename Traits::template Cont<Hash, Equal, MyAlloc> c(clock, MyAlloc(1));
checkContents(c);
}
{
typename Traits::template Cont<MyHash, MyEqual, Alloc> c(clock, MyHash(1), MyEqual(1));
checkContents(c);
}
{
typename Traits::template Cont<MyHash, Equal, MyAlloc> c(clock, MyHash(1), MyAlloc(1));
checkContents(c);
}
{
typename Traits::template Cont<Hash, MyEqual, MyAlloc> c(clock, MyEqual(1), MyAlloc(1));
checkContents(c);
}
{
typename Traits::template Cont<MyHash, MyEqual, MyAlloc> c(
clock, MyHash(1), MyEqual(1), MyAlloc(1));
checkContents(c);
}
}
// ordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
aged_associative_container_test_base::testConstructRange()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
using Comp = typename Traits::Comp;
using Alloc = typename Traits::Alloc;
using MyComp = typename Traits::MyComp;
using MyAlloc = typename Traits::MyAlloc;
typename Traits::ManualClock clock;
auto const v(Traits::values());
// testcase (Traits::name() + " range");
testcase("range");
{
typename Traits::template Cont<Comp, Alloc> c(v.begin(), v.end(), clock);
checkContents(c, v);
}
{
typename Traits::template Cont<MyComp, Alloc> c(v.begin(), v.end(), clock, MyComp(1));
checkContents(c, v);
}
{
typename Traits::template Cont<Comp, MyAlloc> c(v.begin(), v.end(), clock, MyAlloc(1));
checkContents(c, v);
}
{
typename Traits::template Cont<MyComp, MyAlloc> c(
v.begin(), v.end(), clock, MyComp(1), MyAlloc(1));
checkContents(c, v);
}
// swap
{
typename Traits::template Cont<Comp, Alloc> c1(v.begin(), v.end(), clock);
typename Traits::template Cont<Comp, Alloc> c2(clock);
std::swap(c1, c2);
checkContents(c2, v);
}
}
// unordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsUnordered>::type
aged_associative_container_test_base::testConstructRange()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
using Hash = typename Traits::Hash;
using Equal = typename Traits::Equal;
using Alloc = typename Traits::Alloc;
using MyHash = typename Traits::MyHash;
using MyEqual = typename Traits::MyEqual;
using MyAlloc = typename Traits::MyAlloc;
typename Traits::ManualClock clock;
auto const v(Traits::values());
// testcase (Traits::name() + " range");
testcase("range");
{
typename Traits::template Cont<Hash, Equal, Alloc> c(v.begin(), v.end(), clock);
checkContents(c, v);
}
{
typename Traits::template Cont<MyHash, Equal, Alloc> c(
v.begin(), v.end(), clock, MyHash(1));
checkContents(c, v);
}
{
typename Traits::template Cont<Hash, MyEqual, Alloc> c(
v.begin(), v.end(), clock, MyEqual(1));
checkContents(c, v);
}
{
typename Traits::template Cont<Hash, Equal, MyAlloc> c(
v.begin(), v.end(), clock, MyAlloc(1));
checkContents(c, v);
}
{
typename Traits::template Cont<MyHash, MyEqual, Alloc> c(
v.begin(), v.end(), clock, MyHash(1), MyEqual(1));
checkContents(c, v);
}
{
typename Traits::template Cont<MyHash, Equal, MyAlloc> c(
v.begin(), v.end(), clock, MyHash(1), MyAlloc(1));
checkContents(c, v);
}
{
typename Traits::template Cont<Hash, MyEqual, MyAlloc> c(
v.begin(), v.end(), clock, MyEqual(1), MyAlloc(1));
checkContents(c, v);
}
{
typename Traits::template Cont<MyHash, MyEqual, MyAlloc> c(
v.begin(), v.end(), clock, MyHash(1), MyEqual(1), MyAlloc(1));
checkContents(c, v);
}
}
// ordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
aged_associative_container_test_base::testConstructInitList()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
typename Traits::ManualClock clock;
// testcase (Traits::name() + " init-list");
testcase("init-list");
// VFALCO TODO
pass();
}
// unordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsUnordered>::type
aged_associative_container_test_base::testConstructInitList()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
typename Traits::ManualClock clock;
// testcase (Traits::name() + " init-list");
testcase("init-list");
// VFALCO TODO
pass();
}
//------------------------------------------------------------------------------
//
// Copy/Move construction and assign
//
//------------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
aged_associative_container_test_base::testCopyMove()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
using Alloc = typename Traits::Alloc;
typename Traits::ManualClock clock;
auto const v(Traits::values());
// testcase (Traits::name() + " copy/move");
testcase("copy/move");
// copy
{
typename Traits::template Cont<> c(v.begin(), v.end(), clock);
typename Traits::template Cont<> c2(c);
checkContents(c, v);
checkContents(c2, v);
BEAST_EXPECT(c == c2);
unexpected(c != c2);
}
{
typename Traits::template Cont<> c(v.begin(), v.end(), clock);
typename Traits::template Cont<> c2(c, Alloc());
checkContents(c, v);
checkContents(c2, v);
BEAST_EXPECT(c == c2);
unexpected(c != c2);
}
{
typename Traits::template Cont<> c(v.begin(), v.end(), clock);
typename Traits::template Cont<> c2(clock);
c2 = c;
checkContents(c, v);
checkContents(c2, v);
BEAST_EXPECT(c == c2);
unexpected(c != c2);
}
// move
{
typename Traits::template Cont<> c(v.begin(), v.end(), clock);
typename Traits::template Cont<> c2(std::move(c));
checkContents(c2, v);
}
{
typename Traits::template Cont<> c(v.begin(), v.end(), clock);
typename Traits::template Cont<> c2(std::move(c), Alloc());
checkContents(c2, v);
}
{
typename Traits::template Cont<> c(v.begin(), v.end(), clock);
typename Traits::template Cont<> c2(clock);
c2 = std::move(c);
checkContents(c2, v);
}
}
//------------------------------------------------------------------------------
//
// Iterator construction and assignment
//
//------------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
aged_associative_container_test_base::testIterator()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
typename Traits::ManualClock clock;
auto const v(Traits::values());
// testcase (Traits::name() + " iterators");
testcase("iterator");
typename Traits::template Cont<> c{clock};
using iterator = decltype(c.begin());
using const_iterator = decltype(c.cbegin());
// Should be able to construct or assign an iterator from an iterator.
iterator nnIt_0{c.begin()};
iterator nnIt_1{nnIt_0};
BEAST_EXPECT(nnIt_0 == nnIt_1);
iterator nnIt_2;
nnIt_2 = nnIt_1;
BEAST_EXPECT(nnIt_1 == nnIt_2);
// Should be able to construct or assign a const_iterator from a
// const_iterator.
const_iterator ccIt_0{c.cbegin()};
const_iterator ccIt_1{ccIt_0};
BEAST_EXPECT(ccIt_0 == ccIt_1);
const_iterator ccIt_2;
ccIt_2 = ccIt_1;
BEAST_EXPECT(ccIt_1 == ccIt_2);
// Comparison between iterator and const_iterator is okay
BEAST_EXPECT(nnIt_0 == ccIt_0);
BEAST_EXPECT(ccIt_1 == nnIt_1);
// Should be able to construct a const_iterator from an iterator.
const_iterator ncIt_3{c.begin()};
const_iterator ncIt_4{nnIt_0};
BEAST_EXPECT(ncIt_3 == ncIt_4);
const_iterator ncIt_5;
ncIt_5 = nnIt_2;
BEAST_EXPECT(ncIt_5 == ncIt_4);
// None of these should compile because they construct or assign to a
// non-const iterator with a const_iterator.
// iterator cnIt_0 {c.cbegin()};
// iterator cnIt_1 {ccIt_0};
// iterator cnIt_2;
// cnIt_2 = ccIt_2;
}
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
aged_associative_container_test_base::testReverseIterator()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
typename Traits::ManualClock clock;
auto const v(Traits::values());
// testcase (Traits::name() + " reverse_iterators");
testcase("reverse_iterator");
typename Traits::template Cont<> c{clock};
using iterator = decltype(c.begin());
using reverse_iterator = decltype(c.rbegin());
using const_reverse_iterator = decltype(c.crbegin());
// Naming decoder ring
// constructed from ------+ +----- constructed type
// /\/\ -- character pairs
// xAyBit
// r (reverse) or f (forward)--^-^
// ^-^------ C (const) or N (non-const)
// Should be able to construct or assign a reverse_iterator from a
// reverse_iterator.
reverse_iterator rNrNit_0{c.rbegin()};
reverse_iterator rNrNit_1{rNrNit_0};
BEAST_EXPECT(rNrNit_0 == rNrNit_1);
reverse_iterator xXrNit_2;
xXrNit_2 = rNrNit_1;
BEAST_EXPECT(rNrNit_1 == xXrNit_2);
// Should be able to construct or assign a const_reverse_iterator from a
// const_reverse_iterator
const_reverse_iterator rCrCit_0{c.crbegin()};
const_reverse_iterator rCrCit_1{rCrCit_0};
BEAST_EXPECT(rCrCit_0 == rCrCit_1);
const_reverse_iterator xXrCit_2;
xXrCit_2 = rCrCit_1;
BEAST_EXPECT(rCrCit_1 == xXrCit_2);
// Comparison between reverse_iterator and const_reverse_iterator is okay
BEAST_EXPECT(rNrNit_0 == rCrCit_0);
BEAST_EXPECT(rCrCit_1 == rNrNit_1);
// Should be able to construct or assign a const_reverse_iterator from a
// reverse_iterator
const_reverse_iterator rNrCit_0{c.rbegin()};
const_reverse_iterator rNrCit_1{rNrNit_0};
BEAST_EXPECT(rNrCit_0 == rNrCit_1);
xXrCit_2 = rNrNit_1;
BEAST_EXPECT(rNrCit_1 == xXrCit_2);
// The standard allows these conversions:
// o reverse_iterator is explicitly constructible from iterator.
// o const_reverse_iterator is explicitly constructible from
// const_iterator.
// Should be able to construct or assign reverse_iterators from
// non-reverse iterators.
reverse_iterator fNrNit_0{c.begin()};
const_reverse_iterator fNrCit_0{c.begin()};
BEAST_EXPECT(fNrNit_0 == fNrCit_0);
const_reverse_iterator fCrCit_0{c.cbegin()};
BEAST_EXPECT(fNrCit_0 == fCrCit_0);
// None of these should compile because they construct a non-reverse
// iterator from a reverse_iterator.
// iterator rNfNit_0 {c.rbegin()};
// const_iterator rNfCit_0 {c.rbegin()};
// const_iterator rCfCit_0 {c.crbegin()};
// You should not be able to assign an iterator to a reverse_iterator or
// vise-versa. So the following lines should not compile.
iterator xXfNit_0;
// xXfNit_0 = xXrNit_2;
// xXrNit_2 = xXfNit_0;
}
//------------------------------------------------------------------------------
//
// Modifiers
//
//------------------------------------------------------------------------------
template <class Container, class Values>
void
aged_associative_container_test_base::checkInsertCopy(Container& c, Values const& v)
{
for (auto const& e : v)
c.insert(e);
checkContents(c, v);
}
template <class Container, class Values>
void
aged_associative_container_test_base::checkInsertMove(Container& c, Values const& v)
{
Values v2(v);
for (auto& e : v2)
c.insert(std::move(e));
checkContents(c, v);
}
template <class Container, class Values>
void
aged_associative_container_test_base::checkInsertHintCopy(Container& c, Values const& v)
{
for (auto const& e : v)
c.insert(c.cend(), e);
checkContents(c, v);
}
template <class Container, class Values>
void
aged_associative_container_test_base::checkInsertHintMove(Container& c, Values const& v)
{
Values v2(v);
for (auto& e : v2)
c.insert(c.cend(), std::move(e));
checkContents(c, v);
}
template <class Container, class Values>
void
aged_associative_container_test_base::checkEmplace(Container& c, Values const& v)
{
for (auto const& e : v)
c.emplace(e);
checkContents(c, v);
}
template <class Container, class Values>
void
aged_associative_container_test_base::checkEmplaceHint(Container& c, Values const& v)
{
for (auto const& e : v)
c.emplace_hint(c.cend(), e);
checkContents(c, v);
}
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
aged_associative_container_test_base::testModifiers()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
typename Traits::ManualClock clock;
auto const v(Traits::values());
auto const l(make_list(v));
// testcase (Traits::name() + " modify");
testcase("modify");
{
typename Traits::template Cont<> c(clock);
checkInsertCopy(c, v);
}
{
typename Traits::template Cont<> c(clock);
checkInsertCopy(c, l);
}
{
typename Traits::template Cont<> c(clock);
checkInsertMove(c, v);
}
{
typename Traits::template Cont<> c(clock);
checkInsertMove(c, l);
}
{
typename Traits::template Cont<> c(clock);
checkInsertHintCopy(c, v);
}
{
typename Traits::template Cont<> c(clock);
checkInsertHintCopy(c, l);
}
{
typename Traits::template Cont<> c(clock);
checkInsertHintMove(c, v);
}
{
typename Traits::template Cont<> c(clock);
checkInsertHintMove(c, l);
}
}
//------------------------------------------------------------------------------
//
// Chronological ordering
//
//------------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
aged_associative_container_test_base::testChronological()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
typename Traits::ManualClock clock;
auto const v(Traits::values());
// testcase (Traits::name() + " chronological");
testcase("chronological");
typename Traits::template Cont<> c(v.begin(), v.end(), clock);
BEAST_EXPECT(
std::equal(
c.chronological.cbegin(),
c.chronological.cend(),
v.begin(),
v.end(),
equal_value<Traits>()));
// Test touch() with a non-const iterator.
for (auto iter(v.crbegin()); iter != v.crend(); ++iter)
{
using iterator = typename decltype(c)::iterator;
iterator found(c.find(Traits::extract(*iter)));
BEAST_EXPECT(found != c.cend());
if (found == c.cend())
return;
c.touch(found);
}
BEAST_EXPECT(
std::equal(
c.chronological.cbegin(),
c.chronological.cend(),
v.crbegin(),
v.crend(),
equal_value<Traits>()));
// Test touch() with a const_iterator
for (auto iter(v.cbegin()); iter != v.cend(); ++iter)
{
using const_iterator = typename decltype(c)::const_iterator;
const_iterator found(c.find(Traits::extract(*iter)));
BEAST_EXPECT(found != c.cend());
if (found == c.cend())
return;
c.touch(found);
}
BEAST_EXPECT(
std::equal(
c.chronological.cbegin(),
c.chronological.cend(),
v.cbegin(),
v.cend(),
equal_value<Traits>()));
{
// Because touch (reverse_iterator pos) is not allowed, the following
// lines should not compile for any aged_container type.
// c.touch (c.rbegin());
// c.touch (c.crbegin());
}
}
//------------------------------------------------------------------------------
//
// Element creation via operator[]
//
//------------------------------------------------------------------------------
// map, unordered_map
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsMap && !IsMulti>::type
aged_associative_container_test_base::testArrayCreate()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
typename Traits::ManualClock clock;
auto v(Traits::values());
// testcase (Traits::name() + " array create");
testcase("array create");
{
// Copy construct key
typename Traits::template Cont<> c(clock);
for (auto e : v)
c[e.first] = e.second;
checkContents(c, v);
}
{
// Move construct key
typename Traits::template Cont<> c(clock);
for (auto e : v)
c[std::move(e.first)] = e.second;
checkContents(c, v);
}
}
//------------------------------------------------------------------------------
//
// Helpers for erase tests
//
//------------------------------------------------------------------------------
template <class Container, class Values>
void
aged_associative_container_test_base::reverseFillAgedContainer(Container& c, Values const& values)
{
// Just in case the passed in container was not empty.
c.clear();
// c.clock() returns an abstract_clock, so dynamic_cast to manual_clock.
// VFALCO NOTE This is sketchy
using ManualClock = TestTraitsBase::ManualClock;
ManualClock& clk(dynamic_cast<ManualClock&>(c.clock()));
clk.set(0);
Values rev(values);
std::sort(rev.begin(), rev.end());
std::reverse(rev.begin(), rev.end());
for (auto& v : rev)
{
// Add values in reverse order so they are reversed chronologically.
++clk;
c.insert(v);
}
}
// Get one iterator before endIter. We have to use operator++ because you
// cannot use operator-- with unordered container iterators.
template <class Iter>
Iter
aged_associative_container_test_base::nextToEndIter(Iter beginIter, Iter const endIter)
{
if (beginIter == endIter)
{
fail("Internal test failure. Cannot advance beginIter");
return beginIter;
}
//
Iter nextToEnd = beginIter;
do
{
nextToEnd = beginIter++;
} while (beginIter != endIter);
return nextToEnd;
}
// Implementation for the element erase tests
//
// This test accepts:
// o the container from which we will erase elements
// o iterators into that container defining the range of the erase
//
// This implementation does not declare a pass, since it wants to allow
// the caller to examine the size of the container and the returned iterator
//
// Note that this test works on the aged_associative containers because an
// erase only invalidates references and iterators to the erased element
// (see 23.2.4/13). Therefore the passed-in end iterator stays valid through
// the whole test.
template <class Container, class Iter>
bool
aged_associative_container_test_base::doElementErase(
Container& c,
Iter const beginItr,
Iter const endItr)
{
auto it(beginItr);
size_t count = c.size();
while (it != endItr)
{
auto expectIt = it;
++expectIt;
it = c.erase(it);
if (it != expectIt)
{
fail("Unexpected returned iterator from element erase");
return false;
}
--count;
if (count != c.size())
{
fail("Failed to erase element");
return false;
}
if (c.empty())
{
if (it != endItr)
{
fail("Erase of last element didn't produce end");
return false;
}
}
}
return true;
}
//------------------------------------------------------------------------------
//
// Erase of individual elements
//
//------------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
aged_associative_container_test_base::testElementErase()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
// testcase (Traits::name() + " element erase"
testcase("element erase");
// Make and fill the container
typename Traits::ManualClock clock;
typename Traits::template Cont<> c{clock};
reverseFillAgedContainer(c, Traits::values());
{
// Test standard iterators
auto tempContainer(c);
if (!doElementErase(tempContainer, tempContainer.cbegin(), tempContainer.cend()))
return; // Test failed
BEAST_EXPECT(tempContainer.empty());
pass();
}
{
// Test chronological iterators
auto tempContainer(c);
auto& chron(tempContainer.chronological);
if (!doElementErase(tempContainer, chron.begin(), chron.end()))
return; // Test failed
BEAST_EXPECT(tempContainer.empty());
pass();
}
{
// Test standard iterator partial erase
auto tempContainer(c);
BEAST_EXPECT(tempContainer.size() > 2);
if (!doElementErase(
tempContainer,
++tempContainer.begin(),
nextToEndIter(tempContainer.begin(), tempContainer.end())))
return; // Test failed
BEAST_EXPECT(tempContainer.size() == 2);
pass();
}
{
// Test chronological iterator partial erase
auto tempContainer(c);
BEAST_EXPECT(tempContainer.size() > 2);
auto& chron(tempContainer.chronological);
if (!doElementErase(
tempContainer, ++chron.begin(), nextToEndIter(chron.begin(), chron.end())))
return; // Test failed
BEAST_EXPECT(tempContainer.size() == 2);
pass();
}
{
auto tempContainer(c);
BEAST_EXPECT(tempContainer.size() > 4);
// erase(reverse_iterator) is not allowed. None of the following
// should compile for any aged_container type.
// c.erase (c.rbegin());
// c.erase (c.crbegin());
// c.erase(c.rbegin(), ++c.rbegin());
// c.erase(c.crbegin(), ++c.crbegin());
}
}
// Implementation for the range erase tests
//
// This test accepts:
//
// o A container with more than 2 elements and
// o An object to ask for begin() and end() iterators in the passed container
//
// This peculiar interface allows either the container itself to be passed as
// the second argument or the container's "chronological" element. Both
// sources of iterators need to be tested on the container.
//
// The test locates iterators such that a range-based delete leaves the first
// and last elements in the container. It then validates that the container
// ended up with the expected contents.
//
template <class Container, class BeginEndSrc>
void
aged_associative_container_test_base::doRangeErase(Container& c, BeginEndSrc const& beginEndSrc)
{
BEAST_EXPECT(c.size() > 2);
auto itBeginPlusOne(beginEndSrc.begin());
auto const valueFront = *itBeginPlusOne;
++itBeginPlusOne;
// Get one iterator before end()
auto itBack(nextToEndIter(itBeginPlusOne, beginEndSrc.end()));
auto const valueBack = *itBack;
// Erase all elements but first and last
auto const retIter = c.erase(itBeginPlusOne, itBack);
BEAST_EXPECT(c.size() == 2);
BEAST_EXPECT(valueFront == *(beginEndSrc.begin()));
BEAST_EXPECT(valueBack == *(++beginEndSrc.begin()));
BEAST_EXPECT(retIter == (++beginEndSrc.begin()));
}
//------------------------------------------------------------------------------
//
// Erase range of elements
//
//------------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
aged_associative_container_test_base::testRangeErase()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
// testcase (Traits::name() + " element erase"
testcase("range erase");
// Make and fill the container
typename Traits::ManualClock clock;
typename Traits::template Cont<> c{clock};
reverseFillAgedContainer(c, Traits::values());
// Not bothering to test range erase with reverse iterators.
{
auto tempContainer(c);
doRangeErase(tempContainer, tempContainer);
}
{
auto tempContainer(c);
doRangeErase(tempContainer, tempContainer.chronological);
}
}
//------------------------------------------------------------------------------
//
// Container-wide comparison
//
//------------------------------------------------------------------------------
// ordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
aged_associative_container_test_base::testCompare()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
typename Traits::ManualClock clock;
auto const v(Traits::values());
// testcase (Traits::name() + " array create");
testcase("array create");
typename Traits::template Cont<> c1(v.begin(), v.end(), clock);
typename Traits::template Cont<> c2(v.begin(), v.end(), clock);
c2.erase(c2.cbegin());
expect(c1 != c2);
unexpected(c1 == c2);
expect(c1 < c2);
expect(c1 <= c2);
unexpected(c1 > c2);
unexpected(c1 >= c2);
}
//------------------------------------------------------------------------------
//
// Observers
//
//------------------------------------------------------------------------------
// ordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<!IsUnordered>::type
aged_associative_container_test_base::testObservers()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
typename Traits::ManualClock clock;
// testcase (Traits::name() + " observers");
testcase("observers");
typename Traits::template Cont<> c(clock);
c.key_comp();
c.value_comp();
pass();
}
// unordered
template <bool IsUnordered, bool IsMulti, bool IsMap>
typename std::enable_if<IsUnordered>::type
aged_associative_container_test_base::testObservers()
{
using Traits = TestTraits<IsUnordered, IsMulti, IsMap>;
typename Traits::ManualClock clock;
// testcase (Traits::name() + " observers");
testcase("observers");
typename Traits::template Cont<> c(clock);
c.hash_function();
c.key_eq();
pass();
}
//------------------------------------------------------------------------------
//
// Matrix
//
//------------------------------------------------------------------------------
template <bool IsUnordered, bool IsMulti, bool IsMap>
void
aged_associative_container_test_base::testMaybeUnorderedMultiMap()
{
testConstructEmpty<IsUnordered, IsMulti, IsMap>();
testConstructRange<IsUnordered, IsMulti, IsMap>();
testConstructInitList<IsUnordered, IsMulti, IsMap>();
testCopyMove<IsUnordered, IsMulti, IsMap>();
testIterator<IsUnordered, IsMulti, IsMap>();
testReverseIterator<IsUnordered, IsMulti, IsMap>();
testModifiers<IsUnordered, IsMulti, IsMap>();
testChronological<IsUnordered, IsMulti, IsMap>();
testArrayCreate<IsUnordered, IsMulti, IsMap>();
testElementErase<IsUnordered, IsMulti, IsMap>();
testRangeErase<IsUnordered, IsMulti, IsMap>();
testCompare<IsUnordered, IsMulti, IsMap>();
testObservers<IsUnordered, IsMulti, IsMap>();
}
//------------------------------------------------------------------------------
class aged_set_test : public aged_associative_container_test_base
{
public:
// Compile time checks
using Key = std::string;
using T = int;
static_assert(
std::is_same<aged_set<Key>, detail::aged_ordered_container<false, false, Key, void>>::value,
"bad alias: aged_set");
static_assert(
std::is_same<aged_multiset<Key>, detail::aged_ordered_container<true, false, Key, void>>::
value,
"bad alias: aged_multiset");
static_assert(
std::is_same<aged_map<Key, T>, detail::aged_ordered_container<false, true, Key, T>>::value,
"bad alias: aged_map");
static_assert(
std::is_same<aged_multimap<Key, T>, detail::aged_ordered_container<true, true, Key, T>>::
value,
"bad alias: aged_multimap");
static_assert(
std::is_same<
aged_unordered_set<Key>,
detail::aged_unordered_container<false, false, Key, void>>::value,
"bad alias: aged_unordered_set");
static_assert(
std::is_same<
aged_unordered_multiset<Key>,
detail::aged_unordered_container<true, false, Key, void>>::value,
"bad alias: aged_unordered_multiset");
static_assert(
std::is_same<
aged_unordered_map<Key, T>,
detail::aged_unordered_container<false, true, Key, T>>::value,
"bad alias: aged_unordered_map");
static_assert(
std::is_same<
aged_unordered_multimap<Key, T>,
detail::aged_unordered_container<true, true, Key, T>>::value,
"bad alias: aged_unordered_multimap");
void
run() override
{
testMaybeUnorderedMultiMap<false, false, false>();
}
};
class aged_map_test : public aged_associative_container_test_base
{
public:
void
run() override
{
testMaybeUnorderedMultiMap<false, false, true>();
}
};
class aged_multiset_test : public aged_associative_container_test_base
{
public:
void
run() override
{
testMaybeUnorderedMultiMap<false, true, false>();
}
};
class aged_multimap_test : public aged_associative_container_test_base
{
public:
void
run() override
{
testMaybeUnorderedMultiMap<false, true, true>();
}
};
class aged_unordered_set_test : public aged_associative_container_test_base
{
public:
void
run() override
{
testMaybeUnorderedMultiMap<true, false, false>();
}
};
class aged_unordered_map_test : public aged_associative_container_test_base
{
public:
void
run() override
{
testMaybeUnorderedMultiMap<true, false, true>();
}
};
class aged_unordered_multiset_test : public aged_associative_container_test_base
{
public:
void
run() override
{
testMaybeUnorderedMultiMap<true, true, false>();
}
};
class aged_unordered_multimap_test : public aged_associative_container_test_base
{
public:
void
run() override
{
testMaybeUnorderedMultiMap<true, true, true>();
}
};
BEAST_DEFINE_TESTSUITE(aged_set, beast, beast);
BEAST_DEFINE_TESTSUITE(aged_map, beast, beast);
BEAST_DEFINE_TESTSUITE(aged_multiset, beast, beast);
BEAST_DEFINE_TESTSUITE(aged_multimap, beast, beast);
BEAST_DEFINE_TESTSUITE(aged_unordered_set, beast, beast);
BEAST_DEFINE_TESTSUITE(aged_unordered_map, beast, beast);
BEAST_DEFINE_TESTSUITE(aged_unordered_multiset, beast, beast);
BEAST_DEFINE_TESTSUITE(aged_unordered_multimap, beast, beast);
} // namespace beast