// SPDX-License-Identifier: BSD-2-Clause // author: Max Kellermann #include "util/IntrusiveForwardList.hxx" #include #include namespace { struct CharItem final : IntrusiveForwardListHook { char ch; constexpr CharItem(char _ch) noexcept:ch(_ch) {} }; static std::string ToString(const auto &list) noexcept { std::string result; for (const auto &i : list) result.push_back(i.ch); return result; } } // anonymous namespace TEST(IntrusiveForwardList, Basic) { using Item = CharItem; Item items[]{'a', 'b', 'c'}; using List = IntrusiveForwardList; List list; ASSERT_EQ(ToString(list), ""); list.reverse(); ASSERT_EQ(ToString(list), ""); for (auto &i : items) list.push_front(i); ASSERT_EQ(ToString(list), "cba"); list.reverse(); ASSERT_EQ(ToString(list), "abc"); list.pop_front(); ASSERT_EQ(ToString(list), "bc"); list.reverse(); ASSERT_EQ(ToString(list), "cb"); /* move constructor */ auto list2 = std::move(list); ASSERT_EQ(ToString(list2), "cb"); ASSERT_EQ(ToString(list), ""); /* move operator */ list = std::move(list2); ASSERT_EQ(ToString(list), "cb"); ASSERT_EQ(ToString(list2), ""); // insert_after() List::insert_after(list.begin(), items[0]); ASSERT_EQ(ToString(list), "cab"); } TEST(IntrusiveForwardList, ConstantTimeSize) { using Item = CharItem; Item items[]{'a', 'b', 'c'}; IntrusiveForwardList< CharItem, IntrusiveForwardListBaseHookTraits, IntrusiveForwardListOptions{.constant_time_size = true}> list; ASSERT_EQ(ToString(list), ""); ASSERT_EQ(list.size(), 0U); list.reverse(); ASSERT_EQ(ToString(list), ""); ASSERT_EQ(list.size(), 0U); for (auto &i : items) list.push_front(i); ASSERT_EQ(ToString(list), "cba"); ASSERT_EQ(list.size(), 3U); list.reverse(); ASSERT_EQ(ToString(list), "abc"); ASSERT_EQ(list.size(), 3U); list.pop_front(); ASSERT_EQ(ToString(list), "bc"); ASSERT_EQ(list.size(), 2U); list.reverse(); ASSERT_EQ(ToString(list), "cb"); ASSERT_EQ(list.size(), 2U); /* move constructor */ auto list2 = std::move(list); ASSERT_EQ(ToString(list2), "cb"); ASSERT_EQ(list2.size(), 2U); ASSERT_EQ(ToString(list), ""); ASSERT_EQ(list.size(), 0U); /* move operator */ list = std::move(list2); ASSERT_EQ(ToString(list), "cb"); ASSERT_EQ(list.size(), 2U); ASSERT_EQ(ToString(list2), ""); ASSERT_EQ(list2.size(), 0U); // insert_after() list.insert_after(list.begin(), items[0]); ASSERT_EQ(ToString(list), "cab"); ASSERT_EQ(list.size(), 3U); } TEST(IntrusiveForwardList, CacheLast) { using Item = CharItem; Item items[]{'a', 'b', 'c'}; using List =IntrusiveForwardList< CharItem, IntrusiveForwardListBaseHookTraits, IntrusiveForwardListOptions{.cache_last = true}>; List list; ASSERT_EQ(ToString(list), ""); list.reverse(); ASSERT_EQ(ToString(list), ""); /* test push_back() */ for (auto &i : items) list.push_back(i); ASSERT_EQ(ToString(list), "abc"); ASSERT_EQ(&list.back(), &items[2]); list.clear(); /* test push_back() again to see if clear() has worked */ for (auto &i : items) list.push_back(i); ASSERT_EQ(ToString(list), "abc"); ASSERT_EQ(&list.back(), &items[2]); list.clear(); for (auto &i : items) list.push_front(i); ASSERT_EQ(ToString(list), "cba"); ASSERT_EQ(&list.back(), &items[0]); list.erase_after(list.begin()); ASSERT_EQ(ToString(list), "ca"); ASSERT_EQ(&list.back(), &items[0]); list.reverse(); ASSERT_EQ(ToString(list), "ac"); ASSERT_EQ(&list.back(), &items[2]); list.erase_after(list.begin()); ASSERT_EQ(ToString(list), "a"); ASSERT_EQ(&list.back(), &items[0]); list.reverse(); ASSERT_EQ(ToString(list), "a"); ASSERT_EQ(&list.back(), &items[0]); list.pop_front(); ASSERT_EQ(ToString(list), ""); list.insert_after(list.before_begin(), items[0]); ASSERT_EQ(ToString(list), "a"); ASSERT_EQ(&list.back(), &items[0]); list.insert_after(list.before_begin(), items[1]); ASSERT_EQ(ToString(list), "ba"); ASSERT_EQ(&list.back(), &items[0]); list.pop_front(); ASSERT_EQ(ToString(list), "a"); ASSERT_EQ(&list.back(), &items[0]); list.pop_front(); ASSERT_EQ(ToString(list), ""); for (auto &i : items) list.push_back(i); ASSERT_EQ(ToString(list), "abc"); ASSERT_EQ(&list.back(), &items[2]); /* move constructor */ auto list2 = std::move(list); ASSERT_EQ(ToString(list2), "abc"); ASSERT_EQ(&list2.back(), &items[2]); ASSERT_EQ(ToString(list), ""); /* move operator */ list = std::move(list2); ASSERT_EQ(ToString(list), "abc"); ASSERT_EQ(&list.back(), &items[2]); ASSERT_EQ(ToString(list2), ""); list.erase_after(list.begin()); ASSERT_EQ(ToString(list), "ac"); ASSERT_EQ(&list.back(), &items[2]); list.erase_after(list.before_begin()); ASSERT_EQ(ToString(list), "c"); ASSERT_EQ(&list.back(), &items[2]); // insert_after() list.insert_after(list.begin(), items[0]); ASSERT_EQ(ToString(list), "ca"); ASSERT_EQ(&list.back(), &items[0]); }