rippled
Loading...
Searching...
No Matches
multi_runner.h
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#ifndef TEST_UNIT_TEST_MULTI_RUNNER_H
21#define TEST_UNIT_TEST_MULTI_RUNNER_H
22
23#include <xrpl/beast/unit_test/global_suites.h>
24#include <xrpl/beast/unit_test/runner.h>
25
26#include <boost/beast/core/static_string.hpp>
27#include <boost/container/static_vector.hpp>
28#include <boost/interprocess/ipc/message_queue.hpp>
29#include <boost/interprocess/mapped_region.hpp>
30#include <boost/interprocess/shared_memory_object.hpp>
31#include <boost/interprocess/sync/interprocess_mutex.hpp>
32
33#include <atomic>
34#include <chrono>
35#include <numeric>
36#include <sstream>
37#include <string>
38#include <thread>
39#include <unordered_set>
40#include <utility>
41
42namespace ripple {
43namespace test {
44
45namespace detail {
46
48
50{
54
55 explicit case_results(std::string name_ = "") : name(std::move(name_))
56 {
57 }
58};
59
61{
66 typename clock_type::time_point start = clock_type::now();
67
68 explicit suite_results(std::string name_ = "") : name(std::move(name_))
69 {
70 }
71
72 void
73 add(case_results const& r);
74};
75
76struct results
77{
78 using static_string = boost::beast::static_string<256>;
79 // results may be stored in shared memory. Use `static_string` to ensure
80 // pointers from different memory spaces do not co-mingle
82
83 enum { max_top = 10 };
84
89 boost::container::static_vector<run_time, max_top> top;
90 typename clock_type::time_point start = clock_type::now();
91
92 void
93 add(suite_results const& r);
94
95 void
96 merge(results const& r);
97
98 template <class S>
99 void
100 print(S& s);
101};
102
103template <bool IsParent>
105{
106 // `inner` will be created in shared memory. This is one way
107 // multi_runner_parent and multi_runner_child object communicate. The other
108 // way they communicate is through message queues.
109 struct inner
110 {
114 // A parent process will periodically increment `keep_alive_`. The child
115 // processes will check if `keep_alive_` is being incremented. If it is
116 // not incremented for a sufficiently long time, the child will assume
117 // the parent process has died.
119
120 mutable boost::interprocess::interprocess_mutex m_;
122
125
128
129 bool
130 any_failed() const;
131
132 void
133 any_failed(bool v);
134
136 tests() const;
137
139 suites() const;
140
141 void
143
146
147 void
148 add(results const& r);
149
150 template <class S>
151 void
152 print_results(S& s);
153 };
154
155 static constexpr char const* shared_mem_name_ = "RippledUnitTestSharedMem";
156 // name of the message queue a multi_runner_child will use to communicate
157 // with multi_runner_parent
158 static constexpr char const* message_queue_name_ =
159 "RippledUnitTestMessageQueue";
160
161 // `inner_` will be created in shared memory
163 // shared memory to use for the `inner` member
164 boost::interprocess::shared_memory_object shared_mem_;
165 boost::interprocess::mapped_region region_;
166
167protected:
169
171 void
173
174public:
177
180
183
184 void
185 any_failed(bool v);
186
187 void
188 add(results const& r);
189
190 void
192
195
196 template <class S>
197 void
198 print_results(S& s);
199
200 bool
201 any_failed() const;
202
204 tests() const;
205
207 suites() const;
208
209 void
210 add_failures(std::size_t failures);
211};
212
213} // namespace detail
214
215//------------------------------------------------------------------------------
216
219class multi_runner_parent : private detail::multi_runner_base</*IsParent*/ true>
220{
221private:
222 // message_queue_ is used to collect log messages from the children
226 // track running suites so if a child crashes the culprit can be flagged
228
229public:
233
236
237 bool
238 any_failed() const;
239
241 tests() const;
242
244 suites() const;
245
246 void
247 add_failures(std::size_t failures);
248};
249
250//------------------------------------------------------------------------------
251
255 private detail::multi_runner_base</*IsParent*/ false>
256{
257private:
263 bool quiet_{false};
264 bool print_log_{true};
265
268
269public:
273
274 multi_runner_child(std::size_t num_jobs, bool quiet, bool print_log);
276
278 tests() const;
279
281 suites() const;
282
283 void
284 add_failures(std::size_t failures);
285
286 template <class Pred>
287 bool
288 run_multi(Pred pred);
289
290private:
291 virtual void
292 on_suite_begin(beast::unit_test::suite_info const& info) override;
293
294 virtual void
295 on_suite_end() override;
296
297 virtual void
298 on_case_begin(std::string const& name) override;
299
300 virtual void
301 on_case_end() override;
302
303 virtual void
304 on_pass() override;
305
306 virtual void
307 on_fail(std::string const& reason) override;
308
309 virtual void
310 on_log(std::string const& s) override;
311};
312
313//------------------------------------------------------------------------------
314
315template <class Pred>
316bool
318{
320 auto const num_tests = suite.size();
321 bool failed = false;
322
323 auto get_test = [&]() -> beast::unit_test::suite_info const* {
324 auto const cur_test_index = checkout_test_index();
325 if (cur_test_index >= num_tests)
326 return nullptr;
327 auto iter = suite.begin();
328 std::advance(iter, cur_test_index);
329 return &*iter;
330 };
331 while (auto t = get_test())
332 {
333 if (!pred(*t))
334 continue;
335 try
336 {
337 failed = run(*t) || failed;
338 }
339 catch (...)
340 {
341 if (num_jobs_ <= 1)
342 throw; // a single process can die
343
344 // inform the parent
346 s << job_index_ << "> failed Unhandled exception in test.\n";
347 message_queue_send(MessageType::log, s.str());
348 failed = true;
349 }
350 }
351 any_failed(failed);
352 return failed;
353}
354
355} // namespace test
356} // namespace ripple
357
358#endif
T advance(T... args)
Unit test runner interface.
Definition runner.h:27
friend class suite
Definition runner.h:151
bool run(suite_info const &s)
Run the specified suite.
Definition runner.h:175
Associates a unit test type with metadata.
Definition suite_info.h:23
static constexpr char const * shared_mem_name_
boost::interprocess::shared_memory_object shared_mem_
static constexpr char const * message_queue_name_
boost::interprocess::mapped_region region_
void add_failures(std::size_t failures)
void message_queue_send(MessageType mt, std::string const &s)
std::unique_ptr< boost::interprocess::message_queue > message_queue_
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:25
STL namespace.
T str(T... args)
case_results(std::string name_="")
boost::interprocess::interprocess_mutex m_
void merge(results const &r)
boost::beast::static_string< 256 > static_string
void add(suite_results const &r)
boost::container::static_vector< run_time, max_top > top
clock_type::time_point start
void add(case_results const &r)
suite_results(std::string name_="")