Serenity Operating System
1/*
2 * Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibTest/TestCase.h>
8
9#include <AK/IntrusiveList.h>
10#include <AK/NonnullOwnPtr.h>
11#include <AK/RefPtr.h>
12
13class IntrusiveTestItem {
14public:
15 IntrusiveTestItem() = default;
16 IntrusiveListNode<IntrusiveTestItem> m_list_node;
17};
18using IntrusiveTestList = IntrusiveList<&IntrusiveTestItem::m_list_node>;
19
20TEST_CASE(construct)
21{
22 IntrusiveTestList empty;
23 EXPECT(empty.is_empty());
24}
25
26TEST_CASE(insert)
27{
28 IntrusiveTestList list;
29 list.append(*new IntrusiveTestItem());
30
31 EXPECT(!list.is_empty());
32
33 delete list.take_last();
34}
35
36TEST_CASE(insert_before)
37{
38 IntrusiveTestList list;
39 auto two = new IntrusiveTestItem();
40 list.append(*two);
41 auto zero = new IntrusiveTestItem();
42 list.append(*zero);
43 auto one = new IntrusiveTestItem();
44 list.insert_before(*zero, *one);
45
46 EXPECT_EQ(list.first(), two);
47 EXPECT_EQ(list.last(), zero);
48 EXPECT(list.contains(*zero));
49 EXPECT(list.contains(*one));
50 EXPECT(list.contains(*two));
51
52 EXPECT(zero->m_list_node.is_in_list());
53 EXPECT(one->m_list_node.is_in_list());
54 EXPECT(two->m_list_node.is_in_list());
55 EXPECT_EQ(list.size_slow(), 3u);
56
57 while (auto elem = list.take_first()) {
58 delete elem;
59 }
60}
61
62TEST_CASE(enumeration)
63{
64 constexpr size_t expected_size = 10;
65 IntrusiveTestList list;
66 for (size_t i = 0; i < expected_size; i++) {
67 list.append(*new IntrusiveTestItem());
68 }
69
70 size_t actual_size = 0;
71 for (auto& elem : list) {
72 (void)elem;
73 actual_size++;
74 }
75 EXPECT_EQ(expected_size, actual_size);
76 EXPECT_EQ(expected_size, list.size_slow());
77
78 size_t reverse_actual_size = 0;
79 for (auto it = list.rbegin(); it != list.rend(); ++it) {
80 reverse_actual_size++;
81 }
82 EXPECT_EQ(expected_size, reverse_actual_size);
83
84 while (auto elem = list.take_first()) {
85 delete elem;
86 }
87}
88
89class IntrusiveRefPtrItem : public RefCounted<IntrusiveRefPtrItem> {
90public:
91 IntrusiveRefPtrItem() = default;
92 IntrusiveListNode<IntrusiveRefPtrItem, RefPtr<IntrusiveRefPtrItem>> m_list_node;
93};
94using IntrusiveRefPtrList = IntrusiveList<&IntrusiveRefPtrItem::m_list_node>;
95
96TEST_CASE(intrusive_ref_ptr_no_ref_leaks)
97{
98 auto item = adopt_ref(*new IntrusiveRefPtrItem());
99 EXPECT_EQ(1u, item->ref_count());
100 IntrusiveRefPtrList ref_list;
101
102 ref_list.append(*item);
103 EXPECT_EQ(2u, item->ref_count());
104
105 ref_list.remove(*item);
106 EXPECT_EQ(1u, item->ref_count());
107}
108
109TEST_CASE(intrusive_ref_ptr_clear)
110{
111 auto item = adopt_ref(*new IntrusiveRefPtrItem());
112 EXPECT_EQ(1u, item->ref_count());
113 IntrusiveRefPtrList ref_list;
114
115 ref_list.append(*item);
116 EXPECT_EQ(2u, item->ref_count());
117
118 ref_list.clear();
119 EXPECT_EQ(1u, item->ref_count());
120}
121
122TEST_CASE(intrusive_ref_ptr_destructor)
123{
124 auto item = adopt_ref(*new IntrusiveRefPtrItem());
125 EXPECT_EQ(1u, item->ref_count());
126
127 {
128 IntrusiveRefPtrList ref_list;
129 ref_list.append(*item);
130 EXPECT_EQ(2u, item->ref_count());
131 }
132
133 EXPECT_EQ(1u, item->ref_count());
134}
135
136class IntrusiveNonnullRefPtrItem : public RefCounted<IntrusiveNonnullRefPtrItem> {
137public:
138 IntrusiveNonnullRefPtrItem() = default;
139 IntrusiveListNode<IntrusiveNonnullRefPtrItem, NonnullRefPtr<IntrusiveNonnullRefPtrItem>> m_list_node;
140};
141using IntrusiveNonnullRefPtrList = IntrusiveList<&IntrusiveNonnullRefPtrItem::m_list_node>;
142
143TEST_CASE(intrusive_nonnull_ref_ptr_intrusive)
144{
145 auto item = adopt_ref(*new IntrusiveNonnullRefPtrItem());
146 EXPECT_EQ(1u, item->ref_count());
147 IntrusiveNonnullRefPtrList nonnull_ref_list;
148
149 nonnull_ref_list.append(*item);
150 EXPECT_EQ(2u, item->ref_count());
151 EXPECT(!nonnull_ref_list.is_empty());
152
153 nonnull_ref_list.remove(*item);
154 EXPECT_EQ(1u, item->ref_count());
155
156 EXPECT(nonnull_ref_list.is_empty());
157}