xrpld
Loading...
Searching...
No Matches
TaggedCache_test.cpp
1#include <test/unit_test/SuiteJournal.h>
2
3#include <xrpl/basics/IntrusivePointer.h>
4#include <xrpl/basics/IntrusiveRefCounts.h>
5#include <xrpl/basics/TaggedCache.h>
6#include <xrpl/basics/TaggedCache.ipp> // IWYU pragma: keep
7#include <xrpl/basics/chrono.h>
8#include <xrpl/beast/unit_test/suite.h>
9#include <xrpl/beast/utility/Journal.h>
10#include <xrpl/protocol/Protocol.h>
11
12#include <memory>
13#include <utility>
14
15namespace xrpl {
16
17/*
18I guess you can put some items in, make sure they're still there. Let some
19time pass, make sure they're gone. Keep a strong pointer to one of them, make
20sure you can still find it even after time passes. Create two objects with
21the same key, canonicalize them both and make sure you get the same object.
22Put an object in but keep a strong pointer to it, advance the clock a lot,
23then canonicalize a new object with the same key, make sure you get the
24original object.
25*/
26
28{
29public:
30 void
31 run() override
32 {
33 using namespace std::chrono_literals;
34 using beast::Severity;
35 test::SuiteJournal journal("TaggedCache_test", *this);
36
37 TestStopwatch clock;
38 clock.set(0);
39
40 using Key = LedgerIndex;
41 using Value = std::string;
42 using Cache = TaggedCache<Key, Value>;
43
44 Cache c("test", 1, 1s, clock, journal);
45
46 // Insert an item, retrieve it, and age it so it gets purged.
47 {
48 BEAST_EXPECT(c.getCacheSize() == 0);
49 BEAST_EXPECT(c.getTrackSize() == 0);
50 BEAST_EXPECT(!c.insert(1, "one"));
51 BEAST_EXPECT(c.getCacheSize() == 1);
52 BEAST_EXPECT(c.getTrackSize() == 1);
53
54 {
56 BEAST_EXPECT(c.retrieve(1, s));
57 BEAST_EXPECT(s == "one");
58 }
59
60 ++clock;
61 c.sweep();
62 BEAST_EXPECT(c.getCacheSize() == 0);
63 BEAST_EXPECT(c.getTrackSize() == 0);
64 }
65
66 // Insert an item, maintain a strong pointer, age it, and
67 // verify that the entry still exists.
68 {
69 BEAST_EXPECT(!c.insert(2, "two"));
70 BEAST_EXPECT(c.getCacheSize() == 1);
71 BEAST_EXPECT(c.getTrackSize() == 1);
72
73 {
74 auto p = c.fetch(2);
75 BEAST_EXPECT(p != nullptr);
76 ++clock;
77 c.sweep();
78 BEAST_EXPECT(c.getCacheSize() == 0);
79 BEAST_EXPECT(c.getTrackSize() == 1);
80 }
81
82 // Make sure its gone now that our reference is gone
83 ++clock;
84 c.sweep();
85 BEAST_EXPECT(c.getCacheSize() == 0);
86 BEAST_EXPECT(c.getTrackSize() == 0);
87 }
88
89 // Insert the same key/value pair and make sure we get the same result
90 {
91 BEAST_EXPECT(!c.insert(3, "three"));
92
93 {
94 auto const p1 = c.fetch(3);
95 auto p2 = std::make_shared<Value>("three");
96 c.canonicalizeReplaceClient(3, p2);
97 BEAST_EXPECT(p1.get() == p2.get());
98 }
99 ++clock;
100 c.sweep();
101 BEAST_EXPECT(c.getCacheSize() == 0);
102 BEAST_EXPECT(c.getTrackSize() == 0);
103 }
104
105 // Put an object in but keep a strong pointer to it, advance the clock a
106 // lot, then canonicalize a new object with the same key, make sure you
107 // get the original object.
108 {
109 // Put an object in
110 BEAST_EXPECT(!c.insert(4, "four"));
111 BEAST_EXPECT(c.getCacheSize() == 1);
112 BEAST_EXPECT(c.getTrackSize() == 1);
113
114 {
115 // Keep a strong pointer to it
116 auto const p1 = c.fetch(4);
117 BEAST_EXPECT(p1 != nullptr);
118 BEAST_EXPECT(c.getCacheSize() == 1);
119 BEAST_EXPECT(c.getTrackSize() == 1);
120 // Advance the clock a lot
121 ++clock;
122 c.sweep();
123 BEAST_EXPECT(c.getCacheSize() == 0);
124 BEAST_EXPECT(c.getTrackSize() == 1);
125 // Canonicalize a new object with the same key
126 auto p2 = std::make_shared<std::string>("four");
127 BEAST_EXPECT(c.canonicalizeReplaceClient(4, p2));
128 BEAST_EXPECT(c.getCacheSize() == 1);
129 BEAST_EXPECT(c.getTrackSize() == 1);
130 // Make sure we get the original object
131 BEAST_EXPECT(p1.get() == p2.get());
132 }
133
134 ++clock;
135 c.sweep();
136 BEAST_EXPECT(c.getCacheSize() == 0);
137 BEAST_EXPECT(c.getTrackSize() == 0);
138 }
139 {
140 BEAST_EXPECT(!c.insert(5, "five"));
141 BEAST_EXPECT(c.getCacheSize() == 1);
142 BEAST_EXPECT(c.size() == 1);
143
144 {
145 auto const p1 = c.fetch(5);
146 BEAST_EXPECT(p1 != nullptr);
147 BEAST_EXPECT(c.getCacheSize() == 1);
148 BEAST_EXPECT(c.size() == 1);
149
150 // Advance the clock a lot
151 ++clock;
152 c.sweep();
153 BEAST_EXPECT(c.getCacheSize() == 0);
154 BEAST_EXPECT(c.size() == 1);
155
156 auto p2 = std::make_shared<std::string>("five_2");
157 BEAST_EXPECT(c.canonicalizeReplaceCache(5, p2));
158 BEAST_EXPECT(c.getCacheSize() == 1);
159 BEAST_EXPECT(c.size() == 1);
160 // Make sure the caller's original pointer is unchanged
161 BEAST_EXPECT(p1.get() != p2.get());
162 BEAST_EXPECT(*p2 == "five_2");
163
164 auto const p3 = c.fetch(5);
165 BEAST_EXPECT(p3 != nullptr);
166 BEAST_EXPECT(p3.get() == p2.get());
167 BEAST_EXPECT(p3.get() != p1.get());
168 }
169
170 ++clock;
171 c.sweep();
172 BEAST_EXPECT(c.getCacheSize() == 0);
173 BEAST_EXPECT(c.size() == 0);
174 }
175
176 {
177 testcase("intrptr");
178
179 struct MyRefCountObject : IntrusiveRefCounts
180 {
181 std::string data;
182
183 // Needed to support weak intrusive pointers
184 virtual void
185 partialDestructor() {};
186
187 MyRefCountObject() = default;
188 explicit MyRefCountObject(std::string data) : data(std::move(data))
189 {
190 }
191
192 bool
193 operator==(std::string const& other) const
194 {
195 return data == other;
196 }
197 };
198
199 using IntrPtrCache = TaggedCache<
200 Key,
201 MyRefCountObject,
202 /*IsKeyCache*/ false,
205
206 IntrPtrCache intrPtrCache("IntrPtrTest", 1, 1s, clock, journal);
207
208 intrPtrCache.canonicalizeReplaceCache(1, intr_ptr::makeShared<MyRefCountObject>("one"));
209 BEAST_EXPECT(intrPtrCache.getCacheSize() == 1);
210 BEAST_EXPECT(intrPtrCache.size() == 1);
211
212 {
213 {
214 intrPtrCache.canonicalizeReplaceCache(
215 1, intr_ptr::makeShared<MyRefCountObject>("one_replaced"));
216
217 auto p = intrPtrCache.fetch(1);
218 BEAST_EXPECT(*p == "one_replaced");
219
220 // Advance the clock a lot
221 ++clock;
222 intrPtrCache.sweep();
223 BEAST_EXPECT(intrPtrCache.getCacheSize() == 0);
224 BEAST_EXPECT(intrPtrCache.size() == 1);
225
226 intrPtrCache.canonicalizeReplaceCache(
227 1, intr_ptr::makeShared<MyRefCountObject>("one_replaced_2"));
228
229 auto p2 = intrPtrCache.fetch(1);
230 BEAST_EXPECT(*p2 == "one_replaced_2");
231
232 intrPtrCache.del(1, true);
233 }
234
235 intrPtrCache.canonicalizeReplaceCache(
236 1, intr_ptr::makeShared<MyRefCountObject>("one_replaced_3"));
237 auto p3 = intrPtrCache.fetch(1);
238 BEAST_EXPECT(*p3 == "one_replaced_3");
239 }
240
241 ++clock;
242 intrPtrCache.sweep();
243 BEAST_EXPECT(intrPtrCache.getCacheSize() == 0);
244 BEAST_EXPECT(intrPtrCache.size() == 0);
245 }
246 }
247};
248
250
251} // namespace xrpl
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
void run() override
Runs the suite.
Map/cache combination.
Definition TaggedCache.h:59
T make_shared(T... args)
Severity
Severity level / threshold of a Journal message.
Definition Journal.h:11
SharedWeakUnion< T > SharedWeakUnionPtr
SharedIntrusive< T > SharedPtr
SharedPtr< T > makeShared(A &&... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
constexpr bool operator==(BaseUInt< Bits, Tag > const &lhs, BaseUInt< Bits, Tag > const &rhs)
Definition base_uint.h:588
std::uint32_t LedgerIndex
A ledger index.
Definition Protocol.h:259
beast::ManualClock< std::chrono::steady_clock > TestStopwatch
A manual Stopwatch for unit tests.
Definition chrono.h:90
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
Implement the strong count, weak count, and bit flags for an intrusive pointer.