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