rippled
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 <boost/beast/core/static_string.hpp>
24 #include <beast/unit_test/global_suites.hpp>
25 #include <beast/unit_test/runner.hpp>
26 
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 <string>
37 #include <thread>
38 #include <unordered_set>
39 #include <utility>
40 
41 namespace ripple {
42 namespace test {
43 
44 namespace detail {
45 
47 
49 {
53 
54  explicit case_results(std::string name_ = "") : name(std::move(name_))
55  {
56  }
57 };
58 
60 {
65  typename clock_type::time_point start = clock_type::now();
66 
67  explicit suite_results(std::string name_ = "") : name(std::move(name_))
68  {
69  }
70 
71  void
72  add(case_results const& r);
73 };
74 
75 struct results
76 {
77  using static_string = boost::beast::static_string<256>;
78  // results may be stored in shared memory. Use `static_string` to ensure
79  // pointers from different memory spaces do not co-mingle
81 
82  enum { max_top = 10 };
83 
88  boost::container::static_vector<run_time, max_top> top;
89  typename clock_type::time_point start = clock_type::now();
90 
91  void
92  add(suite_results const& r);
93 
94  void
95  merge(results const& r);
96 
97  template <class S>
98  void
99  print(S& s);
100 };
101 
102 template <bool IsParent>
104 {
105  // `inner` will be created in shared memory. This is one way
106  // multi_runner_parent and multi_runner_child object communicate. The other
107  // way they communicate is through message queues.
108  struct inner
109  {
113  // A parent process will periodically increment `keep_alive_`. The child
114  // processes will check if `keep_alive_` is being incremented. If it is
115  // not incremented for a sufficiently long time, the child will assume
116  // the parent process has died.
118 
119  mutable boost::interprocess::interprocess_mutex m_;
121 
124 
127 
128  bool
129  any_failed() const;
130 
131  void
132  any_failed(bool v);
133 
134  void
136 
139 
140  void
141  add(results const& r);
142 
143  template <class S>
144  void
145  print_results(S& s);
146  };
147 
148  static constexpr const char* shared_mem_name_ = "RippledUnitTestSharedMem";
149  // name of the message queue a multi_runner_child will use to communicate
150  // with multi_runner_parent
151  static constexpr const char* message_queue_name_ =
152  "RippledUnitTestMessageQueue";
153 
154  // `inner_` will be created in shared memory
155  inner* inner_;
156  // shared memory to use for the `inner` member
157  boost::interprocess::shared_memory_object shared_mem_;
158  boost::interprocess::mapped_region region_;
159 
160 protected:
162 
164  void
166 
167 public:
170 
173 
176 
177  void
178  any_failed(bool v);
179 
180  void
181  add(results const& r);
182 
183  void
185 
188 
189  template <class S>
190  void
191  print_results(S& s);
192 
193  bool
194  any_failed() const;
195 };
196 
197 } // namespace detail
198 
199 //------------------------------------------------------------------------------
200 
203 class multi_runner_parent : private detail::multi_runner_base</*IsParent*/ true>
204 {
205 private:
206  // message_queue_ is used to collect log messages from the children
210  // track running suites so if a child crashes the culprit can be flagged
212 
213 public:
214  multi_runner_parent(multi_runner_parent const&) = delete;
216  operator=(multi_runner_parent const&) = delete;
217 
220 
221  bool
222  any_failed() const;
223 };
224 
225 //------------------------------------------------------------------------------
226 
229 class multi_runner_child : public beast::unit_test::runner,
230  private detail::multi_runner_base</*IsParent*/ false>
231 {
232 private:
238  bool quiet_{false};
239  bool print_log_{true};
240 
243 
244 public:
245  multi_runner_child(multi_runner_child const&) = delete;
247  operator=(multi_runner_child const&) = delete;
248 
249  multi_runner_child(std::size_t num_jobs, bool quiet, bool print_log);
251 
252  template <class Pred>
253  bool
254  run_multi(Pred pred);
255 
256 private:
257  virtual void
258  on_suite_begin(beast::unit_test::suite_info const& info) override;
259 
260  virtual void
261  on_suite_end() override;
262 
263  virtual void
264  on_case_begin(std::string const& name) override;
265 
266  virtual void
267  on_case_end() override;
268 
269  virtual void
270  on_pass() override;
271 
272  virtual void
273  on_fail(std::string const& reason) override;
274 
275  virtual void
276  on_log(std::string const& s) override;
277 };
278 
279 //------------------------------------------------------------------------------
280 
281 template <class Pred>
282 bool
284 {
285  auto const& suite = beast::unit_test::global_suites();
286  auto const num_tests = suite.size();
287  bool failed = false;
288 
289  auto get_test = [&]() -> beast::unit_test::suite_info const* {
290  auto const cur_test_index = checkout_test_index();
291  if (cur_test_index >= num_tests)
292  return nullptr;
293  auto iter = suite.begin();
294  std::advance(iter, cur_test_index);
295  return &*iter;
296  };
297  while (auto t = get_test())
298  {
299  if (!pred(*t))
300  continue;
301  try
302  {
303  failed = run(*t) || failed;
304  }
305  catch (...)
306  {
307  if (num_jobs_ <= 1)
308  throw; // a single process can die
309 
310  // inform the parent
312  s << job_index_ << "> failed Unhandled exception in test.\n";
313  message_queue_send(MessageType::log, s.str());
314  failed = true;
315  }
316  }
317  any_failed(failed);
318  return failed;
319 }
320 
321 } // namespace test
322 } // namespace ripple
323 
324 #endif
ripple::test::detail::results::suites
std::size_t suites
Definition: multi_runner.h:84
ripple::test::detail::multi_runner_base::checkout_test_index
std::size_t checkout_test_index()
Definition: multi_runner.cpp:284
ripple::test::detail::multi_runner_base::inner::print_results
void print_results(S &s)
Definition: multi_runner.cpp:210
std::chrono::steady_clock
ripple::test::multi_runner_child::run_multi
bool run_multi(Pred pred)
Definition: multi_runner.h:283
ripple::test::detail::suite_results::failed
std::size_t failed
Definition: multi_runner.h:64
ripple::test::detail::multi_runner_base::inner
Definition: multi_runner.h:108
std::string
STL class.
ripple::test::detail::suite_results::total
std::size_t total
Definition: multi_runner.h:63
ripple::test::detail::multi_runner_base::MessageType::test_start
@ test_start
ripple::test::multi_runner_parent::operator=
multi_runner_parent & operator=(multi_runner_parent const &)=delete
utility
ripple::test::detail::case_results
Definition: multi_runner.h:48
ripple::test::multi_runner_child::quiet_
bool quiet_
Definition: multi_runner.h:238
ripple::test::detail::multi_runner_base::inner::inc_keep_alive_count
void inc_keep_alive_count()
Definition: multi_runner.cpp:187
ripple::test::detail::suite_results::name
std::string name
Definition: multi_runner.h:61
ripple::test::detail::suite_results::suite_results
suite_results(std::string name_="")
Definition: multi_runner.h:67
ripple::test::detail::multi_runner_base::print_results
void print_results(S &s)
Definition: multi_runner.cpp:334
ripple::test::detail::multi_runner_base::get_keep_alive_count
std::size_t get_keep_alive_count()
Definition: multi_runner.cpp:326
ripple::test::detail::multi_runner_base::shared_mem_
boost::interprocess::shared_memory_object shared_mem_
Definition: multi_runner.h:157
ripple::test::detail::multi_runner_base::add
void add(results const &r)
Definition: multi_runner.cpp:312
ripple::test::detail::multi_runner_base::inner::checkout_test_index
std::size_t checkout_test_index()
Definition: multi_runner.cpp:166
unordered_set
std::pair
ripple::test::detail::case_results::total
std::size_t total
Definition: multi_runner.h:51
ripple::test::multi_runner_child::print_log_
bool print_log_
Definition: multi_runner.h:239
ripple::test::detail::case_results::name
std::string name
Definition: multi_runner.h:50
ripple::test::detail::suite_results::start
clock_type::time_point start
Definition: multi_runner.h:65
ripple::test::detail::multi_runner_base::region_
boost::interprocess::mapped_region region_
Definition: multi_runner.h:158
ripple::test::multi_runner_parent::running_suites_
std::set< std::string > running_suites_
Definition: multi_runner.h:211
ripple::test::multi_runner_child::continue_keep_alive_
std::atomic< bool > continue_keep_alive_
Definition: multi_runner.h:241
ripple::test::detail::multi_runner_base::inner::results_
detail::results results_
Definition: multi_runner.h:120
std::stringstream
STL class.
ripple::test::detail::multi_runner_base::message_queue_send
void message_queue_send(MessageType mt, std::string const &s)
Definition: multi_runner.cpp:341
ripple::test::detail::multi_runner_base::inner::m_
boost::interprocess::interprocess_mutex m_
Definition: multi_runner.h:119
ripple::test::multi_runner_child::on_suite_end
virtual void on_suite_end() override
Definition: multi_runner.cpp:512
ripple::test::detail::multi_runner_base::inner::job_index_
std::atomic< std::size_t > job_index_
Definition: multi_runner.h:110
ripple::test::detail::suite_results
Definition: multi_runner.h:59
ripple::test::detail::results::add
void add(suite_results const &r)
Definition: multi_runner.cpp:64
ripple::test::detail::multi_runner_base::inner::keep_alive_
std::atomic< std::size_t > keep_alive_
Definition: multi_runner.h:117
ripple::test::detail::results
Definition: multi_runner.h:75
ripple::test::multi_runner_child::~multi_runner_child
~multi_runner_child()
Definition: multi_runner.cpp:493
ripple::test::detail::case_results::case_results
case_results(std::string name_="")
Definition: multi_runner.h:54
ripple::test::detail::multi_runner_base::inner::test_index_
std::atomic< std::size_t > test_index_
Definition: multi_runner.h:111
ripple::test::multi_runner_child::on_suite_begin
virtual void on_suite_begin(beast::unit_test::suite_info const &info) override
Definition: multi_runner.cpp:505
ripple::test::detail::multi_runner_base::inner_
inner * inner_
Definition: multi_runner.h:155
ripple::test::detail::results::cases
std::size_t cases
Definition: multi_runner.h:85
thread
ripple::test::multi_runner_parent::continue_message_queue_
std::atomic< bool > continue_message_queue_
Definition: multi_runner.h:208
ripple::test::detail::multi_runner_base::checkout_job_index
std::size_t checkout_job_index()
Definition: multi_runner.cpp:291
std::ostream
STL class.
ripple::test::multi_runner_child::suite_results_
detail::suite_results suite_results_
Definition: multi_runner.h:235
ripple::test::detail::multi_runner_base::any_failed
bool any_failed() const
Definition: multi_runner.cpp:298
chrono
ripple::test::detail::multi_runner_base::inner::checkout_job_index
std::size_t checkout_job_index()
Definition: multi_runner.cpp:159
ripple::test::detail::multi_runner_base::inner::add
void add(results const &r)
Definition: multi_runner.cpp:201
ripple::test::multi_runner_parent::~multi_runner_parent
~multi_runner_parent()
Definition: multi_runner.cpp:426
ripple::test::detail::multi_runner_base::shared_mem_name_
static constexpr const char * shared_mem_name_
Definition: multi_runner.h:148
ripple::test::multi_runner_child::results_
detail::results results_
Definition: multi_runner.h:234
ripple::test::detail::multi_runner_base< true >::MessageType
MessageType
Definition: multi_runner.h:163
ripple::test::detail::results::total
std::size_t total
Definition: multi_runner.h:86
ripple::test::multi_runner_child::on_log
virtual void on_log(std::string const &s) override
Definition: multi_runner.cpp:560
ripple::test::detail::suite_results::cases
std::size_t cases
Definition: multi_runner.h:62
ripple::test::multi_runner_child::num_jobs_
std::size_t num_jobs_
Definition: multi_runner.h:237
ripple::test::multi_runner_child::case_results_
detail::case_results case_results_
Definition: multi_runner.h:236
ripple::test::multi_runner_parent::any_failed
bool any_failed() const
Definition: multi_runner.cpp:443
std::uint8_t
ripple::test::multi_runner_parent::multi_runner_parent
multi_runner_parent()
Definition: multi_runner.cpp:360
ripple::test::detail::suite_results::add
void add(case_results const &r)
Definition: multi_runner.cpp:54
atomic
ripple::test::detail::results::start
clock_type::time_point start
Definition: multi_runner.h:89
ripple::test::detail::results::print
void print(S &s)
Definition: multi_runner.cpp:138
std::advance
T advance(T... args)
ripple::test::multi_runner_child::on_case_end
virtual void on_case_end() override
Definition: multi_runner.cpp:535
ripple::test::multi_runner_parent::message_queue_thread_
std::thread message_queue_thread_
Definition: multi_runner.h:209
ripple::test::detail::multi_runner_base
Definition: multi_runner.h:103
ripple::test::detail::results::failed
std::size_t failed
Definition: multi_runner.h:87
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::multi_runner_child::on_pass
virtual void on_pass() override
Definition: multi_runner.cpp:541
ripple::test::multi_runner_child::on_fail
virtual void on_fail(std::string const &reason) override
Definition: multi_runner.cpp:547
ripple::test::detail::results::merge
void merge(results const &r)
Definition: multi_runner.cpp:110
ripple::test::detail::results::top
boost::container::static_vector< run_time, max_top > top
Definition: multi_runner.h:88
std
STL namespace.
ripple::test::detail::multi_runner_base::MessageType::test_end
@ test_end
ripple::test::multi_runner_child::operator=
multi_runner_child & operator=(multi_runner_child const &)=delete
ripple::test::detail::results::max_top
@ max_top
Definition: multi_runner.h:82
ripple::test::multi_runner_child
A class to run a subset of unit tests.
Definition: multi_runner.h:229
ripple::test::detail::multi_runner_base::inner::any_failed_
std::atomic< bool > any_failed_
Definition: multi_runner.h:112
ripple::test::detail::multi_runner_base::~multi_runner_base
~multi_runner_base()
Definition: multi_runner.cpp:272
std::stringstream::str
T str(T... args)
std::size_t
ripple::test::multi_runner_child::multi_runner_child
multi_runner_child(multi_runner_child const &)=delete
ripple::test::detail::multi_runner_base::inc_keep_alive_count
void inc_keep_alive_count()
Definition: multi_runner.cpp:319
ripple::test::detail::multi_runner_base::MessageType::log
@ log
ripple::test::detail::results::static_string
boost::beast::static_string< 256 > static_string
Definition: multi_runner.h:77
numeric
ripple::test::multi_runner_parent
Manager for children running unit tests.
Definition: multi_runner.h:203
ripple::test::detail::multi_runner_base::inner::any_failed
bool any_failed() const
Definition: multi_runner.cpp:173
ripple::test::detail::multi_runner_base::message_queue_name_
static constexpr const char * message_queue_name_
Definition: multi_runner.h:151
ripple::test::multi_runner_child::on_case_begin
virtual void on_case_begin(std::string const &name) override
Definition: multi_runner.cpp:519
ripple::test::multi_runner_child::job_index_
std::size_t job_index_
Definition: multi_runner.h:233
ripple::test::detail::multi_runner_base::message_queue_
std::unique_ptr< boost::interprocess::message_queue > message_queue_
Definition: multi_runner.h:161
std::unique_ptr< boost::interprocess::message_queue >
ripple::test::multi_runner_parent::os_
std::ostream & os_
Definition: multi_runner.h:207
std::set< std::string >
ripple::test::detail::multi_runner_base::multi_runner_base
multi_runner_base()
Definition: multi_runner.cpp:217
ripple::test::detail::multi_runner_base::inner::get_keep_alive_count
std::size_t get_keep_alive_count()
Definition: multi_runner.cpp:194
ripple::test::multi_runner_child::keep_alive_thread_
std::thread keep_alive_thread_
Definition: multi_runner.h:242
ripple::test::detail::case_results::failed
std::size_t failed
Definition: multi_runner.h:52
string
std::chrono::steady_clock::now
T now(T... args)