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 {
43
44namespace 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
75struct 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
102template <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
135 tests() const;
136
138 suites() const;
139
140 void
142
145
146 void
147 add(results const& r);
148
149 template <class S>
150 void
151 print_results(S& s);
152 };
153
154 static constexpr char const* shared_mem_name_ = "RippledUnitTestSharedMem";
155 // name of the message queue a multi_runner_child will use to communicate
156 // with multi_runner_parent
157 static constexpr char const* message_queue_name_ =
158 "RippledUnitTestMessageQueue";
159
160 // `inner_` will be created in shared memory
162 // shared memory to use for the `inner` member
163 boost::interprocess::shared_memory_object shared_mem_;
164 boost::interprocess::mapped_region region_;
165
166protected:
168
170 void
172
173public:
176
179
182
183 void
184 any_failed(bool v);
185
186 void
187 add(results const& r);
188
189 void
191
194
195 template <class S>
196 void
197 print_results(S& s);
198
199 bool
200 any_failed() const;
201
203 tests() const;
204
206 suites() const;
207
208 void
209 add_failures(std::size_t failures);
210};
211
212} // namespace detail
213
214namespace test {
215
216//------------------------------------------------------------------------------
217
220class multi_runner_parent : private detail::multi_runner_base</*IsParent*/ true>
221{
222private:
223 // message_queue_ is used to collect log messages from the children
227 // track running suites so if a child crashes the culprit can be flagged
229
230public:
234
237
238 bool
239 any_failed() const;
240
242 tests() const;
243
245 suites() const;
246
247 void
248 add_failures(std::size_t failures);
249};
250
251//------------------------------------------------------------------------------
252
256 private detail::multi_runner_base</*IsParent*/ false>
257{
258private:
264 bool quiet_{false};
265 bool print_log_{true};
266
269
270public:
274
275 multi_runner_child(std::size_t num_jobs, bool quiet, bool print_log);
277
279 tests() const;
280
282 suites() const;
283
284 void
285 add_failures(std::size_t failures);
286
287 template <class Pred>
288 bool
289 run_multi(Pred pred);
290
291private:
292 virtual void
293 on_suite_begin(beast::unit_test::suite_info const& info) override;
294
295 virtual void
296 on_suite_end() override;
297
298 virtual void
299 on_case_begin(std::string const& name) override;
300
301 virtual void
302 on_case_end() override;
303
304 virtual void
305 on_pass() override;
306
307 virtual void
308 on_fail(std::string const& reason) override;
309
310 virtual void
311 on_log(std::string const& s) override;
312};
313
314//------------------------------------------------------------------------------
315
316template <class Pred>
317bool
319{
321 auto const num_tests = suite.size();
322 bool failed = false;
323
324 auto get_test = [&]() -> beast::unit_test::suite_info const* {
325 auto const cur_test_index = checkout_test_index();
326 if (cur_test_index >= num_tests)
327 return nullptr;
328 auto iter = suite.begin();
329 std::advance(iter, cur_test_index);
330 return &*iter;
331 };
332 while (auto t = get_test())
333 {
334 if (!pred(*t))
335 continue;
336 try
337 {
338 failed = run(*t) || failed;
339 }
340 catch (...)
341 {
342 if (num_jobs_ <= 1)
343 throw; // a single process can die
344
345 // inform the parent
347 s << job_index_ << "> failed Unhandled exception in test.\n";
348 message_queue_send(MessageType::log, s.str());
349 failed = true;
350 }
351 }
352 any_failed(failed);
353 return failed;
354}
355
356} // namespace test
357} // namespace ripple
358
359#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
std::unique_ptr< boost::interprocess::message_queue > message_queue_
static constexpr char const * message_queue_name_
void message_queue_send(MessageType mt, std::string const &s)
boost::interprocess::mapped_region region_
void add_failures(std::size_t failures)
static constexpr char const * shared_mem_name_
boost::interprocess::shared_memory_object shared_mem_
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_
std::atomic< std::size_t > keep_alive_
std::atomic< std::size_t > test_index_
std::atomic< std::size_t > job_index_
void add(suite_results const &r)
boost::beast::static_string< 256 > static_string
boost::container::static_vector< run_time, max_top > top
clock_type::time_point start
void merge(results const &r)
void add(case_results const &r)
clock_type::time_point start
suite_results(std::string name_="")