Bitcoin Core 31.99.0
P2P Digital Currency
coinscachepair_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2024-present The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <coins.h>
6
7#include <boost/test/unit_test.hpp>
8
9#include <list>
10
11BOOST_AUTO_TEST_SUITE(coinscachepair_tests)
12
13static constexpr auto NUM_NODES{4};
14
15std::list<CoinsCachePair> CreatePairs(CoinsCachePair& sentinel)
16{
17 std::list<CoinsCachePair> nodes;
18 for (auto i{0}; i < NUM_NODES; ++i) {
19 nodes.emplace_back();
20
21 auto node{std::prev(nodes.end())};
23
24 BOOST_CHECK(node->second.IsDirty());
25 BOOST_CHECK(!node->second.IsFresh());
26 BOOST_CHECK_EQUAL(node->second.Next(), &sentinel);
27 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &(*node));
28
29 if (i > 0) {
30 BOOST_CHECK_EQUAL(std::prev(node)->second.Next(), &(*node));
31 BOOST_CHECK_EQUAL(node->second.Prev(), &(*std::prev(node)));
32 }
33 }
34 return nodes;
35}
36
37BOOST_AUTO_TEST_CASE(linked_list_iteration)
38{
39 CoinsCachePair sentinel;
40 sentinel.second.SelfRef(sentinel);
41 auto nodes{CreatePairs(sentinel)};
42
43 // Check iterating through pairs is identical to iterating through a list
44 auto node{sentinel.second.Next()};
45 for (const auto& expected : nodes) {
46 BOOST_CHECK_EQUAL(&expected, node);
47 node = node->second.Next();
48 }
49 BOOST_CHECK_EQUAL(node, &sentinel);
50
51 // Check iterating through pairs is identical to iterating through a list
52 // Clear the state during iteration
53 node = sentinel.second.Next();
54 for (const auto& expected : nodes) {
55 BOOST_CHECK_EQUAL(&expected, node);
56 auto next = node->second.Next();
57 node->second.SetClean();
58 node = next;
59 }
60 BOOST_CHECK_EQUAL(node, &sentinel);
61 // Check that sentinel's next and prev are itself
62 BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
63 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
64
65 // Delete the nodes from the list to make sure there are no dangling pointers
66 for (auto it{nodes.begin()}; it != nodes.end(); it = nodes.erase(it)) {
67 BOOST_CHECK(!it->second.IsDirty());
68 BOOST_CHECK(!it->second.IsFresh());
69 }
70}
71
72BOOST_AUTO_TEST_CASE(linked_list_iterate_erase)
73{
74 CoinsCachePair sentinel;
75 sentinel.second.SelfRef(sentinel);
76 auto nodes{CreatePairs(sentinel)};
77
78 // Check iterating through pairs is identical to iterating through a list
79 // Erase the nodes as we iterate through, but don't clear state
80 // The state will be cleared by the CCoinsCacheEntry's destructor
81 auto node{sentinel.second.Next()};
82 for (auto expected{nodes.begin()}; expected != nodes.end(); expected = nodes.erase(expected)) {
83 BOOST_CHECK_EQUAL(&(*expected), node);
84 node = node->second.Next();
85 }
86 BOOST_CHECK_EQUAL(node, &sentinel);
87
88 // Check that sentinel's next and prev are itself
89 BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
90 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
91}
92
93BOOST_AUTO_TEST_CASE(linked_list_random_deletion)
94{
95 CoinsCachePair sentinel;
96 sentinel.second.SelfRef(sentinel);
97 auto nodes{CreatePairs(sentinel)};
98
99 // Create linked list sentinel->n1->n2->n3->n4->sentinel
100 auto n1{nodes.begin()};
101 auto n2{std::next(n1)};
102 auto n3{std::next(n2)};
103 auto n4{std::next(n3)};
104
105 // Delete n2
106 // sentinel->n1->n3->n4->sentinel
107 nodes.erase(n2);
108 // Check that n1 now points to n3, and n3 still points to n4
109 // Also check that state was not altered
110 BOOST_CHECK(n1->second.IsDirty());
111 BOOST_CHECK(!n1->second.IsFresh());
112 BOOST_CHECK_EQUAL(n1->second.Next(), &(*n3));
113 BOOST_CHECK(n3->second.IsDirty());
114 BOOST_CHECK(!n3->second.IsFresh());
115 BOOST_CHECK_EQUAL(n3->second.Next(), &(*n4));
116 BOOST_CHECK_EQUAL(n3->second.Prev(), &(*n1));
117
118 // Delete n1
119 // sentinel->n3->n4->sentinel
120 nodes.erase(n1);
121 // Check that sentinel now points to n3, and n3 still points to n4
122 // Also check that state was not altered
123 BOOST_CHECK(n3->second.IsDirty());
124 BOOST_CHECK(!n3->second.IsFresh());
125 BOOST_CHECK_EQUAL(sentinel.second.Next(), &(*n3));
126 BOOST_CHECK_EQUAL(n3->second.Next(), &(*n4));
127 BOOST_CHECK_EQUAL(n3->second.Prev(), &sentinel);
128
129 // Delete n4
130 // sentinel->n3->sentinel
131 nodes.erase(n4);
132 // Check that sentinel still points to n3, and n3 points to sentinel
133 // Also check that state was not altered
134 BOOST_CHECK(n3->second.IsDirty());
135 BOOST_CHECK(!n3->second.IsFresh());
136 BOOST_CHECK_EQUAL(sentinel.second.Next(), &(*n3));
137 BOOST_CHECK_EQUAL(n3->second.Next(), &sentinel);
138 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &(*n3));
139
140 // Delete n3
141 // sentinel->sentinel
142 nodes.erase(n3);
143 // Check that sentinel's next and prev are itself
144 BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
145 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
146}
147
148BOOST_AUTO_TEST_CASE(linked_list_set_state)
149{
150 CoinsCachePair sentinel;
151 sentinel.second.SelfRef(sentinel);
154
155 // Check that setting DIRTY inserts it into linked list and sets state
156 CCoinsCacheEntry::SetDirty(n1, sentinel);
157 BOOST_CHECK(n1.second.IsDirty());
158 BOOST_CHECK(!n1.second.IsFresh());
159 BOOST_CHECK_EQUAL(n1.second.Next(), &sentinel);
160 BOOST_CHECK_EQUAL(n1.second.Prev(), &sentinel);
161 BOOST_CHECK_EQUAL(sentinel.second.Next(), &n1);
162 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n1);
163
164 // Check that setting FRESH on new node inserts it after n1
165 CCoinsCacheEntry::SetFresh(n2, sentinel);
166 BOOST_CHECK(n2.second.IsFresh());
167 BOOST_CHECK(!n2.second.IsDirty());
168 BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
169 BOOST_CHECK_EQUAL(n2.second.Prev(), &n1);
170 BOOST_CHECK_EQUAL(n1.second.Next(), &n2);
171 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
172
173 // Check that we can set extra state, but they don't change our position
174 CCoinsCacheEntry::SetFresh(n1, sentinel);
175 BOOST_CHECK(n1.second.IsDirty());
176 BOOST_CHECK(n1.second.IsFresh());
177 BOOST_CHECK_EQUAL(n1.second.Next(), &n2);
178 BOOST_CHECK_EQUAL(n1.second.Prev(), &sentinel);
179 BOOST_CHECK_EQUAL(sentinel.second.Next(), &n1);
180 BOOST_CHECK_EQUAL(n2.second.Prev(), &n1);
181
182 // Check that we can clear state then re-set it
183 n1.second.SetClean();
184 BOOST_CHECK(!n1.second.IsDirty());
185 BOOST_CHECK(!n1.second.IsFresh());
186 BOOST_CHECK_EQUAL(sentinel.second.Next(), &n2);
187 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
188 BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
189 BOOST_CHECK_EQUAL(n2.second.Prev(), &sentinel);
190
191 // Calling `SetClean` a second time has no effect
192 n1.second.SetClean();
193 BOOST_CHECK(!n1.second.IsDirty());
194 BOOST_CHECK(!n1.second.IsFresh());
195 BOOST_CHECK_EQUAL(sentinel.second.Next(), &n2);
196 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
197 BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
198 BOOST_CHECK_EQUAL(n2.second.Prev(), &sentinel);
199
200 // Adding DIRTY re-inserts it after n2
201 CCoinsCacheEntry::SetDirty(n1, sentinel);
202 BOOST_CHECK(n1.second.IsDirty());
203 BOOST_CHECK(!n1.second.IsFresh());
204 BOOST_CHECK_EQUAL(n2.second.Next(), &n1);
205 BOOST_CHECK_EQUAL(n1.second.Prev(), &n2);
206 BOOST_CHECK_EQUAL(n1.second.Next(), &sentinel);
207 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n1);
208}
209
std::pair< const COutPoint, CCoinsCacheEntry > CoinsCachePair
Definition: coins.h:104
BOOST_AUTO_TEST_CASE(linked_list_iteration)
static constexpr auto NUM_NODES
std::list< CoinsCachePair > CreatePairs(CoinsCachePair &sentinel)
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_EQUAL(headers.FindFirst("key"), "value")
Definition: messages.h:21
#define BOOST_CHECK(expr)
Definition: object.cpp:16
static void SetFresh(CoinsCachePair &pair, CoinsCachePair &sentinel) noexcept
Definition: coins.h:184
static void SetDirty(CoinsCachePair &pair, CoinsCachePair &sentinel) noexcept
Definition: coins.h:183