rippled
multi_runner.cpp
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 #include <test/unit_test/multi_runner.h>
21 
22 #include <beast/unit_test/amount.hpp>
23 
24 #include <boost/lexical_cast.hpp>
25 
26 #include <algorithm>
27 #include <iomanip>
28 #include <iostream>
29 #include <sstream>
30 
31 namespace ripple {
32 namespace test {
33 
34 extern void
35 incPorts();
36 
37 namespace detail {
38 
40 fmtdur(typename clock_type::duration const& d)
41 {
42  using namespace std::chrono;
43  auto const ms = duration_cast<milliseconds>(d);
44  if (ms < seconds{1})
45  return boost::lexical_cast<std::string>(ms.count()) + "ms";
47  ss << std::fixed << std::setprecision(1) << (ms.count() / 1000.) << "s";
48  return ss.str();
49 }
50 
51 //------------------------------------------------------------------------------
52 
53 void
55 {
56  ++cases;
57  total += r.total;
58  failed += r.failed;
59 }
60 
61 //------------------------------------------------------------------------------
62 
63 void
65 {
66  ++suites;
67  total += r.total;
68  cases += r.cases;
69  failed += r.failed;
70  auto const elapsed = clock_type::now() - r.start;
71  if (elapsed >= std::chrono::seconds{1})
72  {
73  auto const iter = std::lower_bound(
74  top.begin(),
75  top.end(),
76  elapsed,
77  [](run_time const& t1, typename clock_type::duration const& t2) {
78  return t1.second > t2;
79  });
80 
81  if (iter != top.end())
82  {
83  if (top.size() == max_top && iter == top.end() - 1)
84  {
85  // avoid invalidating the iterator
86  *iter = run_time{
87  static_string{static_string::string_view_type{r.name}},
88  elapsed};
89  }
90  else
91  {
92  if (top.size() == max_top)
93  top.resize(top.size() - 1);
94  top.emplace(
95  iter,
96  static_string{static_string::string_view_type{r.name}},
97  elapsed);
98  }
99  }
100  else if (top.size() < max_top)
101  {
102  top.emplace_back(
103  static_string{static_string::string_view_type{r.name}},
104  elapsed);
105  }
106  }
107 }
108 
109 void
111 {
112  suites += r.suites;
113  total += r.total;
114  cases += r.cases;
115  failed += r.failed;
116 
117  // combine the two top collections
118  boost::container::static_vector<run_time, 2 * max_top> top_result;
119  top_result.resize(top.size() + r.top.size());
120  std::merge(
121  top.begin(),
122  top.end(),
123  r.top.begin(),
124  r.top.end(),
125  top_result.begin(),
126  [](run_time const& t1, run_time const& t2) {
127  return t1.second > t2.second;
128  });
129 
130  if (top_result.size() > max_top)
131  top_result.resize(max_top);
132 
133  top = top_result;
134 }
135 
136 template <class S>
137 void
139 {
140  using namespace beast::unit_test;
141 
142  if (top.size() > 0)
143  {
144  s << "Longest suite times:\n";
145  for (auto const& [name, dur] : top)
146  s << std::setw(8) << fmtdur(dur) << " " << name << '\n';
147  }
148 
149  auto const elapsed = clock_type::now() - start;
150  s << fmtdur(elapsed) << ", " << amount{suites, "suite"} << ", "
151  << amount{cases, "case"} << ", " << amount{total, "test"} << " total, "
152  << amount{failed, "failure"} << std::endl;
153 }
154 
155 //------------------------------------------------------------------------------
156 
157 template <bool IsParent>
160 {
161  return job_index_++;
162 }
163 
164 template <bool IsParent>
167 {
168  return test_index_++;
169 }
170 
171 template <bool IsParent>
172 bool
174 {
175  return any_failed_;
176 }
177 
178 template <bool IsParent>
179 void
181 {
182  any_failed_ = any_failed_ || v;
183 }
184 
185 template <bool IsParent>
188 {
189  std::lock_guard l{m_};
190  return results_.total;
191 }
192 
193 template <bool IsParent>
196 {
197  std::lock_guard l{m_};
198  return results_.suites;
199 }
200 
201 template <bool IsParent>
202 void
204 {
205  ++keep_alive_;
206 }
207 
208 template <bool IsParent>
211 {
212  return keep_alive_;
213 }
214 
215 template <bool IsParent>
216 void
218 {
219  std::lock_guard l{m_};
220  results_.merge(r);
221 }
222 
223 template <bool IsParent>
224 template <class S>
225 void
227 {
228  std::lock_guard l{m_};
229  results_.print(s);
230 }
231 
232 template <bool IsParent>
234 {
235  try
236  {
237  if (IsParent)
238  {
239  // cleanup any leftover state for any previous failed runs
240  boost::interprocess::shared_memory_object::remove(shared_mem_name_);
241  boost::interprocess::message_queue::remove(message_queue_name_);
242  }
243 
244  shared_mem_ = boost::interprocess::shared_memory_object{
246  IsParent,
247  boost::interprocess::create_only_t,
248  boost::interprocess::open_only_t>{},
249  shared_mem_name_,
250  boost::interprocess::read_write};
251 
252  if (IsParent)
253  {
254  shared_mem_.truncate(sizeof(inner));
255  message_queue_ =
256  std::make_unique<boost::interprocess::message_queue>(
257  boost::interprocess::create_only,
258  message_queue_name_,
259  /*max messages*/ 16,
260  /*max message size*/ 1 << 20);
261  }
262  else
263  {
264  message_queue_ =
265  std::make_unique<boost::interprocess::message_queue>(
266  boost::interprocess::open_only, message_queue_name_);
267  }
268 
269  region_ = boost::interprocess::mapped_region{
270  shared_mem_, boost::interprocess::read_write};
271  if (IsParent)
272  inner_ = new (region_.get_address()) inner{};
273  else
274  inner_ = reinterpret_cast<inner*>(region_.get_address());
275  }
276  catch (...)
277  {
278  if (IsParent)
279  {
280  boost::interprocess::shared_memory_object::remove(shared_mem_name_);
281  boost::interprocess::message_queue::remove(message_queue_name_);
282  }
283  throw;
284  }
285 }
286 
287 template <bool IsParent>
289 {
290  if (IsParent)
291  {
292  inner_->~inner();
293  boost::interprocess::shared_memory_object::remove(shared_mem_name_);
294  boost::interprocess::message_queue::remove(message_queue_name_);
295  }
296 }
297 
298 template <bool IsParent>
301 {
302  return inner_->checkout_test_index();
303 }
304 
305 template <bool IsParent>
308 {
309  return inner_->checkout_job_index();
310 }
311 
312 template <bool IsParent>
313 bool
315 {
316  return inner_->any_failed();
317 }
318 
319 template <bool IsParent>
320 void
322 {
323  return inner_->any_failed(v);
324 }
325 
326 template <bool IsParent>
327 void
329 {
330  inner_->add(r);
331 }
332 
333 template <bool IsParent>
334 void
336 {
337  inner_->inc_keep_alive_count();
338 }
339 
340 template <bool IsParent>
343 {
344  return inner_->get_keep_alive_count();
345 }
346 
347 template <bool IsParent>
348 template <class S>
349 void
351 {
352  inner_->print_results(s);
353 }
354 
355 template <bool IsParent>
356 void
358  MessageType mt,
359  std::string const& s)
360 {
361  // must use a mutex since the two "sends" must happen in order
362  std::lock_guard l{inner_->m_};
363  message_queue_->send(&mt, sizeof(mt), /*priority*/ 0);
364  message_queue_->send(s.c_str(), s.size(), /*priority*/ 0);
365 }
366 
367 template <bool IsParent>
370 {
371  return inner_->tests();
372 }
373 
374 template <bool IsParent>
377 {
378  return inner_->suites();
379 }
380 
381 template <bool IsParent>
382 void
384 {
386  results.failed += failures;
387  add(results);
388  any_failed(failures != 0);
389 }
390 
391 template <bool IsParent>
393 template <bool IsParent>
395 
396 } // namespace detail
397 
398 //------------------------------------------------------------------------------
399 
401 {
403  std::vector<char> buf(1 << 20);
404  while (this->continue_message_queue_ ||
405  this->message_queue_->get_num_msg())
406  {
407  // let children know the parent is still alive
408  this->inc_keep_alive_count();
409  if (!this->message_queue_->get_num_msg())
410  {
411  // If a child does not see the keep alive count incremented,
412  // it will assume the parent has died. This sleep time needs
413  // to be small enough so the child will see increments from
414  // a live parent.
416  continue;
417  }
418  try
419  {
420  std::size_t recvd_size = 0;
421  unsigned int priority = 0;
422  this->message_queue_->receive(
423  buf.data(), buf.size(), recvd_size, priority);
424  if (!recvd_size)
425  continue;
426  assert(recvd_size == 1);
427  MessageType mt{*reinterpret_cast<MessageType*>(buf.data())};
428 
429  this->message_queue_->receive(
430  buf.data(), buf.size(), recvd_size, priority);
431  if (recvd_size)
432  {
433  std::string s{buf.data(), recvd_size};
434  switch (mt)
435  {
436  case MessageType::log:
437  this->os_ << s;
438  this->os_.flush();
439  break;
440  case MessageType::test_start:
441  running_suites_.insert(std::move(s));
442  break;
443  case MessageType::test_end:
445  break;
446  default:
447  assert(0); // unknown message type
448  }
449  }
450  }
451  catch (std::exception const& e)
452  {
453  std::cerr << "Error: " << e.what()
454  << " reading unit test message queue.\n";
455  return;
456  }
457  catch (...)
458  {
459  std::cerr << "Unknown error reading unit test message queue.\n";
460  return;
461  }
462  }
463  });
464 }
465 
467 {
468  using namespace beast::unit_test;
469 
470  continue_message_queue_ = false;
472 
474 
475  for (auto const& s : running_suites_)
476  {
477  os_ << "\nSuite: " << s
478  << " failed to complete. The child process may have crashed.\n";
479  }
480 }
481 
482 bool
484 {
486 }
487 
490 {
492 }
493 
496 {
498 }
499 
500 void
502 {
504 }
505 
506 //------------------------------------------------------------------------------
507 
509  std::size_t num_jobs,
510  bool quiet,
511  bool print_log)
512  : job_index_{checkout_job_index()}
513  , num_jobs_{num_jobs}
514  , quiet_{quiet}
515  , print_log_{!quiet || print_log}
516 {
517  // incPort twice (2*jobIndex_) because some tests need two envs
518  for (std::size_t i = 0; i < 2 * job_index_; ++i)
519  test::incPorts();
520 
521  if (num_jobs_ > 1)
522  {
523  keep_alive_thread_ = std::thread([this] {
524  std::size_t last_count = get_keep_alive_count();
525  while (this->continue_keep_alive_)
526  {
527  // Use a small sleep time so in the normal case the child
528  // process may shutdown quickly. However, to protect against
529  // false alarms, use a longer sleep time later on.
531  auto cur_count = this->get_keep_alive_count();
532  if (cur_count == last_count)
533  {
534  // longer sleep time to protect against false alarms
536  cur_count = this->get_keep_alive_count();
537  if (cur_count == last_count)
538  {
539  // assume parent process is no longer alive
540  std::cerr << "multi_runner_child " << job_index_
541  << ": Assuming parent died, exiting.\n";
542  std::exit(EXIT_FAILURE);
543  }
544  }
545  last_count = cur_count;
546  }
547  });
548  }
549 }
550 
552 {
553  if (num_jobs_ > 1)
554  {
555  continue_keep_alive_ = false;
557  }
558 
559  add(results_);
560 }
561 
564 {
565  return results_.total;
566 }
567 
570 {
571  return results_.suites;
572 }
573 
574 void
576 {
577  results_.failed += failures;
578  any_failed(failures != 0);
579 }
580 
581 void
582 multi_runner_child::on_suite_begin(beast::unit_test::suite_info const& info)
583 {
584  suite_results_ = detail::suite_results{info.full_name()};
585  message_queue_send(MessageType::test_start, suite_results_.name);
586 }
587 
588 void
590 {
592  message_queue_send(MessageType::test_end, suite_results_.name);
593 }
594 
595 void
597 {
599 
600  if (quiet_)
601  return;
602 
604  if (num_jobs_ > 1)
605  s << job_index_ << "> ";
606  s << suite_results_.name
607  << (case_results_.name.empty() ? "" : (" " + case_results_.name)) << '\n';
608  message_queue_send(MessageType::log, s.str());
609 }
610 
611 void
613 {
615 }
616 
617 void
619 {
621 }
622 
623 void
625 {
629  if (num_jobs_ > 1)
630  s << job_index_ << "> ";
631  s << "#" << case_results_.total << " failed" << (reason.empty() ? "" : ": ")
632  << reason << '\n';
633  message_queue_send(MessageType::log, s.str());
634 }
635 
636 void
638 {
639  if (!print_log_)
640  return;
641 
643  if (num_jobs_ > 1)
644  s << job_index_ << "> ";
645  s << msg;
646  message_queue_send(MessageType::log, s.str());
647 }
648 
649 namespace detail {
650 template class multi_runner_base<true>;
651 template class multi_runner_base<false>;
652 } // namespace detail
653 
654 } // namespace test
655 } // namespace ripple
ripple::test::detail::results::suites
std::size_t suites
Definition: multi_runner.h:84
ripple::test::detail::multi_runner_base::suites
std::size_t suites() const
Definition: multi_runner.cpp:376
ripple::test::detail::multi_runner_base::checkout_test_index
std::size_t checkout_test_index()
Definition: multi_runner.cpp:300
sstream
ripple::test::detail::multi_runner_base::inner::print_results
void print_results(S &s)
Definition: multi_runner.cpp:226
std::setprecision
T setprecision(T... args)
std::this_thread::sleep_for
T sleep_for(T... args)
ripple::test::detail::suite_results::failed
std::size_t failed
Definition: multi_runner.h:64
std::string
STL class.
ripple::test::detail::suite_results::total
std::size_t total
Definition: multi_runner.h:63
ripple::test::multi_runner_parent::add_failures
void add_failures(std::size_t failures)
Definition: multi_runner.cpp:501
ripple::test::detail::case_results
Definition: multi_runner.h:48
ripple::test::multi_runner_child::quiet_
bool quiet_
Definition: multi_runner.h:262
std::exception
STL class.
ripple::test::detail::multi_runner_base::inner::inc_keep_alive_count
void inc_keep_alive_count()
Definition: multi_runner.cpp:203
ripple::test::detail::suite_results::name
std::string name
Definition: multi_runner.h:61
ripple::test::detail::multi_runner_base::print_results
void print_results(S &s)
Definition: multi_runner.cpp:350
ripple::test::detail::multi_runner_base::get_keep_alive_count
std::size_t get_keep_alive_count()
Definition: multi_runner.cpp:342
ripple::test::detail::multi_runner_base::add
void add(results const &r)
Definition: multi_runner.cpp:328
ripple::test::detail::multi_runner_base::inner::checkout_test_index
std::size_t checkout_test_index()
Definition: multi_runner.cpp:166
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:263
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
std::vector< char >
beast::unit_test
Definition: define_print.cpp:16
std::string::size
T size(T... args)
ripple::test::multi_runner_parent::running_suites_
std::set< std::string > running_suites_
Definition: multi_runner.h:226
std::chrono::seconds
ripple::test::multi_runner_child::continue_keep_alive_
std::atomic< bool > continue_keep_alive_
Definition: multi_runner.h:265
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:357
std::lock_guard
STL class.
std::cerr
ripple::test::multi_runner_child::on_suite_end
virtual void on_suite_end() override
Definition: multi_runner.cpp:589
ripple::test::multi_runner_child::tests
std::size_t tests() const
Definition: multi_runner.cpp:563
iostream
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
algorithm
ripple::test::detail::results
Definition: multi_runner.h:75
ripple::test::multi_runner_parent::suites
std::size_t suites() const
Definition: multi_runner.cpp:495
ripple::test::multi_runner_child::~multi_runner_child
~multi_runner_child()
Definition: multi_runner.cpp:551
ripple::test::detail::fmtdur
std::string fmtdur(typename clock_type::duration const &d)
Definition: multi_runner.cpp:40
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:582
ripple::test::detail::multi_runner_base::add_failures
void add_failures(std::size_t failures)
Definition: multi_runner.cpp:383
ripple::test::detail::results::cases
std::size_t cases
Definition: multi_runner.h:85
ripple::test::multi_runner_child::suites
std::size_t suites() const
Definition: multi_runner.cpp:569
std::thread
STL class.
ripple::test::multi_runner_parent::continue_message_queue_
std::atomic< bool > continue_message_queue_
Definition: multi_runner.h:223
ripple::test::detail::multi_runner_base::checkout_job_index
std::size_t checkout_job_index()
Definition: multi_runner.cpp:307
ripple::test::multi_runner_child::suite_results_
detail::suite_results suite_results_
Definition: multi_runner.h:259
ripple::test::detail::multi_runner_base::any_failed
bool any_failed() const
Definition: multi_runner.cpp:314
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:217
ripple::test::multi_runner_parent::~multi_runner_parent
~multi_runner_parent()
Definition: multi_runner.cpp:466
std::string::c_str
T c_str(T... args)
ripple::test::multi_runner_child::results_
detail::results results_
Definition: multi_runner.h:258
ripple::test::detail::multi_runner_base< true >::MessageType
MessageType
Definition: multi_runner.h:169
ripple::test::detail::results::total
std::size_t total
Definition: multi_runner.h:86
std::ostream::flush
T flush(T... args)
ripple::test::multi_runner_child::on_log
virtual void on_log(std::string const &s) override
Definition: multi_runner.cpp:637
std::set::erase
T erase(T... args)
ripple::test::detail::suite_results::cases
std::size_t cases
Definition: multi_runner.h:62
ripple::test::multi_runner_child::add_failures
void add_failures(std::size_t failures)
Definition: multi_runner.cpp:575
ripple::test::multi_runner_child::num_jobs_
std::size_t num_jobs_
Definition: multi_runner.h:261
ripple::test::multi_runner_child::case_results_
detail::case_results case_results_
Definition: multi_runner.h:260
std::merge
T merge(T... args)
ripple::test::multi_runner_parent::any_failed
bool any_failed() const
Definition: multi_runner.cpp:483
ripple::test::multi_runner_parent::multi_runner_parent
multi_runner_parent()
Definition: multi_runner.cpp:400
ripple::test::detail::suite_results::add
void add(case_results const &r)
Definition: multi_runner.cpp:54
ripple::test::detail::multi_runner_base::inner::suites
std::size_t suites() const
Definition: multi_runner.cpp:195
ripple::test::detail::results::max_top
@ max_top
Definition: multi_runner.h:82
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
ripple::test::multi_runner_child::on_case_end
virtual void on_case_end() override
Definition: multi_runner.cpp:612
ripple::test::multi_runner_parent::message_queue_thread_
std::thread message_queue_thread_
Definition: multi_runner.h:224
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:618
std::lower_bound
T lower_bound(T... args)
std::endl
T endl(T... args)
ripple::test::multi_runner_child::on_fail
virtual void on_fail(std::string const &reason) override
Definition: multi_runner.cpp:624
iomanip
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.
std::set::insert
T insert(T... args)
ripple::test::multi_runner_parent::tests
std::size_t tests() const
Definition: multi_runner.cpp:489
ripple::test::incPorts
void incPorts()
Definition: envconfig.cpp:29
ripple::test::detail::multi_runner_base::tests
std::size_t tests() const
Definition: multi_runner.cpp:369
std::fixed
T fixed(T... args)
ripple::test::detail::multi_runner_base::inner::tests
std::size_t tests() const
Definition: multi_runner.cpp:187
std::string::empty
T empty(T... args)
ripple::test::detail::multi_runner_base::~multi_runner_base
~multi_runner_base()
Definition: multi_runner.cpp:288
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:335
ripple::test::detail::results::static_string
boost::beast::static_string< 256 > static_string
Definition: multi_runner.h:77
std::setw
T setw(T... args)
std::conditional_t
ripple::test::detail::multi_runner_base::inner::any_failed
bool any_failed() const
Definition: multi_runner.cpp:173
ripple::test::multi_runner_child::on_case_begin
virtual void on_case_begin(std::string const &name) override
Definition: multi_runner.cpp:596
ripple::test::multi_runner_child::job_index_
std::size_t job_index_
Definition: multi_runner.h:257
ripple::test::detail::multi_runner_base< true >::message_queue_
std::unique_ptr< boost::interprocess::message_queue > message_queue_
Definition: multi_runner.h:167
ripple::test::multi_runner_parent::os_
std::ostream & os_
Definition: multi_runner.h:222
std::vector::data
T data(T... args)
ripple::test::detail::multi_runner_base::multi_runner_base
multi_runner_base()
Definition: multi_runner.cpp:233
std::exit
T exit(T... args)
ripple::test::detail::multi_runner_base::inner::get_keep_alive_count
std::size_t get_keep_alive_count()
Definition: multi_runner.cpp:210
std::thread::join
T join(T... args)
std::exception::what
T what(T... args)
ripple::test::multi_runner_child::keep_alive_thread_
std::thread keep_alive_thread_
Definition: multi_runner.h:266
ripple::test::detail::case_results::failed
std::size_t failed
Definition: multi_runner.h:52
std::chrono
std::chrono::steady_clock::now
T now(T... args)