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

source code of boost/libs/beast/include/boost/beast/_experimental/unit_test/suite.hpp