]> git.proxmox.com Git - ceph.git/blame - ceph/src/Beast/extras/beast/unit_test/suite.hpp
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / Beast / extras / beast / unit_test / suite.hpp
CommitLineData
7c673cae
FG
1//
2// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7
8#ifndef BEAST_UNIT_TEST_SUITE_HPP
9#define BEAST_UNIT_TEST_SUITE_HPP
10
11#include <beast/unit_test/runner.hpp>
12#include <boost/filesystem.hpp>
13#include <boost/lexical_cast.hpp>
14#include <ostream>
15#include <sstream>
16#include <string>
17
18namespace beast {
19namespace unit_test {
20
21namespace detail {
22
23template<class String>
24static
25std::string
26make_reason(String const& reason,
27 char const* file, int line)
28{
29 std::string s(reason);
30 if(! s.empty())
31 s.append(": ");
32 namespace fs = boost::filesystem;
33 s.append(fs::path{file}.filename().string());
34 s.append("(");
35 s.append(boost::lexical_cast<std::string>(line));
36 s.append(")");
37 return s;
38}
39
40} // detail
41
42class thread;
43
44enum abort_t
45{
46 no_abort_on_fail,
47 abort_on_fail
48};
49
50/** A testsuite class.
51
52 Derived classes execute a series of testcases, where each testcase is
53 a series of pass/fail tests. To provide a unit test using this class,
54 derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a
55 translation unit.
56*/
57class suite
58{
59private:
60 bool abort_ = false;
61 bool aborted_ = false;
62 runner* runner_ = nullptr;
63
64 // This exception is thrown internally to stop the current suite
65 // in the event of a failure, if the option to stop is set.
66 struct abort_exception : public std::exception
67 {
68 char const*
69 what() const noexcept override
70 {
71 return "test suite aborted";
72 }
73 };
74
75 template<class CharT, class Traits, class Allocator>
76 class log_buf
77 : public std::basic_stringbuf<CharT, Traits, Allocator>
78 {
79 suite& suite_;
80
81 public:
82 explicit
83 log_buf(suite& self)
84 : suite_(self)
85 {
86 }
87
88 ~log_buf()
89 {
90 sync();
91 }
92
93 int
94 sync() override
95 {
96 auto const& s = this->str();
97 if(s.size() > 0)
98 suite_.runner_->log(s);
99 this->str("");
100 return 0;
101 }
102 };
103
104 template<
105 class CharT,
106 class Traits = std::char_traits<CharT>,
107 class Allocator = std::allocator<CharT>
108 >
109 class log_os : public std::basic_ostream<CharT, Traits>
110 {
111 log_buf<CharT, Traits, Allocator> buf_;
112
113 public:
114 explicit
115 log_os(suite& self)
116 : std::basic_ostream<CharT, Traits>(&buf_)
117 , buf_(self)
118 {
119 }
120 };
121
122 class scoped_testcase;
123
124 class testcase_t
125 {
126 suite& suite_;
127 std::stringstream ss_;
128
129 public:
130 explicit
131 testcase_t(suite& self)
132 : suite_(self)
133 {
134 }
135
136 /** Open a new testcase.
137
138 A testcase is a series of evaluated test conditions. A test
139 suite may have multiple test cases. A test is associated with
140 the last opened testcase. When the test first runs, a default
141 unnamed case is opened. Tests with only one case may omit the
142 call to testcase.
143
144 @param abort Determines if suite continues running after a failure.
145 */
146 void
147 operator()(std::string const& name,
148 abort_t abort = no_abort_on_fail);
149
150 scoped_testcase
151 operator()(abort_t abort);
152
153 template<class T>
154 scoped_testcase
155 operator<<(T const& t);
156 };
157
158public:
159 /** Logging output stream.
160
161 Text sent to the log output stream will be forwarded to
162 the output stream associated with the runner.
163 */
164 log_os<char> log;
165
166 /** Memberspace for declaring test cases. */
167 testcase_t testcase;
168
169 /** Returns the "current" running suite.
170 If no suite is running, nullptr is returned.
171 */
172 static
173 suite*
174 this_suite()
175 {
176 return *p_this_suite();
177 }
178
179 suite()
180 : log(*this)
181 , testcase(*this)
182 {
183 }
184
185 /** Invokes the test using the specified runner.
186
187 Data members are set up here instead of the constructor as a
188 convenience to writing the derived class to avoid repetition of
189 forwarded constructor arguments to the base.
190 Normally this is called by the framework for you.
191 */
192 template<class = void>
193 void
194 operator()(runner& r);
195
196 /** Record a successful test condition. */
197 template<class = void>
198 void
199 pass();
200
201 /** Record a failure.
202
203 @param reason Optional text added to the output on a failure.
204
205 @param file The source code file where the test failed.
206
207 @param line The source code line number where the test failed.
208 */
209 /** @{ */
210 template<class String>
211 void
212 fail(String const& reason, char const* file, int line);
213
214 template<class = void>
215 void
216 fail(std::string const& reason = "");
217 /** @} */
218
219 /** Evaluate a test condition.
220
221 This function provides improved logging by incorporating the
222 file name and line number into the reported output on failure,
223 as well as additional text specified by the caller.
224
225 @param shouldBeTrue The condition to test. The condition
226 is evaluated in a boolean context.
227
228 @param reason Optional added text to output on a failure.
229
230 @param file The source code file where the test failed.
231
232 @param line The source code line number where the test failed.
233
234 @return `true` if the test condition indicates success.
235 */
236 /** @{ */
237 template<class Condition>
238 bool
239 expect(Condition const& shouldBeTrue)
240 {
241 return expect(shouldBeTrue, "");
242 }
243
244 template<class Condition, class String>
245 bool
246 expect(Condition const& shouldBeTrue, String const& reason);
247
248 template<class Condition>
249 bool
250 expect(Condition const& shouldBeTrue,
251 char const* file, int line)
252 {
253 return expect(shouldBeTrue, "", file, line);
254 }
255
256 template<class Condition, class String>
257 bool
258 expect(Condition const& shouldBeTrue,
259 String const& reason, char const* file, int line);
260 /** @} */
261
262 //
263 // DEPRECATED
264 //
265 // Expect an exception from f()
266 template<class F, class String>
267 bool
268 except(F&& f, String const& reason);
269 template<class F>
270 bool
271 except(F&& f)
272 {
273 return except(f, "");
274 }
275 template<class E, class F, class String>
276 bool
277 except(F&& f, String const& reason);
278 template<class E, class F>
279 bool
280 except(F&& f)
281 {
282 return except<E>(f, "");
283 }
284 template<class F, class String>
285 bool
286 unexcept(F&& f, String const& reason);
287 template<class F>
288 bool
289 unexcept(F&& f)
290 {
291 return unexcept(f, "");
292 }
293
294 /** Return the argument associated with the runner. */
295 std::string const&
296 arg() const
297 {
298 return runner_->arg();
299 }
300
301 // DEPRECATED
302 // @return `true` if the test condition indicates success(a false value)
303 template<class Condition, class String>
304 bool
305 unexpected(Condition shouldBeFalse,
306 String const& reason);
307
308 template<class Condition>
309 bool
310 unexpected(Condition shouldBeFalse)
311 {
312 return unexpected(shouldBeFalse, "");
313 }
314
315private:
316 friend class thread;
317
318 static
319 suite**
320 p_this_suite()
321 {
322 static suite* pts = nullptr;
323 return &pts;
324 }
325
326 /** Runs the suite. */
327 virtual
328 void
329 run() = 0;
330
331 void
332 propagate_abort();
333
334 template<class = void>
335 void
336 run(runner& r);
337};
338
339//------------------------------------------------------------------------------
340
341// Helper for streaming testcase names
342class suite::scoped_testcase
343{
344private:
345 suite& suite_;
346 std::stringstream& ss_;
347
348public:
349 scoped_testcase& operator=(scoped_testcase const&) = delete;
350
351 ~scoped_testcase()
352 {
353 auto const& name = ss_.str();
354 if(! name.empty())
355 suite_.runner_->testcase(name);
356 }
357
358 scoped_testcase(suite& self, std::stringstream& ss)
359 : suite_(self)
360 , ss_(ss)
361 {
362 ss_.clear();
363 ss_.str({});
364 }
365
366 template<class T>
367 scoped_testcase(suite& self,
368 std::stringstream& ss, T const& t)
369 : suite_(self)
370 , ss_(ss)
371 {
372 ss_.clear();
373 ss_.str({});
374 ss_ << t;
375 }
376
377 template<class T>
378 scoped_testcase&
379 operator<<(T const& t)
380 {
381 ss_ << t;
382 return *this;
383 }
384};
385
386//------------------------------------------------------------------------------
387
388inline
389void
390suite::testcase_t::operator()(
391 std::string const& name, abort_t abort)
392{
393 suite_.abort_ = abort == abort_on_fail;
394 suite_.runner_->testcase(name);
395}
396
397inline
398suite::scoped_testcase
399suite::testcase_t::operator()(abort_t abort)
400{
401 suite_.abort_ = abort == abort_on_fail;
402 return { suite_, ss_ };
403}
404
405template<class T>
406inline
407suite::scoped_testcase
408suite::testcase_t::operator<<(T const& t)
409{
410 return { suite_, ss_, t };
411}
412
413//------------------------------------------------------------------------------
414
415template<class>
416void
417suite::
418operator()(runner& r)
419{
420 *p_this_suite() = this;
421 try
422 {
423 run(r);
424 *p_this_suite() = nullptr;
425 }
426 catch(...)
427 {
428 *p_this_suite() = nullptr;
429 throw;
430 }
431}
432
433template<class Condition, class String>
434bool
435suite::
436expect(
437 Condition const& shouldBeTrue, String const& reason)
438{
439 if(shouldBeTrue)
440 {
441 pass();
442 return true;
443 }
444 fail(reason);
445 return false;
446}
447
448template<class Condition, class String>
449bool
450suite::
451expect(Condition const& shouldBeTrue,
452 String const& reason, char const* file, int line)
453{
454 if(shouldBeTrue)
455 {
456 pass();
457 return true;
458 }
459 fail(detail::make_reason(reason, file, line));
460 return false;
461}
462
463// DEPRECATED
464
465template<class F, class String>
466bool
467suite::
468except(F&& f, String const& reason)
469{
470 try
471 {
472 f();
473 fail(reason);
474 return false;
475 }
476 catch(...)
477 {
478 pass();
479 }
480 return true;
481}
482
483template<class E, class F, class String>
484bool
485suite::
486except(F&& f, String const& reason)
487{
488 try
489 {
490 f();
491 fail(reason);
492 return false;
493 }
494 catch(E const&)
495 {
496 pass();
497 }
498 return true;
499}
500
501template<class F, class String>
502bool
503suite::
504unexcept(F&& f, String const& reason)
505{
506 try
507 {
508 f();
509 pass();
510 return true;
511 }
512 catch(...)
513 {
514 fail(reason);
515 }
516 return false;
517}
518
519template<class Condition, class String>
520bool
521suite::
522unexpected(
523 Condition shouldBeFalse, String const& reason)
524{
525 bool const b =
526 static_cast<bool>(shouldBeFalse);
527 if(! b)
528 pass();
529 else
530 fail(reason);
531 return ! b;
532}
533
534template<class>
535void
536suite::
537pass()
538{
539 propagate_abort();
540 runner_->pass();
541}
542
543// ::fail
544template<class>
545void
546suite::
547fail(std::string const& reason)
548{
549 propagate_abort();
550 runner_->fail(reason);
551 if(abort_)
552 {
553 aborted_ = true;
554 throw abort_exception();
555 }
556}
557
558template<class String>
559void
560suite::
561fail(String const& reason, char const* file, int line)
562{
563 fail(detail::make_reason(reason, file, line));
564}
565
566inline
567void
568suite::
569propagate_abort()
570{
571 if(abort_ && aborted_)
572 throw abort_exception();
573}
574
575template<class>
576void
577suite::
578run(runner& r)
579{
580 runner_ = &r;
581
582 try
583 {
584 run();
585 }
586 catch(abort_exception const&)
587 {
588 // ends the suite
589 }
590 catch(std::exception const& e)
591 {
592 runner_->fail("unhandled exception: " +
593 std::string(e.what()));
594 }
595 catch(...)
596 {
597 runner_->fail("unhandled exception");
598 }
599}
600
601#ifndef BEAST_EXPECT
602/** Check a precondition.
603
604 If the condition is false, the file and line number are reported.
605*/
606#define BEAST_EXPECT(cond) expect(cond, __FILE__, __LINE__)
607#endif
608
609#ifndef BEAST_EXPECTS
610/** Check a precondition.
611
612 If the condition is false, the file and line number are reported.
613*/
614#define BEAST_EXPECTS(cond, reason) ((cond) ? (pass(), true) : \
615 (fail((reason), __FILE__, __LINE__), false))
616#endif
617
618} // unit_test
619} // beast
620
621//------------------------------------------------------------------------------
622
623// detail:
624// This inserts the suite with the given manual flag
625#define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual) \
626 static beast::unit_test::detail::insert_suite <Class##_test> \
627 Library ## Module ## Class ## _test_instance( \
628 #Class, #Module, #Library, manual)
629
630//------------------------------------------------------------------------------
631
632// Preprocessor directives for controlling unit test definitions.
633
634// If this is already defined, don't redefine it. This allows
635// programs to provide custom behavior for testsuite definitions
636//
637#ifndef BEAST_DEFINE_TESTSUITE
638
639/** Enables insertion of test suites into the global container.
640 The default is to insert all test suite definitions into the global
641 container. If BEAST_DEFINE_TESTSUITE is user defined, this macro
642 has no effect.
643*/
644#ifndef BEAST_NO_UNIT_TEST_INLINE
645#define BEAST_NO_UNIT_TEST_INLINE 0
646#endif
647
648/** Define a unit test suite.
649
650 Class The type representing the class being tested.
651 Module Identifies the module.
652 Library Identifies the library.
653
654 The declaration for the class implementing the test should be the same
655 as Class ## _test. For example, if Class is aged_ordered_container, the
656 test class must be declared as:
657
658 @code
659
660 struct aged_ordered_container_test : beast::unit_test::suite
661 {
662 //...
663 };
664
665 @endcode
666
667 The macro invocation must appear in the same namespace as the test class.
668*/
669
670#if BEAST_NO_UNIT_TEST_INLINE
671#define BEAST_DEFINE_TESTSUITE(Class,Module,Library)
672
673#else
674#include <beast/unit_test/global_suites.hpp>
675#define BEAST_DEFINE_TESTSUITE(Class,Module,Library) \
676 BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,false)
677#define BEAST_DEFINE_TESTSUITE_MANUAL(Class,Module,Library) \
678 BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,true)
679
680#endif
681
682#endif
683
684//------------------------------------------------------------------------------
685
686#endif