Bitcoin Core  28.1.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 
11 BOOST_AUTO_TEST_SUITE(coinscachepair_tests)
12 
13 static constexpr auto NUM_NODES{4};
14 
15 std::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())};
22  node->second.AddFlags(CCoinsCacheEntry::DIRTY, *node, sentinel);
23 
24  BOOST_CHECK_EQUAL(node->second.GetFlags(), CCoinsCacheEntry::DIRTY);
25  BOOST_CHECK_EQUAL(node->second.Next(), &sentinel);
26  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &(*node));
27 
28  if (i > 0) {
29  BOOST_CHECK_EQUAL(std::prev(node)->second.Next(), &(*node));
30  BOOST_CHECK_EQUAL(node->second.Prev(), &(*std::prev(node)));
31  }
32  }
33  return nodes;
34 }
35 
36 BOOST_AUTO_TEST_CASE(linked_list_iteration)
37 {
38  CoinsCachePair sentinel;
39  sentinel.second.SelfRef(sentinel);
40  auto nodes{CreatePairs(sentinel)};
41 
42  // Check iterating through pairs is identical to iterating through a list
43  auto node{sentinel.second.Next()};
44  for (const auto& expected : nodes) {
45  BOOST_CHECK_EQUAL(&expected, node);
46  node = node->second.Next();
47  }
48  BOOST_CHECK_EQUAL(node, &sentinel);
49 
50  // Check iterating through pairs is identical to iterating through a list
51  // Clear the flags during iteration
52  node = sentinel.second.Next();
53  for (const auto& expected : nodes) {
54  BOOST_CHECK_EQUAL(&expected, node);
55  auto next = node->second.Next();
56  node->second.ClearFlags();
57  node = next;
58  }
59  BOOST_CHECK_EQUAL(node, &sentinel);
60  // Check that sentinel's next and prev are itself
61  BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
62  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
63 
64  // Delete the nodes from the list to make sure there are no dangling pointers
65  for (auto it{nodes.begin()}; it != nodes.end(); it = nodes.erase(it)) {
66  BOOST_CHECK_EQUAL(it->second.GetFlags(), 0);
67  }
68 }
69 
70 BOOST_AUTO_TEST_CASE(linked_list_iterate_erase)
71 {
72  CoinsCachePair sentinel;
73  sentinel.second.SelfRef(sentinel);
74  auto nodes{CreatePairs(sentinel)};
75 
76  // Check iterating through pairs is identical to iterating through a list
77  // Erase the nodes as we iterate through, but don't clear flags
78  // The flags will be cleared by the CCoinsCacheEntry's destructor
79  auto node{sentinel.second.Next()};
80  for (auto expected{nodes.begin()}; expected != nodes.end(); expected = nodes.erase(expected)) {
81  BOOST_CHECK_EQUAL(&(*expected), node);
82  node = node->second.Next();
83  }
84  BOOST_CHECK_EQUAL(node, &sentinel);
85 
86  // Check that sentinel's next and prev are itself
87  BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
88  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
89 }
90 
91 BOOST_AUTO_TEST_CASE(linked_list_random_deletion)
92 {
93  CoinsCachePair sentinel;
94  sentinel.second.SelfRef(sentinel);
95  auto nodes{CreatePairs(sentinel)};
96 
97  // Create linked list sentinel->n1->n2->n3->n4->sentinel
98  auto n1{nodes.begin()};
99  auto n2{std::next(n1)};
100  auto n3{std::next(n2)};
101  auto n4{std::next(n3)};
102 
103  // Delete n2
104  // sentinel->n1->n3->n4->sentinel
105  nodes.erase(n2);
106  // Check that n1 now points to n3, and n3 still points to n4
107  // Also check that flags were not altered
108  BOOST_CHECK_EQUAL(n1->second.GetFlags(), CCoinsCacheEntry::DIRTY);
109  BOOST_CHECK_EQUAL(n1->second.Next(), &(*n3));
110  BOOST_CHECK_EQUAL(n3->second.GetFlags(), CCoinsCacheEntry::DIRTY);
111  BOOST_CHECK_EQUAL(n3->second.Next(), &(*n4));
112  BOOST_CHECK_EQUAL(n3->second.Prev(), &(*n1));
113 
114  // Delete n1
115  // sentinel->n3->n4->sentinel
116  nodes.erase(n1);
117  // Check that sentinel now points to n3, and n3 still points to n4
118  // Also check that flags were not altered
119  BOOST_CHECK_EQUAL(n3->second.GetFlags(), CCoinsCacheEntry::DIRTY);
120  BOOST_CHECK_EQUAL(sentinel.second.Next(), &(*n3));
121  BOOST_CHECK_EQUAL(n3->second.Next(), &(*n4));
122  BOOST_CHECK_EQUAL(n3->second.Prev(), &sentinel);
123 
124  // Delete n4
125  // sentinel->n3->sentinel
126  nodes.erase(n4);
127  // Check that sentinel still points to n3, and n3 points to sentinel
128  // Also check that flags were not altered
129  BOOST_CHECK_EQUAL(n3->second.GetFlags(), CCoinsCacheEntry::DIRTY);
130  BOOST_CHECK_EQUAL(sentinel.second.Next(), &(*n3));
131  BOOST_CHECK_EQUAL(n3->second.Next(), &sentinel);
132  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &(*n3));
133 
134  // Delete n3
135  // sentinel->sentinel
136  nodes.erase(n3);
137  // Check that sentinel's next and prev are itself
138  BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
139  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
140 }
141 
142 BOOST_AUTO_TEST_CASE(linked_list_add_flags)
143 {
144  CoinsCachePair sentinel;
145  sentinel.second.SelfRef(sentinel);
146  CoinsCachePair n1;
147  CoinsCachePair n2;
148 
149  // Check that adding 0 flag has no effect
150  n1.second.AddFlags(0, n1, sentinel);
151  BOOST_CHECK_EQUAL(n1.second.GetFlags(), 0);
152  BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
153  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
154 
155  // Check that adding DIRTY flag inserts it into linked list and sets flags
156  n1.second.AddFlags(CCoinsCacheEntry::DIRTY, n1, sentinel);
157  BOOST_CHECK_EQUAL(n1.second.GetFlags(), CCoinsCacheEntry::DIRTY);
158  BOOST_CHECK_EQUAL(n1.second.Next(), &sentinel);
159  BOOST_CHECK_EQUAL(n1.second.Prev(), &sentinel);
160  BOOST_CHECK_EQUAL(sentinel.second.Next(), &n1);
161  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n1);
162 
163  // Check that adding FRESH flag on new node inserts it after n1
164  n2.second.AddFlags(CCoinsCacheEntry::FRESH, n2, sentinel);
165  BOOST_CHECK_EQUAL(n2.second.GetFlags(), CCoinsCacheEntry::FRESH);
166  BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
167  BOOST_CHECK_EQUAL(n2.second.Prev(), &n1);
168  BOOST_CHECK_EQUAL(n1.second.Next(), &n2);
169  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
170 
171  // Check that adding 0 flag has no effect, and doesn't change position
172  n1.second.AddFlags(0, n1, sentinel);
173  BOOST_CHECK_EQUAL(n1.second.GetFlags(), CCoinsCacheEntry::DIRTY);
174  BOOST_CHECK_EQUAL(n1.second.Next(), &n2);
175  BOOST_CHECK_EQUAL(n1.second.Prev(), &sentinel);
176  BOOST_CHECK_EQUAL(sentinel.second.Next(), &n1);
177  BOOST_CHECK_EQUAL(n2.second.Prev(), &n1);
178 
179  // Check that we can add extra flags, but they don't change our position
180  n1.second.AddFlags(CCoinsCacheEntry::FRESH, n1, sentinel);
182  BOOST_CHECK_EQUAL(n1.second.Next(), &n2);
183  BOOST_CHECK_EQUAL(n1.second.Prev(), &sentinel);
184  BOOST_CHECK_EQUAL(sentinel.second.Next(), &n1);
185  BOOST_CHECK_EQUAL(n2.second.Prev(), &n1);
186 
187  // Check that we can clear flags then re-add them
188  n1.second.ClearFlags();
189  BOOST_CHECK_EQUAL(n1.second.GetFlags(), 0);
190  BOOST_CHECK_EQUAL(sentinel.second.Next(), &n2);
191  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
192  BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
193  BOOST_CHECK_EQUAL(n2.second.Prev(), &sentinel);
194 
195  // Check that calling `ClearFlags` with 0 flags has no effect
196  n1.second.ClearFlags();
197  BOOST_CHECK_EQUAL(n1.second.GetFlags(), 0);
198  BOOST_CHECK_EQUAL(sentinel.second.Next(), &n2);
199  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
200  BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
201  BOOST_CHECK_EQUAL(n2.second.Prev(), &sentinel);
202 
203  // Adding 0 still has no effect
204  n1.second.AddFlags(0, n1, sentinel);
205  BOOST_CHECK_EQUAL(sentinel.second.Next(), &n2);
206  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
207  BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
208  BOOST_CHECK_EQUAL(n2.second.Prev(), &sentinel);
209 
210  // But adding DIRTY re-inserts it after n2
211  n1.second.AddFlags(CCoinsCacheEntry::DIRTY, n1, sentinel);
212  BOOST_CHECK_EQUAL(n1.second.GetFlags(), CCoinsCacheEntry::DIRTY);
213  BOOST_CHECK_EQUAL(n2.second.Next(), &n1);
214  BOOST_CHECK_EQUAL(n1.second.Prev(), &n2);
215  BOOST_CHECK_EQUAL(n1.second.Next(), &sentinel);
216  BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n1);
217 }
218 
BOOST_AUTO_TEST_CASE(linked_list_iteration)
DIRTY means the CCoinsCacheEntry is potentially different from the version in the parent cache...
Definition: coins.h:142
std::pair< const COutPoint, CCoinsCacheEntry > CoinsCachePair
Definition: coins.h:91
BOOST_AUTO_TEST_SUITE_END()
static constexpr auto NUM_NODES
std::list< CoinsCachePair > CreatePairs(CoinsCachePair &sentinel)
Definition: messages.h:20
FRESH means the parent cache does not have this coin or that it is a spent coin in the parent cache...
Definition: coins.h:152
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
BOOST_AUTO_TEST_SUITE(cuckoocache_tests)
Test Suite for CuckooCache.