xrpld
Toggle main menu visibility
Loading...
Searching...
No Matches
tests
libxrpl
nodestore
tests/libxrpl/nodestore/NuDBFactory.cpp
1
#include <xrpl/basics/ByteUtilities.h>
2
#include <xrpl/basics/FileUtilities.h>
3
#include <xrpl/beast/utility/Journal.h>
4
#include <xrpl/config/BasicConfig.h>
5
#include <xrpl/nodestore/DummyScheduler.h>
6
#include <xrpl/nodestore/Manager.h>
7
8
#include <gtest/gtest.h>
9
#include <helpers/CaptureSink.h>
10
#include <helpers/TestSink.h>
11
#include <nodestore/TestBase.h>
12
13
#include <
array
>
14
#include <
cstddef
>
15
#include <
exception
>
16
#include <
memory
>
17
#include <
string
>
18
#include <
utility
>
19
#include <
vector
>
20
21
namespace
xrpl::node_store
{
22
23
namespace
{
24
25
Section
26
makeSection(std::string
const
& path, std::string
const
& blockSize =
""
)
27
{
28
Section params;
29
params.set(
"type"
,
"nudb"
);
30
params.set(
"path"
, path);
31
if
(!blockSize.empty())
32
params.set(
"nudb_block_size"
, blockSize);
33
return
params;
34
}
35
36
void
37
runRoundTrip(Section
const
& params, std::size_t expectedBlocksize)
38
{
39
DummyScheduler
scheduler;
40
beast::Journal
const
journal(
TestSink::instance
());
41
auto
backend =
Manager::instance
().
makeBackend
(params,
megabytes
(4), scheduler, journal);
42
43
ASSERT_TRUE(backend);
44
ASSERT_EQ(backend->getBlockSize(), expectedBlocksize);
45
backend->open();
46
ASSERT_TRUE(backend->isOpen());
47
48
auto
const
batch =
createPredictableBatch
(10, 12345);
49
storeBatch
(*backend, batch);
50
51
auto
const
copy
=
fetchCopyOfBatch
(*backend, batch);
52
53
backend->close();
54
EXPECT_EQ(batch, copy);
55
}
56
57
}
// namespace
58
59
TEST
(
NuDBFactory
, default_block_size)
60
{
61
TempDir
const
tempDir;
62
auto
const
params = makeSection(tempDir.
path
());
63
ASSERT_NO_FATAL_FAILURE(runRoundTrip(params, 4096));
64
}
65
66
TEST
(
NuDBFactory
, valid_block_sizes)
67
{
68
auto
const
kValidSizes = std::to_array<std::size_t>({4096, 8192, 16384, 32768});
69
for
(
auto
const
size : kValidSizes)
70
{
71
SCOPED_TRACE(
"size="
+
std::to_string
(size));
72
TempDir
const
tempDir;
73
auto
const
params = makeSection(tempDir.
path
(),
std::to_string
(size));
74
ASSERT_NO_FATAL_FAILURE(runRoundTrip(params, size));
75
}
76
77
// empty value is ignored by config parser; default (4096) is used
78
{
79
TempDir
const
tempDir;
80
auto
const
params = makeSection(tempDir.
path
(),
""
);
81
ASSERT_NO_FATAL_FAILURE(runRoundTrip(params, 4096));
82
}
83
}
84
85
TEST
(
NuDBFactory
, invalid_block_sizes)
86
{
87
std::vector<std::string>
const
kInvalidSizes = {
88
"2048"
,
// too small
89
"1024"
,
// too small
90
"65536"
,
// too large
91
"131072"
,
// too large
92
"5000"
,
// not power of 2
93
"6000"
,
// not power of 2
94
"10000"
,
// not power of 2
95
"0"
,
// zero
96
"-1"
,
// negative
97
"abc"
,
// non-numeric
98
"4k"
,
// invalid format
99
"4096.5"
};
// decimal
100
101
for
(
auto
const
& size : kInvalidSizes)
102
{
103
SCOPED_TRACE(
"size='"
+ size +
"'"
);
104
TempDir
const
tempDir;
105
auto
const
params = makeSection(tempDir.
path
(), size);
106
EXPECT_THROW(runRoundTrip(params, 4096),
std::exception
);
107
}
108
109
// whitespace handling — lexical_cast may or may not strip; treat as invalid
110
std::vector<std::string>
const
kWhitespaceSizes = {
"4096 "
,
" 4096"
};
111
for
(
auto
const
& size : kWhitespaceSizes)
112
{
113
SCOPED_TRACE(
"size='"
+ size +
"'"
);
114
TempDir
const
tempDir;
115
auto
const
params = makeSection(tempDir.
path
(), size);
116
EXPECT_THROW(runRoundTrip(params, 4096),
std::exception
);
117
}
118
}
119
120
TEST
(
NuDBFactory
, log_messages)
121
{
122
// valid custom block size emits info log
123
{
124
TempDir
const
tempDir;
125
auto
const
params = makeSection(tempDir.
path
(),
"8192"
);
126
test::CaptureSink
sink(
beast::Severity::Info
);
127
beast::Journal
const
journal(sink);
128
129
DummyScheduler
scheduler;
130
[[maybe_unused]]
auto
backend =
131
Manager::instance
().
makeBackend
(params,
megabytes
(4), scheduler, journal);
132
133
EXPECT_TRUE(sink.
messages
().contains(
"Using custom NuDB block size: 8192"
));
134
}
135
136
// invalid block size throws with informative message
137
{
138
TempDir
const
tempDir;
139
auto
const
params = makeSection(tempDir.
path
(),
"5000"
);
140
test::CaptureSink
sink(
beast::Severity::Warning
);
141
beast::Journal
const
journal(sink);
142
DummyScheduler
scheduler;
143
try
144
{
145
auto
backend =
146
Manager::instance
().
makeBackend
(params,
megabytes
(4), scheduler, journal);
147
FAIL() <<
"expected exception for invalid block size 5000"
;
148
}
149
catch
(
std::exception
const
& e)
150
{
151
std::string
const
what{e.
what
()};
152
EXPECT_TRUE(what.contains(
"Invalid nudb_block_size: 5000"
));
153
EXPECT_TRUE(what.contains(
"Must be power of 2 between 4096 and 32768"
));
154
}
155
}
156
157
// non-numeric value throws
158
{
159
TempDir
const
tempDir;
160
auto
const
params = makeSection(tempDir.
path
(),
"invalid"
);
161
test::CaptureSink
sink(
beast::Severity::Warning
);
162
beast::Journal
const
journal(sink);
163
DummyScheduler
scheduler;
164
try
165
{
166
auto
backend =
167
Manager::instance
().
makeBackend
(params,
megabytes
(4), scheduler, journal);
168
FAIL() <<
"expected exception for non-numeric block size"
;
169
}
170
catch
(
std::exception
const
& e)
171
{
172
std::string
const
what{e.
what
()};
173
EXPECT_TRUE(what.contains(
"Invalid nudb_block_size value: invalid"
));
174
}
175
}
176
}
177
178
TEST
(
NuDBFactory
, power_of_two_validation)
179
{
180
std::vector<std::pair<std::string, bool>
>
const
kCASES = {
181
{
"4095"
,
false
},
// just below minimum
182
{
"4096"
,
true
},
// minimum valid
183
{
"4097"
,
false
},
// not power of 2
184
{
"8192"
,
true
},
// valid power of 2
185
{
"8193"
,
false
},
// not power of 2
186
{
"16384"
,
true
},
// valid power of 2
187
{
"32768"
,
true
},
// maximum valid
188
{
"32769"
,
false
},
// just above maximum
189
{
"65536"
,
false
}};
// power of 2 but too large
190
191
for
(
auto
const
& [size, shouldWork] : kCASES)
192
{
193
SCOPED_TRACE(
"size="
+ size +
" shouldWork="
+ (shouldWork ?
"true"
:
"false"
));
194
TempDir
const
tempDir;
195
auto
const
params = makeSection(tempDir.
path
(), size);
196
test::CaptureSink
sink(
beast::Severity::Warning
);
197
beast::Journal
const
journal(sink);
198
DummyScheduler
scheduler;
199
try
200
{
201
auto
backend =
202
Manager::instance
().
makeBackend
(params,
megabytes
(4), scheduler, journal);
203
EXPECT_TRUE(shouldWork);
204
}
205
catch
(
std::exception
const
& e)
206
{
207
// A throw is only expected for sizes that should NOT work; if a
208
// valid size throws, fail here instead of silently matching the
209
// message below (which would mask the regression).
210
EXPECT_FALSE(shouldWork);
211
std::string
const
what{e.
what
()};
212
EXPECT_TRUE(what.contains(
"Invalid nudb_block_size"
));
213
}
214
}
215
}
216
217
TEST
(
NuDBFactory
, both_constructor_variants)
218
{
219
TempDir
const
tempDir;
220
auto
const
params = makeSection(tempDir.
path
(),
"16384"
);
221
DummyScheduler
scheduler;
222
beast::Journal
const
journal(
TestSink::instance
());
223
224
auto
backend1 =
Manager::instance
().
makeBackend
(params,
megabytes
(4), scheduler, journal);
225
EXPECT_NE(backend1,
nullptr
);
226
ASSERT_NO_FATAL_FAILURE(runRoundTrip(params, 16384));
227
228
// Test second constructor (with nudb::context)
229
// Note: This would require access to nudb::context, which might not be
230
// easily testable without more complex setup. For now, we test that
231
// the factory can create backends with the first constructor.
232
}
233
234
TEST
(
NuDBFactory
, configuration_parsing)
235
{
236
// basic valid format emits success log
237
{
238
TempDir
const
tempDir;
239
auto
const
params = makeSection(tempDir.
path
(),
"8192"
);
240
test::CaptureSink
sink(
beast::Severity::Info
);
241
beast::Journal
const
journal(sink);
242
DummyScheduler
scheduler;
243
[[maybe_unused]]
auto
backend =
244
Manager::instance
().
makeBackend
(params,
megabytes
(4), scheduler, journal);
245
EXPECT_TRUE(sink.
messages
().contains(
"Using custom NuDB block size"
));
246
}
247
248
// Test whitespace handling separately since lexical_cast behavior may vary
249
std::vector<std::string>
const
kWhitespaceFormats = {
" 8192"
,
"8192 "
};
250
for
(
auto
const
& format : kWhitespaceFormats)
251
{
252
SCOPED_TRACE(
"format='"
+ format +
"'"
);
253
TempDir
const
tempDir;
254
auto
const
params = makeSection(tempDir.
path
(), format);
255
test::CaptureSink
sink(
beast::Severity::Debug
);
256
beast::Journal
const
journal(sink);
257
DummyScheduler
scheduler;
258
EXPECT_ANY_THROW(
Manager::instance
().makeBackend(params,
megabytes
(4), scheduler, journal));
259
}
260
}
261
262
TEST
(
NuDBFactory
, data_persistence)
263
{
264
std::vector<std::string>
const
kBlockSizes = {
"4096"
,
"8192"
,
"16384"
,
"32768"
};
265
for
(
auto
const
& size : kBlockSizes)
266
{
267
SCOPED_TRACE(
"size="
+ size);
268
TempDir
const
tempDir;
269
auto
const
params = makeSection(tempDir.
path
(), size);
270
DummyScheduler
scheduler;
271
beast::Journal
const
journal(
TestSink::instance
());
272
273
// Create test data
274
auto
const
batch =
createPredictableBatch
(50, 54321);
275
276
// Store data
277
{
278
auto
backend =
279
Manager::instance
().
makeBackend
(params,
megabytes
(4), scheduler, journal);
280
backend->open();
281
storeBatch
(*backend, batch);
282
backend->close();
283
}
284
285
// Retrieve data in new backend instance
286
{
287
auto
backend =
288
Manager::instance
().
makeBackend
(params,
megabytes
(4), scheduler, journal);
289
backend->open();
290
auto
const
copy =
fetchCopyOfBatch
(*backend, batch);
291
EXPECT_EQ(batch, copy);
292
backend->close();
293
}
294
}
295
}
296
297
}
// namespace xrpl::node_store
array
std::string
beast::Journal
A generic endpoint for log messages.
Definition
Journal.h:44
xrpl::TempDir
RAII temporary directory.
Definition
FileUtilities.h:44
xrpl::TempDir::path
std::string path() const
Get the native path for the temporary directory.
Definition
libxrpl/basics/FileUtilities.cpp:130
xrpl::TestSink::instance
static TestSink & instance()
Definition
TestSink.h:12
xrpl::node_store::DummyScheduler
Simple NodeStore Scheduler that just performs the tasks synchronously.
Definition
DummyScheduler.h:12
xrpl::node_store::Manager::makeBackend
virtual std::unique_ptr< Backend > makeBackend(Section const ¶meters, std::size_t burstSize, Scheduler &scheduler, beast::Journal journal)=0
Create a backend.
xrpl::node_store::Manager::instance
static Manager & instance()
Returns the instance of the manager singleton.
Definition
ManagerImp.cpp:133
xrpl::node_store::NuDBFactory
Definition
libxrpl/nodestore/backend/NuDBFactory.cpp:398
xrpl::test::CaptureSink
Definition
CaptureSink.h:12
xrpl::test::CaptureSink::messages
std::string messages() const
Definition
CaptureSink.h:41
std::copy
T copy(T... args)
cstddef
exception
memory
beast::Severity::Warning
@ Warning
Definition
Journal.h:22
beast::Severity::Info
@ Info
Definition
Journal.h:21
beast::Severity::Debug
@ Debug
Definition
Journal.h:20
xrpl::node_store
Definition
ServiceRegistry.h:18
xrpl::node_store::TEST
TEST(NodeStoreBasics, predictable_batches)
Definition
Basics.cpp:14
xrpl::node_store::createPredictableBatch
Batch createPredictableBatch(std::size_t numObjects, std::uint64_t seed)
Definition
TestBase.h:48
xrpl::node_store::storeBatch
void storeBatch(Backend &backend, Batch const &batch)
Definition
TestBase.h:85
xrpl::node_store::fetchCopyOfBatch
Batch fetchCopyOfBatch(Backend &backend, Batch const &batch)
Definition
TestBase.h:92
xrpl::megabytes
constexpr auto megabytes(T value) noexcept
Definition
ByteUtilities.h:14
string
std::to_string
T to_string(T... args)
utility
vector
std::exception::what
T what(T... args)
Generated by
1.17.0