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
6#include <boost/beast/core/static_string.hpp>
7#include <boost/container/static_vector.hpp>
8#include <boost/interprocess/ipc/message_queue.hpp>
9#include <boost/interprocess/mapped_region.hpp>
10#include <boost/interprocess/shared_memory_object.hpp>
11#include <boost/interprocess/sync/interprocess_mutex.hpp>
12
13#include <atomic>
14#include <chrono>
15#include <numeric>
16#include <sstream>
17#include <string>
18#include <thread>
19#include <unordered_set>
20#include <utility>
21
22namespace xrpl {
23
24namespace detail {
25
27
29{
33
34 explicit CaseResults(std::string name = "") : name(std::move(name))
35 {
36 }
37};
38
40{
45 clock_type::time_point start = clock_type::now();
46
47 explicit SuiteResults(std::string name = "") : name(std::move(name))
48 {
49 }
50
51 void
52 add(CaseResults const& r);
53};
54
55struct Results
56{
57 using static_string = boost::beast::static_string<256>;
58 // results may be stored in shared memory. Use `static_string` to ensure
59 // pointers from different memory spaces do not co-mingle
61
62 static constexpr auto kMaxTop = 10;
63
68 boost::container::static_vector<run_time, kMaxTop> top;
69 clock_type::time_point start = clock_type::now();
70
71 void
72 add(SuiteResults const& r);
73
74 void
75 merge(Results const& r);
76
77 template <class S>
78 void
79 print(S& s);
80};
81
82template <bool IsParent>
84{
85 // `inner` will be created in shared memory. This is one way
86 // multi_runner_parent and multi_runner_child object communicate. The other
87 // way they communicate is through message queues.
88 struct Inner
89 {
93 // A parent process will periodically increment `keepAlive`. The child
94 // processes will check if `keepAlive` is being incremented. If it is
95 // not incremented for a sufficiently long time, the child will assume
96 // the parent process has died.
98
99 mutable boost::interprocess::interprocess_mutex m;
101
104
107
108 bool
109 anyFailed() const;
110
111 void
112 anyFailed(bool v);
113
115 tests() const;
116
118 suites() const;
119
120 void
122
125
126 void
127 add(Results const& r);
128
129 template <class S>
130 void
131 printResults(S& s);
132 };
133
134 static constexpr char const* kSharedMemName = "XrpldUnitTestSharedMem";
135 // name of the message queue a multi_runner_child will use to communicate
136 // with multi_runner_parent
137 static constexpr char const* kMessageQueueName = "XrpldUnitTestMessageQueue";
138
139 // `inner_` will be created in shared memory
141 // shared memory to use for the `inner` member
142 boost::interprocess::shared_memory_object sharedMem_;
143 boost::interprocess::mapped_region region_;
144
145protected:
147
149 void
151
152public:
155
158
161
162 void
163 anyFailed(bool v);
164
165 void
166 add(Results const& r);
167
168 void
170
173
174 template <class S>
175 void
176 printResults(S& s);
177
178 [[nodiscard]] bool
179 anyFailed() const;
180
181 [[nodiscard]] std::size_t
182 tests() const;
183
184 [[nodiscard]] std::size_t
185 suites() const;
186
187 void
188 addFailures(std::size_t failures);
189};
190
191} // namespace detail
192
193namespace test {
194
195//------------------------------------------------------------------------------
196
199class MultiRunnerParent : private detail::MultiRunnerBase</*IsParent*/ true>
200{
201private:
202 // message_queue_ is used to collect log messages from the children
206 // track running suites so if a child crashes the culprit can be flagged
208
209public:
212 operator=(MultiRunnerParent const&) = delete;
213
216
217 [[nodiscard]] bool
218 anyFailed() const;
219
220 [[nodiscard]] std::size_t
221 tests() const;
222
223 [[nodiscard]] std::size_t
224 suites() const;
225
226 void
227 addFailures(std::size_t failures);
228};
229
230//------------------------------------------------------------------------------
231
235 private detail::MultiRunnerBase</*IsParent*/ false>
236{
237private:
243 bool quiet_{false};
244 bool printLog_{true};
245
248
249public:
252 operator=(MultiRunnerChild const&) = delete;
253
254 MultiRunnerChild(std::size_t numJobs, bool quiet, bool printLog);
255 ~MultiRunnerChild() override;
256
257 [[nodiscard]] std::size_t
258 tests() const;
259
260 [[nodiscard]] std::size_t
261 suites() const;
262
263 void
264 addFailures(std::size_t failures);
265
266 template <class Pred>
267 bool
268 runMulti(Pred pred);
269
270private:
271 void
272 onSuiteBegin(beast::unit_test::SuiteInfo const& info) override;
273
274 void
275 onSuiteEnd() override;
276
277 void
278 onCaseBegin(std::string const& name) override;
279
280 void
281 onCaseEnd() override;
282
283 void
284 onPass() override;
285
286 void
287 onFail(std::string const& reason) override;
288
289 void
290 onLog(std::string const& s) override;
291};
292
293//------------------------------------------------------------------------------
294
295template <class Pred>
296bool
298{
299 auto const& suite = beast::unit_test::globalSuites();
300 auto const numTests = suite.size();
301 bool failed = false;
302
303 auto getTest = [&]() -> beast::unit_test::SuiteInfo const* {
304 auto const curTestIndex = checkoutTestIndex();
305 if (curTestIndex >= numTests)
306 return nullptr;
307 auto iter = suite.begin();
308 std::advance(iter, curTestIndex);
309 return &*iter;
310 };
311 while (auto t = getTest())
312 {
313 if (!pred(*t))
314 continue;
315 try
316 {
317 failed = run(*t) || failed;
318 }
319 catch (...)
320 {
321 if (numJobs_ <= 1)
322 throw; // a single process can die
323
324 // inform the parent
326 s << jobIndex_ << "> failed Unhandled exception in test.\n";
327 messageQueueSend(MessageType::Log, s.str());
328 failed = true;
329 }
330 }
331 anyFailed(failed);
332 return failed;
333}
334
335} // namespace test
336} // namespace xrpl
T advance(T... args)
Unit test runner interface.
Definition runner.h:22
bool run(SuiteInfo const &s)
Run the specified suite.
Definition runner.h:170
Associates a unit test type with metadata.
Definition suite_info.h:18
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)