xrpld
Toggle main menu visibility
Loading...
Searching...
No Matches
test
core
ClosureCounter_test.cpp
1
#include <test/jtx/Env.h>
2
#include <test/jtx/envconfig.h>
3
4
#include <xrpl/beast/unit_test/suite.h>
5
#include <xrpl/beast/utility/Journal.h>
6
#include <xrpl/core/ClosureCounter.h>
7
8
#include <
atomic
>
9
#include <
optional
>
10
#include <
thread
>
11
#include <
utility
>
12
13
namespace
xrpl::test
{
14
15
//------------------------------------------------------------------------------
16
17
class
ClosureCounter_test
:
public
beast::unit_test::Suite
18
{
19
// We're only using Env for its Journal. That Journal gives better
20
// coverage in unit tests.
21
test::jtx::Env
env_
{*
this
,
jtx::envconfig
(),
nullptr
,
beast::Severity::Disabled
};
22
beast::Journal
j_
{
env_
.app().getJournal(
"ClosureCounter_test"
)};
23
24
void
25
testConstruction
()
26
{
27
// Build different kinds of ClosureCounters.
28
{
29
// Count closures that return void and take no arguments.
30
ClosureCounter<void>
voidCounter;
31
BEAST_EXPECT(voidCounter.
count
() == 0);
32
33
int
evidence = 0;
34
// Make sure voidCounter.wrap works with an rvalue closure.
35
auto
wrapped = voidCounter.
wrap
([&evidence]() { ++evidence; });
36
BEAST_EXPECT(voidCounter.
count
() == 1);
37
BEAST_EXPECT(evidence == 0);
38
BEAST_EXPECT(wrapped);
39
40
// wrapped() should be callable with no arguments.
41
(*wrapped)();
// NOLINT(bugprone-unchecked-optional-access)
42
BEAST_EXPECT(evidence == 1);
43
(*wrapped)();
// NOLINT(bugprone-unchecked-optional-access)
44
BEAST_EXPECT(evidence == 2);
45
46
// Destroying the contents of wrapped should decrement voidCounter.
47
wrapped = std::nullopt;
48
BEAST_EXPECT(voidCounter.
count
() == 0);
49
}
50
{
51
// Count closures that return void and take one int argument.
52
ClosureCounter<void, int>
setCounter;
53
BEAST_EXPECT(setCounter.
count
() == 0);
54
55
int
evidence = 0;
56
// Make sure setCounter.wrap works with a non-const lvalue closure.
57
auto
setInt = [&evidence](
int
i) { evidence = i; };
58
auto
wrapped = setCounter.
wrap
(setInt);
59
60
BEAST_EXPECT(setCounter.
count
() == 1);
61
BEAST_EXPECT(evidence == 0);
62
BEAST_EXPECT(wrapped);
63
64
// wrapped() should be callable with one integer argument.
65
(*wrapped)(5);
// NOLINT(bugprone-unchecked-optional-access)
66
BEAST_EXPECT(evidence == 5);
67
(*wrapped)(11);
// NOLINT(bugprone-unchecked-optional-access)
68
BEAST_EXPECT(evidence == 11);
69
70
// Destroying the contents of wrapped should decrement setCounter.
71
wrapped = std::nullopt;
72
BEAST_EXPECT(setCounter.
count
() == 0);
73
}
74
{
75
// Count closures that return int and take two int arguments.
76
ClosureCounter<int, int, int>
sumCounter;
77
BEAST_EXPECT(sumCounter.
count
() == 0);
78
79
// Make sure sumCounter.wrap works with a const lvalue closure.
80
auto
const
sum
= [](
int
ii,
int
jj) {
return
ii + jj; };
81
auto
wrapped = sumCounter.
wrap
(
sum
);
82
83
BEAST_EXPECT(sumCounter.
count
() == 1);
84
BEAST_EXPECT(wrapped);
85
86
// wrapped() should be callable with two integers.
87
BEAST_EXPECT((*wrapped)(5, 2) == 7);
// NOLINT(bugprone-unchecked-optional-access)
88
BEAST_EXPECT((*wrapped)(2, -8) == -6);
// NOLINT(bugprone-unchecked-optional-access)
89
90
// Destroying the contents of wrapped should decrement sumCounter.
91
wrapped = std::nullopt;
92
BEAST_EXPECT(sumCounter.
count
() == 0);
93
}
94
}
95
96
// A class used to test argument passing.
97
class
TrackedString
98
{
99
public
:
100
int
copies
= {0};
101
int
moves
= {0};
102
std::string
str
;
103
104
TrackedString
() =
delete
;
105
106
explicit
TrackedString
(
char
const
* rhs) :
str
(rhs)
107
{
108
}
109
110
// Copy constructor
111
TrackedString
(
TrackedString
const
& rhs)
112
:
copies
(rhs.
copies
+ 1),
moves
(rhs.
moves
),
str
(rhs.
str
)
113
{
114
}
115
116
// Move constructor
117
TrackedString
(
TrackedString
&& rhs) noexcept
118
:
copies
(rhs.copies),
moves
(rhs.moves + 1),
str
(std::move(rhs.str))
119
{
120
}
121
122
// Delete copy and move assignment.
123
TrackedString
&
124
operator=
(
TrackedString
const
& rhs) =
delete
;
125
126
// String concatenation
127
TrackedString
&
128
operator+=
(
char
const
* rhs)
129
{
130
str
+= rhs;
131
return
*
this
;
132
}
133
134
friend
TrackedString
135
operator+
(
TrackedString
const
& s,
char
const
* rhs)
136
{
137
TrackedString
ret{s};
138
ret.
str
+= rhs;
139
return
ret;
140
}
141
};
142
143
void
144
testArgs
()
145
{
146
// Make sure a wrapped closure handles rvalue reference arguments
147
// correctly.
148
{
149
// Pass by value.
150
ClosureCounter<TrackedString, TrackedString>
strCounter;
151
BEAST_EXPECT(strCounter.
count
() == 0);
152
153
auto
wrapped = strCounter.
wrap
([](
TrackedString
in) {
return
in +=
"!"
; });
154
155
BEAST_EXPECT(strCounter.
count
() == 1);
156
BEAST_EXPECT(wrapped);
157
158
TrackedString
const
strValue(
"value"
);
159
TrackedString
const
result =
160
(*wrapped)(strValue);
// NOLINT(bugprone-unchecked-optional-access)
161
BEAST_EXPECT(result.
copies
== 2);
162
BEAST_EXPECT(result.
moves
== 1);
163
BEAST_EXPECT(result.
str
==
"value!"
);
164
BEAST_EXPECT(strValue.
str
.
size
() == 5);
165
}
166
{
167
// Use a const lvalue argument.
168
ClosureCounter<TrackedString, TrackedString const&>
strCounter;
169
BEAST_EXPECT(strCounter.
count
() == 0);
170
171
auto
wrapped = strCounter.
wrap
([](
TrackedString
const
& in) {
return
in +
"!"
; });
172
173
BEAST_EXPECT(strCounter.
count
() == 1);
174
BEAST_EXPECT(wrapped);
175
176
TrackedString
const
strConstLValue(
"const lvalue"
);
177
TrackedString
const
result =
178
(*wrapped)(strConstLValue);
// NOLINT(bugprone-unchecked-optional-access)
179
BEAST_EXPECT(result.
copies
== 1);
180
// BEAST_EXPECT (result.moves == ?); // moves VS == 1, gcc == 0
181
BEAST_EXPECT(result.
str
==
"const lvalue!"
);
182
BEAST_EXPECT(strConstLValue.
str
.
size
() == 12);
183
}
184
{
185
// Use a non-const lvalue argument.
186
ClosureCounter<TrackedString, TrackedString&>
strCounter;
187
BEAST_EXPECT(strCounter.
count
() == 0);
188
189
auto
wrapped = strCounter.
wrap
([](
TrackedString
& in) {
return
in +=
"!"
; });
190
191
BEAST_EXPECT(strCounter.
count
() == 1);
192
BEAST_EXPECT(wrapped);
193
194
TrackedString
strLValue(
"lvalue"
);
195
TrackedString
const
result =
196
(*wrapped)(strLValue);
// NOLINT(bugprone-unchecked-optional-access)
197
BEAST_EXPECT(result.
copies
== 1);
198
BEAST_EXPECT(result.
moves
== 0);
199
BEAST_EXPECT(result.
str
==
"lvalue!"
);
200
BEAST_EXPECT(strLValue.
str
== result.
str
);
201
}
202
{
203
// Use an rvalue argument.
204
ClosureCounter<TrackedString, TrackedString&&>
strCounter;
205
BEAST_EXPECT(strCounter.
count
() == 0);
206
207
auto
wrapped = strCounter.
wrap
([](
TrackedString
&& in) {
208
// Note that kNone of the compilers noticed that in was
209
// leaving scope. So, without intervention, they would
210
// do a copy for the return (June 2017). An explicit
211
// std::move() was required.
212
in +=
"!"
;
213
return
std::move(in);
214
});
215
216
BEAST_EXPECT(strCounter.
count
() == 1);
217
BEAST_EXPECT(wrapped);
218
219
// Make the string big enough to (probably) avoid the small string optimization.
220
TrackedString
strRValue(
"rvalue abcdefghijklmnopqrstuvwxyz"
);
221
TrackedString
const
result =
222
(*wrapped)(std::move(strRValue));
// NOLINT(bugprone-unchecked-optional-access)
223
BEAST_EXPECT(result.
copies
== 0);
224
BEAST_EXPECT(result.
moves
== 1);
225
BEAST_EXPECT(result.
str
==
"rvalue abcdefghijklmnopqrstuvwxyz!"
);
226
BEAST_EXPECT(strRValue.
str
.
empty
());
// NOLINT(bugprone-use-after-move)
227
}
228
}
229
230
void
231
testWrap
()
232
{
233
// Verify reference counting.
234
ClosureCounter<void>
voidCounter;
235
BEAST_EXPECT(voidCounter.
count
() == 0);
236
{
237
auto
wrapped1 = voidCounter.
wrap
([]() {});
238
BEAST_EXPECT(voidCounter.
count
() == 1);
239
{
240
// Copy should increase reference count.
241
auto
wrapped2(wrapped1);
242
BEAST_EXPECT(voidCounter.
count
() == 2);
243
{
244
// Move should increase reference count.
245
auto
wrapped3(std::move(wrapped2));
246
BEAST_EXPECT(voidCounter.
count
() == 3);
247
{
248
// An additional closure also increases count.
249
auto
wrapped4 = voidCounter.
wrap
([]() {});
250
BEAST_EXPECT(voidCounter.
count
() == 4);
251
}
252
BEAST_EXPECT(voidCounter.
count
() == 3);
253
}
254
BEAST_EXPECT(voidCounter.
count
() == 2);
255
}
256
BEAST_EXPECT(voidCounter.
count
() == 1);
257
}
258
BEAST_EXPECT(voidCounter.
count
() == 0);
259
260
// Join with 0 count should not stall.
261
using namespace
std::chrono_literals;
262
voidCounter.
join
(
"testWrap"
, 1ms,
j_
);
263
264
// Wrapping a closure after join() should return std::nullopt.
265
BEAST_EXPECT(voidCounter.
wrap
([]() {}) == std::nullopt);
266
}
267
268
void
269
testWaitOnJoin
()
270
{
271
// Verify reference counting.
272
ClosureCounter<void>
voidCounter;
273
BEAST_EXPECT(voidCounter.
count
() == 0);
274
275
auto
wrapped = (voidCounter.
wrap
([]() {}));
276
BEAST_EXPECT(voidCounter.
count
() == 1);
277
278
// Calling join() now should stall, so do it on a different thread.
279
std::atomic<bool>
threadExited{
false
};
280
std::thread
localThread([&voidCounter, &threadExited,
this
]() {
281
// Should stall after calling join.
282
using namespace
std::chrono_literals;
283
voidCounter.
join
(
"testWaitOnJoin"
, 1ms,
j_
);
284
threadExited.
store
(
true
);
285
});
286
287
// Wait for the thread to call voidCounter.join().
288
while
(!voidCounter.
joined
())
289
;
290
291
// The thread should still be active after waiting 5 milliseconds.
292
// This is not a guarantee that join() stalled the thread, but it
293
// improves confidence.
294
using namespace
std::chrono_literals;
295
std::this_thread::sleep_for
(5ms);
296
BEAST_EXPECT(threadExited ==
false
);
297
298
// Destroy the contents of wrapped and expect the thread to exit
299
// (asynchronously).
300
wrapped = std::nullopt;
301
BEAST_EXPECT(voidCounter.
count
() == 0);
302
303
// Wait for the thread to exit.
304
while
(!threadExited)
305
;
306
localThread.join();
307
}
308
309
public
:
310
void
311
run
()
override
312
{
313
testConstruction
();
314
testArgs
();
315
testWrap
();
316
testWaitOnJoin
();
317
}
318
};
319
320
BEAST_DEFINE_TESTSUITE
(
ClosureCounter
, core,
xrpl
);
321
322
}
// namespace xrpl::test
atomic
std::string
beast::Journal
A generic endpoint for log messages.
Definition
Journal.h:44
beast::unit_test::Suite
A testsuite class.
Definition
suite.h:52
xrpl::ClosureCounter
The role of a ClosureCounter is to assist in shutdown by letting callers wait for the completion of c...
Definition
ClosureCounter.h:38
xrpl::ClosureCounter::join
void join(char const *name, std::chrono::milliseconds wait, beast::Journal j)
Returns once all counted in-flight closures are destroyed.
Definition
ClosureCounter.h:151
xrpl::ClosureCounter::wrap
std::optional< Substitute< Closure > > wrap(Closure &&closure)
Wrap the passed closure with a reference counter.
Definition
ClosureCounter.h:176
xrpl::ClosureCounter::joined
bool joined() const
Returns true if this has been joined.
Definition
ClosureCounter.h:204
xrpl::ClosureCounter::count
int count() const
Current number of Closures outstanding.
Definition
ClosureCounter.h:191
xrpl::test::ClosureCounter_test::TrackedString
Definition
ClosureCounter_test.cpp:98
xrpl::test::ClosureCounter_test::TrackedString::operator+=
TrackedString & operator+=(char const *rhs)
Definition
ClosureCounter_test.cpp:128
xrpl::test::ClosureCounter_test::TrackedString::operator+
friend TrackedString operator+(TrackedString const &s, char const *rhs)
Definition
ClosureCounter_test.cpp:135
xrpl::test::ClosureCounter_test::TrackedString::TrackedString
TrackedString()=delete
xrpl::test::ClosureCounter_test::TrackedString::copies
int copies
Definition
ClosureCounter_test.cpp:100
xrpl::test::ClosureCounter_test::TrackedString::moves
int moves
Definition
ClosureCounter_test.cpp:101
xrpl::test::ClosureCounter_test::TrackedString::TrackedString
TrackedString(TrackedString const &rhs)
Definition
ClosureCounter_test.cpp:111
xrpl::test::ClosureCounter_test::TrackedString::TrackedString
TrackedString(char const *rhs)
Definition
ClosureCounter_test.cpp:106
xrpl::test::ClosureCounter_test::TrackedString::TrackedString
TrackedString(TrackedString &&rhs) noexcept
Definition
ClosureCounter_test.cpp:117
xrpl::test::ClosureCounter_test::TrackedString::operator=
TrackedString & operator=(TrackedString const &rhs)=delete
xrpl::test::ClosureCounter_test::TrackedString::str
std::string str
Definition
ClosureCounter_test.cpp:102
xrpl::test::ClosureCounter_test
Definition
ClosureCounter_test.cpp:18
xrpl::test::ClosureCounter_test::testWrap
void testWrap()
Definition
ClosureCounter_test.cpp:231
xrpl::test::ClosureCounter_test::j_
beast::Journal j_
Definition
ClosureCounter_test.cpp:22
xrpl::test::ClosureCounter_test::run
void run() override
Runs the suite.
Definition
ClosureCounter_test.cpp:311
xrpl::test::ClosureCounter_test::testConstruction
void testConstruction()
Definition
ClosureCounter_test.cpp:25
xrpl::test::ClosureCounter_test::testWaitOnJoin
void testWaitOnJoin()
Definition
ClosureCounter_test.cpp:269
xrpl::test::ClosureCounter_test::testArgs
void testArgs()
Definition
ClosureCounter_test.cpp:144
xrpl::test::ClosureCounter_test::env_
test::jtx::Env env_
Definition
ClosureCounter_test.cpp:21
xrpl::test::jtx::Env
A transaction testing environment.
Definition
Env.h:161
std::string::empty
T empty(T... args)
beast::Severity::Disabled
@ Disabled
Definition
Journal.h:26
xrpl::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition
envconfig.h:38
xrpl::test
Definition
STLedgerEntry.h:21
xrpl::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
xrpl
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition
algorithm.h:5
xrpl::sum
static auto sum(TCollection const &col)
Definition
BookStep.cpp:1058
optional
std::string::size
T size(T... args)
std::this_thread::sleep_for
T sleep_for(T... args)
std::atomic::store
T store(T... args)
thread
utility
Generated by
1.17.0