From c8ed28e9c6c8d8d76daa52841a014f9a3b6d1c73 Mon Sep 17 00:00:00 2001
From: Max Kellermann <mk@cm4all.com>
Date: Tue, 23 Apr 2024 20:33:10 +0200
Subject: [PATCH] test/util/TestIntrusiveList: add test for clear_and_dispose()
 with modifying disposer

---
 test/util/TestIntrusiveList.cxx | 93 +++++++++++++++++++++++++++++++++
 1 file changed, 93 insertions(+)

diff --git a/test/util/TestIntrusiveList.cxx b/test/util/TestIntrusiveList.cxx
index ddd1b8bf4..9eceaac2b 100644
--- a/test/util/TestIntrusiveList.cxx
+++ b/test/util/TestIntrusiveList.cxx
@@ -279,3 +279,96 @@ TEST(IntrusiveList, Sort)
 	ASSERT_EQ(&*std::next(list.begin(), 1), &items[2]);
 	ASSERT_EQ(&*std::next(list.begin(), 2), &items[4]);
 }
+
+/**
+ * Call clear_and_dispose(), and let the disposer unlink the last
+ * item.
+ */
+TEST(IntrusiveList, ClearDisposeUnlink)
+{
+	using Item = CharItem<IntrusiveHookMode::TRACK>;
+
+	Item a{'a'}, b{'b'};
+
+	bool a_disposed = false;
+
+	EXPECT_FALSE(a.is_linked());
+	EXPECT_FALSE(b.is_linked());
+
+	IntrusiveList<Item> list;
+	list.push_back(a);
+	list.push_back(b);
+
+	EXPECT_TRUE(a.is_linked());
+	EXPECT_TRUE(b.is_linked());
+
+	list.clear_and_dispose([&](Item *item){
+		EXPECT_FALSE(a.is_linked());
+		EXPECT_TRUE(b.is_linked());
+		EXPECT_EQ(item, &a);
+		EXPECT_FALSE(a_disposed);
+
+		a_disposed = true;
+
+		b.unlink();
+	});
+
+	EXPECT_TRUE(a_disposed);
+	EXPECT_TRUE(list.empty());
+}
+
+/**
+ * Call clear_and_dispose(), and let the disposer push a new item.
+ */
+TEST(IntrusiveList, ClearDisposePush)
+{
+	using Item = CharItem<IntrusiveHookMode::TRACK>;
+
+	Item a{'a'}, b{'b'};
+
+	bool a_disposed = false, b_added = false, b_disposed = false;
+
+	EXPECT_FALSE(a.is_linked());
+	EXPECT_FALSE(b.is_linked());
+
+	IntrusiveList<Item> list;
+	list.push_back(a);
+
+	EXPECT_TRUE(a.is_linked());
+	EXPECT_FALSE(b.is_linked());
+
+	list.clear_and_dispose([&](Item *item){
+		if (!a_disposed) {
+			EXPECT_EQ(item, &a);
+			EXPECT_FALSE(a.is_linked());
+			EXPECT_FALSE(b.is_linked());
+			EXPECT_FALSE(a_disposed);
+			EXPECT_FALSE(b_disposed);
+			EXPECT_FALSE(b_added);
+
+			a_disposed = true;
+
+			list.push_back(b);
+			EXPECT_FALSE(a.is_linked());
+			EXPECT_TRUE(b.is_linked());
+
+			b_added = true;
+		} else if (!b_disposed) {
+			EXPECT_TRUE(b_added);
+			EXPECT_EQ(item, &b);
+			EXPECT_FALSE(a.is_linked());
+			EXPECT_FALSE(b.is_linked());
+			EXPECT_TRUE(a_disposed);
+			EXPECT_FALSE(b_disposed);
+
+			b_disposed = true;
+		} else {
+			FAIL();
+		}
+	});
+
+	EXPECT_TRUE(a_disposed);
+	EXPECT_TRUE(b_added);
+	EXPECT_TRUE(b_disposed);
+	EXPECT_TRUE(list.empty());
+}