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{
236private:
242 bool quiet_{false};
243 bool print_log_{true};
244
247
248public:
252
253 multi_runner_child(std::size_t num_jobs, bool quiet, bool print_log);
255
257 tests() const;
258
260 suites() const;
261
262 void
263 add_failures(std::size_t failures);
264
265 template <class Pred>
266 bool
267 run_multi(Pred pred);
268
269private:
270 virtual void
271 on_suite_begin(beast::unit_test::suite_info const& info) override;
272
273 virtual void
274 on_suite_end() override;
275
276 virtual void
277 on_case_begin(std::string const& name) override;
278
279 virtual void
280 on_case_end() override;
281
282 virtual void
283 on_pass() override;
284
285 virtual void
286 on_fail(std::string const& reason) override;
287
288 virtual void
289 on_log(std::string const& s) override;
290};
291
292//------------------------------------------------------------------------------
293
294template <class Pred>
295bool
297{
299 auto const num_tests = suite.size();
300 bool failed = false;
301
302 auto get_test = [&]() -> beast::unit_test::suite_info const* {
303 auto const cur_test_index = checkout_test_index();
304 if (cur_test_index >= num_tests)
305 return nullptr;
306 auto iter = suite.begin();
307 std::advance(iter, cur_test_index);
308 return &*iter;
309 };
310 while (auto t = get_test())
311 {
312 if (!pred(*t))
313 continue;
314 try
315 {
316 failed = run(*t) || failed;
317 }
318 catch (...)
319 {
320 if (num_jobs_ <= 1)
321 throw; // a single process can die
322
323 // inform the parent
325 s << job_index_ << "> failed Unhandled exception in test.\n";
326 message_queue_send(MessageType::log, s.str());
327 failed = true;
328 }
329 }
330 any_failed(failed);
331 return failed;
332}
333
334} // namespace test
335} // 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