xrpld
Loading...
Searching...
No Matches
multi_runner.h
1#pragma once
2
3#include <xrpl/beast/unit_test/global_suites.h>
4#include <xrpl/beast/unit_test/runner.h>
5#include <xrpl/beast/unit_test/suite_info.h>
6
7#include <boost/beast/core/static_string.hpp>
8#include <boost/container/static_vector.hpp>
9#include <boost/interprocess/ipc/message_queue.hpp>
10#include <boost/interprocess/mapped_region.hpp>
11#include <boost/interprocess/shared_memory_object.hpp>
12#include <boost/interprocess/sync/interprocess_mutex.hpp>
13
14#include <atomic>
15#include <chrono>
16#include <cstddef>
17#include <cstdint>
18#include <iterator>
19#include <memory>
20#include <ostream>
21#include <set>
22#include <sstream>
23#include <string>
24#include <thread>
25#include <utility>
26
27namespace xrpl {
28
29namespace detail {
30
32
34{
38
39 explicit CaseResults(std::string name = "") : name(std::move(name))
40 {
41 }
42};
43
45{
50 clock_type::time_point start = clock_type::now();
51
52 explicit SuiteResults(std::string name = "") : name(std::move(name))
53 {
54 }
55
56 void
57 add(CaseResults const& r);
58};
59
60struct Results
61{
62 using static_string = boost::beast::static_string<256>;
63 // results may be stored in shared memory. Use `static_string` to ensure
64 // pointers from different memory spaces do not co-mingle
66
67 static constexpr auto kMaxTop = 10;
68
73 boost::container::static_vector<run_time, kMaxTop> top;
74 clock_type::time_point start = clock_type::now();
75
76 void
77 add(SuiteResults const& r);
78
79 void
80 merge(Results const& r);
81
82 template <class S>
83 void
84 print(S& s);
85};
86
87template <bool IsParent>
89{
90 // `inner` will be created in shared memory. This is one way
91 // multi_runner_parent and multi_runner_child object communicate. The other
92 // way they communicate is through message queues.
93 struct Inner
94 {
98 // A parent process will periodically increment `keepAlive`. The child
99 // processes will check if `keepAlive` is being incremented. If it is
100 // not incremented for a sufficiently long time, the child will assume
101 // the parent process has died.
103
104 mutable boost::interprocess::interprocess_mutex m;
106
109
112
113 bool
114 anyFailed() const;
115
116 void
117 anyFailed(bool v);
118
120 tests() const;
121
123 suites() const;
124
125 void
127
130
131 void
132 add(Results const& r);
133
134 template <class S>
135 void
136 printResults(S& s);
137 };
138
139 static constexpr char const* kSharedMemName = "XrpldUnitTestSharedMem";
140 // name of the message queue a multi_runner_child will use to communicate
141 // with multi_runner_parent
142 static constexpr char const* kMessageQueueName = "XrpldUnitTestMessageQueue";
143
144 // `inner_` will be created in shared memory
146 // shared memory to use for the `inner` member
147 boost::interprocess::shared_memory_object sharedMem_;
148 boost::interprocess::mapped_region region_;
149
150protected:
152
154 void
156
157public:
160
163
166
167 void
168 anyFailed(bool v);
169
170 void
171 add(Results const& r);
172
173 void
175
178
179 template <class S>
180 void
181 printResults(S& s);
182
183 [[nodiscard]] bool
184 anyFailed() const;
185
186 [[nodiscard]] std::size_t
187 tests() const;
188
189 [[nodiscard]] std::size_t
190 suites() const;
191
192 void
193 addFailures(std::size_t failures);
194};
195
196} // namespace detail
197
198namespace test {
199
200//------------------------------------------------------------------------------
201
205class MultiRunnerParent : private detail::MultiRunnerBase</*IsParent*/ true>
206{
207private:
208 // message_queue_ is used to collect log messages from the children
212 // track running suites so if a child crashes the culprit can be flagged
214
215public:
218 operator=(MultiRunnerParent const&) = delete;
219
222
223 [[nodiscard]] bool
224 anyFailed() const;
225
226 [[nodiscard]] std::size_t
227 tests() const;
228
229 [[nodiscard]] std::size_t
230 suites() const;
231
232 void
233 addFailures(std::size_t failures);
234};
235
236//------------------------------------------------------------------------------
237
242 private detail::MultiRunnerBase</*IsParent*/ false>
243{
244private:
250 bool quiet_{false};
251 bool printLog_{true};
252
255
256public:
259 operator=(MultiRunnerChild const&) = delete;
260
261 MultiRunnerChild(std::size_t numJobs, bool quiet, bool printLog);
262 ~MultiRunnerChild() override;
263
264 [[nodiscard]] std::size_t
265 tests() const;
266
267 [[nodiscard]] std::size_t
268 suites() const;
269
270 void
271 addFailures(std::size_t failures);
272
273 template <class Pred>
274 bool
275 runMulti(Pred pred);
276
277private:
278 void
279 onSuiteBegin(beast::unit_test::SuiteInfo const& info) override;
280
281 void
282 onSuiteEnd() override;
283
284 void
285 onCaseBegin(std::string const& name) override;
286
287 void
288 onCaseEnd() override;
289
290 void
291 onPass() override;
292
293 void
294 onFail(std::string const& reason) override;
295
296 void
297 onLog(std::string const& s) override;
298};
299
300//------------------------------------------------------------------------------
301
302template <class Pred>
303bool
305{
306 auto const& suite = beast::unit_test::globalSuites();
307 auto const numTests = suite.size();
308 bool failed = false;
309
310 auto getTest = [&]() -> beast::unit_test::SuiteInfo const* {
311 auto const curTestIndex = checkoutTestIndex();
312 if (curTestIndex >= numTests)
313 return nullptr;
314 auto iter = suite.begin();
315 std::advance(iter, curTestIndex);
316 return &*iter;
317 };
318 while (auto t = getTest())
319 {
320 if (!pred(*t))
321 continue;
322 try
323 {
324 failed = run(*t) || failed;
325 }
326 catch (...)
327 {
328 if (numJobs_ <= 1)
329 throw; // a single process can die
330
331 // inform the parent
333 s << jobIndex_ << "> failed Unhandled exception in test.\n";
334 messageQueueSend(MessageType::Log, s.str());
335 failed = true;
336 }
337 }
338 anyFailed(failed);
339 return failed;
340}
341
342} // namespace test
343} // namespace xrpl
T advance(T... args)
Unit test runner interface.
Definition runner.h:23
bool run(SuiteInfo const &s)
Run the specified suite.
Definition runner.h:193
Associates a unit test type with metadata.
Definition suite_info.h:20
std::unique_ptr< boost::interprocess::message_queue > messageQueue_
static constexpr char const * kSharedMemName
void addFailures(std::size_t failures)
void add(Results const &r)
boost::interprocess::shared_memory_object sharedMem_
static constexpr char const * kMessageQueueName
boost::interprocess::mapped_region region_
void messageQueueSend(MessageType mt, std::string const &s)
void onSuiteEnd() override
Called when a suite ends.
MultiRunnerChild & operator=(MultiRunnerChild const &)=delete
void onFail(std::string const &reason) override
Called for each failing condition.
void onPass() override
Called for each passing condition.
void onSuiteBegin(beast::unit_test::SuiteInfo const &info) override
Called when a new suite starts.
void onLog(std::string const &s) override
Called when a test logs output.
detail::SuiteResults suiteResults_
void addFailures(std::size_t failures)
detail::CaseResults caseResults_
void onCaseEnd() override
Called when a new case ends.
std::atomic< bool > continueKeepAlive_
void onCaseBegin(std::string const &name) override
Called when a new case starts.
MultiRunnerChild(MultiRunnerChild const &)=delete
MultiRunnerParent & operator=(MultiRunnerParent const &)=delete
void addFailures(std::size_t failures)
std::set< std::string > runningSuites_
std::atomic< bool > continueMessageQueue_
MultiRunnerParent(MultiRunnerParent const &)=delete
SuiteList const & globalSuites()
Holds test suites registered during static initialization.
STL namespace.
std::chrono::steady_clock clock_type
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
T str(T... args)
CaseResults(std::string name="")
std::atomic< std::size_t > keepAlive
boost::interprocess::interprocess_mutex m
std::atomic< std::size_t > jobIndex
std::atomic< std::size_t > testIndex
static constexpr auto kMaxTop
std::pair< static_string, clock_type::duration > run_time
boost::container::static_vector< run_time, kMaxTop > top
boost::beast::static_string< 256 > static_string
clock_type::time_point start
void merge(Results const &r)
void add(SuiteResults const &r)
clock_type::time_point start
SuiteResults(std::string name="")
void add(CaseResults const &r)