rippled
ClosureCounter_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2017 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/beast/unit_test.h>
21 #include <ripple/core/ClosureCounter.h>
22 #include <test/jtx/Env.h>
23 
24 #include <atomic>
25 #include <chrono>
26 #include <thread>
27 
28 namespace ripple {
29 namespace test {
30 
31 //------------------------------------------------------------------------------
32 
33 class ClosureCounter_test : public beast::unit_test::suite
34 {
35  // We're only using Env for its Journal. That Journal gives better
36  // coverage in unit tests.
38  *this,
40  nullptr,
42  beast::Journal j{env_.app().journal("ClosureCounter_test")};
43 
44  void
46  {
47  // Build different kinds of ClosureCounters.
48  {
49  // Count closures that return void and take no arguments.
50  ClosureCounter<void> voidCounter;
51  BEAST_EXPECT(voidCounter.count() == 0);
52 
53  int evidence = 0;
54  // Make sure voidCounter.wrap works with an rvalue closure.
55  auto wrapped = voidCounter.wrap([&evidence]() { ++evidence; });
56  BEAST_EXPECT(voidCounter.count() == 1);
57  BEAST_EXPECT(evidence == 0);
58  BEAST_EXPECT(wrapped);
59 
60  // wrapped() should be callable with no arguments.
61  (*wrapped)();
62  BEAST_EXPECT(evidence == 1);
63  (*wrapped)();
64  BEAST_EXPECT(evidence == 2);
65 
66  // Destroying the contents of wrapped should decrement voidCounter.
67  wrapped = std::nullopt;
68  BEAST_EXPECT(voidCounter.count() == 0);
69  }
70  {
71  // Count closures that return void and take one int argument.
72  ClosureCounter<void, int> setCounter;
73  BEAST_EXPECT(setCounter.count() == 0);
74 
75  int evidence = 0;
76  // Make sure setCounter.wrap works with a non-const lvalue closure.
77  auto setInt = [&evidence](int i) { evidence = i; };
78  auto wrapped = setCounter.wrap(setInt);
79 
80  BEAST_EXPECT(setCounter.count() == 1);
81  BEAST_EXPECT(evidence == 0);
82  BEAST_EXPECT(wrapped);
83 
84  // wrapped() should be callable with one integer argument.
85  (*wrapped)(5);
86  BEAST_EXPECT(evidence == 5);
87  (*wrapped)(11);
88  BEAST_EXPECT(evidence == 11);
89 
90  // Destroying the contents of wrapped should decrement setCounter.
91  wrapped = std::nullopt;
92  BEAST_EXPECT(setCounter.count() == 0);
93  }
94  {
95  // Count closures that return int and take two int arguments.
97  BEAST_EXPECT(sumCounter.count() == 0);
98 
99  // Make sure sumCounter.wrap works with a const lvalue closure.
100  auto const sum = [](int ii, int jj) { return ii + jj; };
101  auto wrapped = sumCounter.wrap(sum);
102 
103  BEAST_EXPECT(sumCounter.count() == 1);
104  BEAST_EXPECT(wrapped);
105 
106  // wrapped() should be callable with two integers.
107  BEAST_EXPECT((*wrapped)(5, 2) == 7);
108  BEAST_EXPECT((*wrapped)(2, -8) == -6);
109 
110  // Destroying the contents of wrapped should decrement sumCounter.
111  wrapped = std::nullopt;
112  BEAST_EXPECT(sumCounter.count() == 0);
113  }
114  }
115 
116  // A class used to test argument passing.
118  {
119  public:
120  int copies = {0};
121  int moves = {0};
123 
124  TrackedString() = delete;
125 
126  explicit TrackedString(char const* rhs) : str(rhs)
127  {
128  }
129 
130  // Copy constructor
132  : copies(rhs.copies + 1), moves(rhs.moves), str(rhs.str)
133  {
134  }
135 
136  // Move constructor
137  TrackedString(TrackedString&& rhs) noexcept
138  : copies(rhs.copies), moves(rhs.moves + 1), str(std::move(rhs.str))
139  {
140  }
141 
142  // Delete copy and move assignment.
144  operator=(TrackedString const& rhs) = delete;
145 
146  // String concatenation
148  operator+=(char const* rhs)
149  {
150  str += rhs;
151  return *this;
152  }
153 
154  friend TrackedString
155  operator+(TrackedString const& s, char const* rhs)
156  {
157  TrackedString ret{s};
158  ret.str += rhs;
159  return ret;
160  }
161  };
162 
163  void
165  {
166  // Make sure a wrapped closure handles rvalue reference arguments
167  // correctly.
168  {
169  // Pass by value.
171  BEAST_EXPECT(strCounter.count() == 0);
172 
173  auto wrapped =
174  strCounter.wrap([](TrackedString in) { return in += "!"; });
175 
176  BEAST_EXPECT(strCounter.count() == 1);
177  BEAST_EXPECT(wrapped);
178 
179  TrackedString const strValue("value");
180  TrackedString const result = (*wrapped)(strValue);
181  BEAST_EXPECT(result.copies == 2);
182  BEAST_EXPECT(result.moves == 1);
183  BEAST_EXPECT(result.str == "value!");
184  BEAST_EXPECT(strValue.str.size() == 5);
185  }
186  {
187  // Use a const lvalue argument.
189  BEAST_EXPECT(strCounter.count() == 0);
190 
191  auto wrapped = strCounter.wrap(
192  [](TrackedString const& in) { return in + "!"; });
193 
194  BEAST_EXPECT(strCounter.count() == 1);
195  BEAST_EXPECT(wrapped);
196 
197  TrackedString const strConstLValue("const lvalue");
198  TrackedString const result = (*wrapped)(strConstLValue);
199  BEAST_EXPECT(result.copies == 1);
200  // BEAST_EXPECT (result.moves == ?); // moves VS == 1, gcc == 0
201  BEAST_EXPECT(result.str == "const lvalue!");
202  BEAST_EXPECT(strConstLValue.str.size() == 12);
203  }
204  {
205  // Use a non-const lvalue argument.
207  BEAST_EXPECT(strCounter.count() == 0);
208 
209  auto wrapped =
210  strCounter.wrap([](TrackedString& in) { return in += "!"; });
211 
212  BEAST_EXPECT(strCounter.count() == 1);
213  BEAST_EXPECT(wrapped);
214 
215  TrackedString strLValue("lvalue");
216  TrackedString const result = (*wrapped)(strLValue);
217  BEAST_EXPECT(result.copies == 1);
218  BEAST_EXPECT(result.moves == 0);
219  BEAST_EXPECT(result.str == "lvalue!");
220  BEAST_EXPECT(strLValue.str == result.str);
221  }
222  {
223  // Use an rvalue argument.
225  BEAST_EXPECT(strCounter.count() == 0);
226 
227  auto wrapped = strCounter.wrap([](TrackedString&& in) {
228  // Note that none of the compilers noticed that in was
229  // leaving scope. So, without intervention, they would
230  // do a copy for the return (June 2017). An explicit
231  // std::move() was required.
232  return std::move(in += "!");
233  });
234 
235  BEAST_EXPECT(strCounter.count() == 1);
236  BEAST_EXPECT(wrapped);
237 
238  // Make the string big enough to (probably) avoid the small string
239  // optimization.
240  TrackedString strRValue("rvalue abcdefghijklmnopqrstuvwxyz");
241  TrackedString const result = (*wrapped)(std::move(strRValue));
242  BEAST_EXPECT(result.copies == 0);
243  BEAST_EXPECT(result.moves == 1);
244  BEAST_EXPECT(result.str == "rvalue abcdefghijklmnopqrstuvwxyz!");
245  BEAST_EXPECT(strRValue.str.size() == 0);
246  }
247  }
248 
249  void
251  {
252  // Verify reference counting.
253  ClosureCounter<void> voidCounter;
254  BEAST_EXPECT(voidCounter.count() == 0);
255  {
256  auto wrapped1 = voidCounter.wrap([]() {});
257  BEAST_EXPECT(voidCounter.count() == 1);
258  {
259  // Copy should increase reference count.
260  auto wrapped2(wrapped1);
261  BEAST_EXPECT(voidCounter.count() == 2);
262  {
263  // Move should increase reference count.
264  auto wrapped3(std::move(wrapped2));
265  BEAST_EXPECT(voidCounter.count() == 3);
266  {
267  // An additional closure also increases count.
268  auto wrapped4 = voidCounter.wrap([]() {});
269  BEAST_EXPECT(voidCounter.count() == 4);
270  }
271  BEAST_EXPECT(voidCounter.count() == 3);
272  }
273  BEAST_EXPECT(voidCounter.count() == 2);
274  }
275  BEAST_EXPECT(voidCounter.count() == 1);
276  }
277  BEAST_EXPECT(voidCounter.count() == 0);
278 
279  // Join with 0 count should not stall.
280  using namespace std::chrono_literals;
281  voidCounter.join("testWrap", 1ms, j);
282 
283  // Wrapping a closure after join() should return std::nullopt.
284  BEAST_EXPECT(voidCounter.wrap([]() {}) == std::nullopt);
285  }
286 
287  void
289  {
290  // Verify reference counting.
291  ClosureCounter<void> voidCounter;
292  BEAST_EXPECT(voidCounter.count() == 0);
293 
294  auto wrapped = (voidCounter.wrap([]() {}));
295  BEAST_EXPECT(voidCounter.count() == 1);
296 
297  // Calling join() now should stall, so do it on a different thread.
298  std::atomic<bool> threadExited{false};
299  std::thread localThread([&voidCounter, &threadExited, this]() {
300  // Should stall after calling join.
301  using namespace std::chrono_literals;
302  voidCounter.join("testWaitOnJoin", 1ms, j);
303  threadExited.store(true);
304  });
305 
306  // Wait for the thread to call voidCounter.join().
307  while (!voidCounter.joined())
308  ;
309 
310  // The thread should still be active after waiting 5 milliseconds.
311  // This is not a guarantee that join() stalled the thread, but it
312  // improves confidence.
313  using namespace std::chrono_literals;
315  BEAST_EXPECT(threadExited == false);
316 
317  // Destroy the contents of wrapped and expect the thread to exit
318  // (asynchronously).
319  wrapped = std::nullopt;
320  BEAST_EXPECT(voidCounter.count() == 0);
321 
322  // Wait for the thread to exit.
323  while (threadExited == false)
324  ;
325  localThread.join();
326  }
327 
328 public:
329  void
330  run() override
331  {
333  testArgs();
334  testWrap();
335  testWaitOnJoin();
336  }
337 };
338 
340 
341 } // namespace test
342 } // namespace ripple
std::this_thread::sleep_for
T sleep_for(T... args)
ripple::test::ClosureCounter_test::TrackedString::copies
int copies
Definition: ClosureCounter_test.cpp:120
ripple::test::ClosureCounter_test::testWrap
void testWrap()
Definition: ClosureCounter_test.cpp:250
std::string
STL class.
beast::severities::kDisabled
@ kDisabled
Definition: Journal.h:41
ripple::test::ClosureCounter_test
Definition: ClosureCounter_test.cpp:33
ripple::test::ClosureCounter_test::TrackedString::TrackedString
TrackedString(char const *rhs)
Definition: ClosureCounter_test.cpp:126
std::string::size
T size(T... args)
ripple::test::ClosureCounter_test::TrackedString::TrackedString
TrackedString(TrackedString &&rhs) noexcept
Definition: ClosureCounter_test.cpp:137
ripple::QualityDirection::in
@ in
ripple::test::ClosureCounter_test::TrackedString::TrackedString
TrackedString()=delete
ripple::test::ClosureCounter_test::testArgs
void testArgs()
Definition: ClosureCounter_test.cpp:164
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:241
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::test::ClosureCounter_test::TrackedString::TrackedString
TrackedString(TrackedString const &rhs)
Definition: ClosureCounter_test.cpp:131
ripple::test::ClosureCounter_test::TrackedString::operator+
friend TrackedString operator+(TrackedString const &s, char const *rhs)
Definition: ClosureCounter_test.cpp:155
thread
chrono
ripple::test::ClosureCounter_test::TrackedString
Definition: ClosureCounter_test.cpp:117
ripple::test::ClosureCounter_test::testConstruction
void testConstruction()
Definition: ClosureCounter_test.cpp:45
ripple::test::ClosureCounter_test::TrackedString::moves
int moves
Definition: ClosureCounter_test.cpp:121
ripple::test::ClosureCounter_test::testWaitOnJoin
void testWaitOnJoin()
Definition: ClosureCounter_test.cpp:288
ripple::ClosureCounter< void >
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
atomic
ripple::test::ClosureCounter_test::TrackedString::operator+=
TrackedString & operator+=(char const *rhs)
Definition: ClosureCounter_test.cpp:148
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Application::journal
virtual beast::Journal journal(std::string const &name)=0
ripple::test::ClosureCounter_test::env_
test::jtx::Env env_
Definition: ClosureCounter_test.cpp:37
ripple::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:166
ripple::ClosureCounter::count
int count() const
Current number of Closures outstanding.
Definition: ClosureCounter.h:205
ripple::test::ClosureCounter_test::TrackedString::str
std::string str
Definition: ClosureCounter_test.cpp:122
ripple::sum
static auto sum(TCollection const &col)
Definition: BookStep.cpp:710
ripple::test::ClosureCounter_test::j
beast::Journal j
Definition: ClosureCounter_test.cpp:42
ripple::ClosureCounter::joined
bool joined() const
Returns true if this has been joined.
Definition: ClosureCounter.h:217
ripple::test::ClosureCounter_test::TrackedString::operator=
TrackedString & operator=(TrackedString const &rhs)=delete
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
ripple::test::ClosureCounter_test::run
void run() override
Definition: ClosureCounter_test.cpp:330
ripple::ClosureCounter::wrap
std::optional< Substitute< Closure > > wrap(Closure &&closure)
Wrap the passed closure with a reference counter.
Definition: ClosureCounter.h:192
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)