#include #include #include #include #include #include #include #include #include #include #include #include #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 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 m_less; }; template class HashT { public: explicit HashT(int) { } std::size_t operator()(T const& t) const { return m_hash(t); } private: HashT() = delete; std::hash m_hash; }; template 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 m_eq; }; template struct AllocT { using value_type = T; // using std::true_type::type = propagate_on_container_swap :; template struct rebind { using other = AllocT; }; explicit AllocT(int) { } AllocT(AllocT const&) = default; template AllocT(AllocT const&) { } template bool operator==(AllocT const&) const { return true; } template bool operator!=(AllocT const& o) const { return !(*this == o); } T* allocate(std::size_t n, T const* = 0) { return static_cast(::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 MaybeUnordered : public Base { public: using Comp = std::less; using MyComp = CompT; protected: static std::string name_ordered_part() { return ""; } }; // unordered template class MaybeUnordered : public Base { public: using Hash = std::hash; using Equal = std::equal_to; using MyHash = HashT; using MyEqual = EqualT; protected: static std::string name_ordered_part() { return "unordered_"; } }; // unique template class MaybeMulti : public Base { public: protected: static std::string name_multi_part() { return ""; } }; // multi template class MaybeMulti : public Base { public: protected: static std::string name_multi_part() { return "multi"; } }; // set template class MaybeMap : public Base { public: using T = void; using Value = typename Base::Key; using Values = std::vector; 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 MaybeMap : public Base { public: using T = int; using Value = std::pair; using Values = std::vector; 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 struct ContType { template < class Compare = std::less, class Allocator = std::allocator> 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 struct ContType { template < class Hash = std::hash, class KeyEqual = std::equal_to, class Allocator = std::allocator> 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; }; template struct TestTraitsHelper : MaybeUnordered, IsMulti>, IsUnordered> { private: using Base = MaybeUnordered, IsMulti>, IsUnordered>; public: using typename Base::Key; using is_unordered = std::integral_constant; using is_multi = std::integral_constant; using is_map = std::integral_constant; using Alloc = std::allocator; using MyAlloc = AllocT; static std::string name() { return std::string("aged_") + Base::name_ordered_part() + Base::name_multi_part() + Base::name_map_part(); } }; template struct TestTraits : TestTraitsHelper, ContType> { }; template static std::string name(Cont const&) { return TestTraits::name(); } template struct equal_value { bool operator()(typename Traits::Value const& lhs, typename Traits::Value const& rhs) { return Traits::extract(lhs) == Traits::extract(rhs); } }; template static std::vector make_list(Cont const& c) { return std::vector(c.begin(), c.end()); } //-------------------------------------------------------------------------- template typename std::enable_if::type checkMapContents(Container& c, Values const& v); template typename std::enable_if::type checkMapContents(Container, Values const&) { } // unordered template typename std::enable_if::type::is_unordered::value>::type checkUnorderedContentsRefRef(C&& c, Values const& v); template typename std::enable_if::type::is_unordered::value>::type checkUnorderedContentsRefRef(C&&, Values const&) { } template void checkContentsRefRef(C&& c, Values const& v); template void checkContents(Cont& c, Values const& v); template void checkContents(Cont& c); //-------------------------------------------------------------------------- // ordered template typename std::enable_if::type testConstructEmpty(); // unordered template typename std::enable_if::type testConstructEmpty(); // ordered template typename std::enable_if::type testConstructRange(); // unordered template typename std::enable_if::type testConstructRange(); // ordered template typename std::enable_if::type testConstructInitList(); // unordered template typename std::enable_if::type testConstructInitList(); //-------------------------------------------------------------------------- template void testCopyMove(); //-------------------------------------------------------------------------- template void testIterator(); // Unordered containers don't have reverse iterators template typename std::enable_if::type testReverseIterator(); template typename std::enable_if::type testReverseIterator() { } //-------------------------------------------------------------------------- template void checkInsertCopy(Container& c, Values const& v); template void checkInsertMove(Container& c, Values const& v); template void checkInsertHintCopy(Container& c, Values const& v); template void checkInsertHintMove(Container& c, Values const& v); template void checkEmplace(Container& c, Values const& v); template void checkEmplaceHint(Container& c, Values const& v); template void testModifiers(); //-------------------------------------------------------------------------- template void testChronological(); //-------------------------------------------------------------------------- // map, unordered_map template typename std::enable_if::type testArrayCreate(); template typename std::enable_if::type testArrayCreate() { } //-------------------------------------------------------------------------- // Helpers for erase tests template void reverseFillAgedContainer(Container& c, Values const& v); template Iter nextToEndIter(Iter const beginIter, Iter const endItr); //-------------------------------------------------------------------------- template bool doElementErase(Container& c, Iter const beginItr, Iter const endItr); template void testElementErase(); //-------------------------------------------------------------------------- template void doRangeErase(Container& c, BeginEndSrc const& beginEndSrc); template void testRangeErase(); //-------------------------------------------------------------------------- // ordered template typename std::enable_if::type testCompare(); template typename std::enable_if::type testCompare() { } //-------------------------------------------------------------------------- // ordered template typename std::enable_if::type testObservers(); // unordered template typename std::enable_if::type testObservers(); //-------------------------------------------------------------------------- template void testMaybeUnorderedMultiMap(); template void testMaybeUnorderedMulti(); template void testMaybeUnordered(); }; //------------------------------------------------------------------------------ // Check contents via at() and operator[] // map, unordered_map template typename std::enable_if::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 typename std::enable_if::type::is_unordered::value>::type aged_associative_container_test_base::checkUnorderedContentsRefRef(C&& c, Values const& v) { using Cont = typename std::remove_reference::type; using Traits = TestTraits; 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 void aged_associative_container_test_base::checkContentsRefRef(C&& c, Values const& v) { using Cont = typename std::remove_reference::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 void aged_associative_container_test_base::checkContents(Cont& c, Values const& v) { checkContentsRefRef(c, v); checkContentsRefRef(const_cast(c), v); checkMapContents(c, v); } template void aged_associative_container_test_base::checkContents(Cont& c) { using Traits = TestTraits; using Values = typename Traits::Values; checkContents(c, Values()); } //------------------------------------------------------------------------------ // // Construction // //------------------------------------------------------------------------------ // ordered template typename std::enable_if::type aged_associative_container_test_base::testConstructEmpty() { using Traits = TestTraits; 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 c(clock); checkContents(c); } { typename Traits::template Cont c(clock, MyComp(1)); checkContents(c); } { typename Traits::template Cont c(clock, MyAlloc(1)); checkContents(c); } { typename Traits::template Cont c(clock, MyComp(1), MyAlloc(1)); checkContents(c); } } // unordered template typename std::enable_if::type aged_associative_container_test_base::testConstructEmpty() { using Traits = TestTraits; 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 c(clock); checkContents(c); } { typename Traits::template Cont c(clock, MyHash(1)); checkContents(c); } { typename Traits::template Cont c(clock, MyEqual(1)); checkContents(c); } { typename Traits::template Cont c(clock, MyAlloc(1)); checkContents(c); } { typename Traits::template Cont c(clock, MyHash(1), MyEqual(1)); checkContents(c); } { typename Traits::template Cont c(clock, MyHash(1), MyAlloc(1)); checkContents(c); } { typename Traits::template Cont c(clock, MyEqual(1), MyAlloc(1)); checkContents(c); } { typename Traits::template Cont c( clock, MyHash(1), MyEqual(1), MyAlloc(1)); checkContents(c); } } // ordered template typename std::enable_if::type aged_associative_container_test_base::testConstructRange() { using Traits = TestTraits; 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 c(v.begin(), v.end(), clock); checkContents(c, v); } { typename Traits::template Cont c(v.begin(), v.end(), clock, MyComp(1)); checkContents(c, v); } { typename Traits::template Cont c(v.begin(), v.end(), clock, MyAlloc(1)); checkContents(c, v); } { typename Traits::template Cont c( v.begin(), v.end(), clock, MyComp(1), MyAlloc(1)); checkContents(c, v); } // swap { typename Traits::template Cont c1(v.begin(), v.end(), clock); typename Traits::template Cont c2(clock); std::swap(c1, c2); checkContents(c2, v); } } // unordered template typename std::enable_if::type aged_associative_container_test_base::testConstructRange() { using Traits = TestTraits; 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 c(v.begin(), v.end(), clock); checkContents(c, v); } { typename Traits::template Cont c( v.begin(), v.end(), clock, MyHash(1)); checkContents(c, v); } { typename Traits::template Cont c( v.begin(), v.end(), clock, MyEqual(1)); checkContents(c, v); } { typename Traits::template Cont c( v.begin(), v.end(), clock, MyAlloc(1)); checkContents(c, v); } { typename Traits::template Cont c( v.begin(), v.end(), clock, MyHash(1), MyEqual(1)); checkContents(c, v); } { typename Traits::template Cont c( v.begin(), v.end(), clock, MyHash(1), MyAlloc(1)); checkContents(c, v); } { typename Traits::template Cont c( v.begin(), v.end(), clock, MyEqual(1), MyAlloc(1)); checkContents(c, v); } { typename Traits::template Cont c( v.begin(), v.end(), clock, MyHash(1), MyEqual(1), MyAlloc(1)); checkContents(c, v); } } // ordered template typename std::enable_if::type aged_associative_container_test_base::testConstructInitList() { using Traits = TestTraits; typename Traits::ManualClock clock; // testcase (Traits::name() + " init-list"); testcase("init-list"); // VFALCO TODO pass(); } // unordered template typename std::enable_if::type aged_associative_container_test_base::testConstructInitList() { using Traits = TestTraits; typename Traits::ManualClock clock; // testcase (Traits::name() + " init-list"); testcase("init-list"); // VFALCO TODO pass(); } //------------------------------------------------------------------------------ // // Copy/Move construction and assign // //------------------------------------------------------------------------------ template void aged_associative_container_test_base::testCopyMove() { using Traits = TestTraits; 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 void aged_associative_container_test_base::testIterator() { using Traits = TestTraits; 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 typename std::enable_if::type aged_associative_container_test_base::testReverseIterator() { using Traits = TestTraits; 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 void aged_associative_container_test_base::checkInsertCopy(Container& c, Values const& v) { for (auto const& e : v) c.insert(e); checkContents(c, v); } template 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 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 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 void aged_associative_container_test_base::checkEmplace(Container& c, Values const& v) { for (auto const& e : v) c.emplace(e); checkContents(c, v); } template 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 void aged_associative_container_test_base::testModifiers() { using Traits = TestTraits; 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 void aged_associative_container_test_base::testChronological() { using Traits = TestTraits; 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())); // 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())); // 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())); { // 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 typename std::enable_if::type aged_associative_container_test_base::testArrayCreate() { using Traits = TestTraits; 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 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(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 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 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 void aged_associative_container_test_base::testElementErase() { using Traits = TestTraits; // 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 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 void aged_associative_container_test_base::testRangeErase() { using Traits = TestTraits; // 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 typename std::enable_if::type aged_associative_container_test_base::testCompare() { using Traits = TestTraits; 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 typename std::enable_if::type aged_associative_container_test_base::testObservers() { using Traits = TestTraits; 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 typename std::enable_if::type aged_associative_container_test_base::testObservers() { using Traits = TestTraits; 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 void aged_associative_container_test_base::testMaybeUnorderedMultiMap() { testConstructEmpty(); testConstructRange(); testConstructInitList(); testCopyMove(); testIterator(); testReverseIterator(); testModifiers(); testChronological(); testArrayCreate(); testElementErase(); testRangeErase(); testCompare(); testObservers(); } //------------------------------------------------------------------------------ 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, detail::aged_ordered_container>::value, "bad alias: aged_set"); static_assert( std::is_same, detail::aged_ordered_container>:: value, "bad alias: aged_multiset"); static_assert( std::is_same, detail::aged_ordered_container>::value, "bad alias: aged_map"); static_assert( std::is_same, detail::aged_ordered_container>:: value, "bad alias: aged_multimap"); static_assert( std::is_same< aged_unordered_set, detail::aged_unordered_container>::value, "bad alias: aged_unordered_set"); static_assert( std::is_same< aged_unordered_multiset, detail::aged_unordered_container>::value, "bad alias: aged_unordered_multiset"); static_assert( std::is_same< aged_unordered_map, detail::aged_unordered_container>::value, "bad alias: aged_unordered_map"); static_assert( std::is_same< aged_unordered_multimap, detail::aged_unordered_container>::value, "bad alias: aged_unordered_multimap"); void run() override { testMaybeUnorderedMultiMap(); } }; class aged_map_test : public aged_associative_container_test_base { public: void run() override { testMaybeUnorderedMultiMap(); } }; class aged_multiset_test : public aged_associative_container_test_base { public: void run() override { testMaybeUnorderedMultiMap(); } }; class aged_multimap_test : public aged_associative_container_test_base { public: void run() override { testMaybeUnorderedMultiMap(); } }; class aged_unordered_set_test : public aged_associative_container_test_base { public: void run() override { testMaybeUnorderedMultiMap(); } }; class aged_unordered_map_test : public aged_associative_container_test_base { public: void run() override { testMaybeUnorderedMultiMap(); } }; class aged_unordered_multiset_test : public aged_associative_container_test_base { public: void run() override { testMaybeUnorderedMultiMap(); } }; class aged_unordered_multimap_test : public aged_associative_container_test_base { public: void run() override { testMaybeUnorderedMultiMap(); } }; 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