rippled
Loading...
Searching...
No Matches
multi_runner.h
1#ifndef TEST_UNIT_TEST_MULTI_RUNNER_H
2#define TEST_UNIT_TEST_MULTI_RUNNER_H
3
4#include <xrpl/beast/unit_test/global_suites.h>
5#include <xrpl/beast/unit_test/runner.h>
6
7#include <boost/beast/core/static_string.hpp>
8#include <boost/container/static_vector.hpp>
9#include <boost/interprocess/ipc/message_queue.hpp>
10#include <boost/interprocess/mapped_region.hpp>
11#include <boost/interprocess/shared_memory_object.hpp>
12#include <boost/interprocess/sync/interprocess_mutex.hpp>
13
14#include <atomic>
15#include <chrono>
16#include <numeric>
17#include <sstream>
18#include <string>
19#include <thread>
20#include <unordered_set>
21#include <utility>
22
23namespace ripple {
24
25namespace detail {
26
28
30{
34
35 explicit case_results(std::string name_ = "") : name(std::move(name_))
36 {
37 }
38};
39
41{
46 typename clock_type::time_point start = clock_type::now();
47
48 explicit suite_results(std::string name_ = "") : name(std::move(name_))
49 {
50 }
51
52 void
53 add(case_results const& r);
54};
55
56struct results
57{
58 using static_string = boost::beast::static_string<256>;
59 // results may be stored in shared memory. Use `static_string` to ensure
60 // pointers from different memory spaces do not co-mingle
62
63 enum { max_top = 10 };
64
69 boost::container::static_vector<run_time, max_top> top;
70 typename clock_type::time_point start = clock_type::now();
71
72 void
73 add(suite_results const& r);
74
75 void
76 merge(results const& r);
77
78 template <class S>
79 void
80 print(S& s);
81};
82
83template <bool IsParent>
85{
86 // `inner` will be created in shared memory. This is one way
87 // multi_runner_parent and multi_runner_child object communicate. The other
88 // way they communicate is through message queues.
89 struct inner
90 {
94 // A parent process will periodically increment `keep_alive_`. The child
95 // processes will check if `keep_alive_` is being incremented. If it is
96 // not incremented for a sufficiently long time, the child will assume
97 // the parent process has died.
99
100 mutable boost::interprocess::interprocess_mutex m_;
102
105
108
109 bool
110 any_failed() const;
111
112 void
113 any_failed(bool v);
114
116 tests() const;
117
119 suites() const;
120
121 void
123
126
127 void
128 add(results const& r);
129
130 template <class S>
131 void
132 print_results(S& s);
133 };
134
135 static constexpr char const* shared_mem_name_ = "RippledUnitTestSharedMem";
136 // name of the message queue a multi_runner_child will use to communicate
137 // with multi_runner_parent
138 static constexpr char const* message_queue_name_ =
139 "RippledUnitTestMessageQueue";
140
141 // `inner_` will be created in shared memory
143 // shared memory to use for the `inner` member
144 boost::interprocess::shared_memory_object shared_mem_;
145 boost::interprocess::mapped_region region_;
146
147protected:
149
151 void
153
154public:
157
160
163
164 void
165 any_failed(bool v);
166
167 void
168 add(results const& r);
169
170 void
172
175
176 template <class S>
177 void
178 print_results(S& s);
179
180 bool
181 any_failed() const;
182
184 tests() const;
185
187 suites() const;
188
189 void
190 add_failures(std::size_t failures);
191};
192
193} // namespace detail
194
195namespace test {
196
197//------------------------------------------------------------------------------
198
201class multi_runner_parent : private detail::multi_runner_base</*IsParent*/ true>
202{
203private:
204 // message_queue_ is used to collect log messages from the children
208 // track running suites so if a child crashes the culprit can be flagged
210
211public:
215
218
219 bool
220 any_failed() const;
221
223 tests() const;
224
226 suites() const;
227
228 void
229 add_failures(std::size_t failures);
230};
231
232//------------------------------------------------------------------------------
233
237 private detail::multi_runner_base</*IsParent*/ false>
238{
239private:
245 bool quiet_{false};
246 bool print_log_{true};
247
250
251public:
255
256 multi_runner_child(std::size_t num_jobs, bool quiet, bool print_log);
258
260 tests() const;
261
263 suites() const;
264
265 void
266 add_failures(std::size_t failures);
267
268 template <class Pred>
269 bool
270 run_multi(Pred pred);
271
272private:
273 virtual void
274 on_suite_begin(beast::unit_test::suite_info const& info) override;
275
276 virtual void
277 on_suite_end() override;
278
279 virtual void
280 on_case_begin(std::string const& name) override;
281
282 virtual void
283 on_case_end() override;
284
285 virtual void
286 on_pass() override;
287
288 virtual void
289 on_fail(std::string const& reason) override;
290
291 virtual void
292 on_log(std::string const& s) override;
293};
294
295//------------------------------------------------------------------------------
296
297template <class Pred>
298bool
300{
302 auto const num_tests = suite.size();
303 bool failed = false;
304
305 auto get_test = [&]() -> beast::unit_test::suite_info const* {
306 auto const cur_test_index = checkout_test_index();
307 if (cur_test_index >= num_tests)
308 return nullptr;
309 auto iter = suite.begin();
310 std::advance(iter, cur_test_index);
311 return &*iter;
312 };
313 while (auto t = get_test())
314 {
315 if (!pred(*t))
316 continue;
317 try
318 {
319 failed = run(*t) || failed;
320 }
321 catch (...)
322 {
323 if (num_jobs_ <= 1)
324 throw; // a single process can die
325
326 // inform the parent
328 s << job_index_ << "> failed Unhandled exception in test.\n";
329 message_queue_send(MessageType::log, s.str());
330 failed = true;
331 }
332 }
333 any_failed(failed);
334 return failed;
335}
336
337} // namespace test
338} // namespace ripple
339
340#endif
T advance(T... args)
Unit test runner interface.
Definition runner.h:24
friend class suite
Definition runner.h:148
bool run(suite_info const &s)
Run the specified suite.
Definition runner.h:172
Associates a unit test type with metadata.
Definition suite_info.h:20
std::unique_ptr< boost::interprocess::message_queue > message_queue_
static constexpr char const * message_queue_name_
void message_queue_send(MessageType mt, std::string const &s)
boost::interprocess::mapped_region region_
void add_failures(std::size_t failures)
static constexpr char const * shared_mem_name_
boost::interprocess::shared_memory_object shared_mem_
A class to run a subset of unit tests.
multi_runner_child(multi_runner_child const &)=delete
virtual void on_log(std::string const &s) override
Called when a test logs output.
detail::case_results case_results_
virtual void on_case_end() override
Called when a new case ends.
multi_runner_child & operator=(multi_runner_child const &)=delete
detail::suite_results suite_results_
virtual void on_case_begin(std::string const &name) override
Called when a new case starts.
void add_failures(std::size_t failures)
virtual void on_suite_end() override
Called when a suite ends.
std::atomic< bool > continue_keep_alive_
virtual void on_suite_begin(beast::unit_test::suite_info const &info) override
Called when a new suite starts.
virtual void on_pass() override
Called for each passing condition.
virtual void on_fail(std::string const &reason) override
Called for each failing condition.
Manager for children running unit tests.
std::atomic< bool > continue_message_queue_
void add_failures(std::size_t failures)
multi_runner_parent & operator=(multi_runner_parent const &)=delete
multi_runner_parent(multi_runner_parent const &)=delete
std::set< std::string > running_suites_
suite_list const & global_suites()
Holds test suites registered during static initialization.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
STL namespace.
T str(T... args)
case_results(std::string name_="")
boost::interprocess::interprocess_mutex m_
std::atomic< std::size_t > keep_alive_
std::atomic< std::size_t > test_index_
std::atomic< std::size_t > job_index_
void add(suite_results const &r)
boost::beast::static_string< 256 > static_string
boost::container::static_vector< run_time, max_top > top
clock_type::time_point start
void merge(results const &r)
void add(case_results const &r)
clock_type::time_point start
suite_results(std::string name_="")