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