2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
7 // Official repository: https://github.com/boostorg/beast
10 #ifndef BOOST_BEAST_UNIT_TEST_SUITE_HPP
11 #define BOOST_BEAST_UNIT_TEST_SUITE_HPP
13 #include <boost/beast/_experimental/unit_test/runner.hpp>
14 #include <boost/throw_exception.hpp>
25 template<class String>
27 make_reason(String const& reason,
28 char const* file, int line)
30 std::string s(reason);
33 char const* path = file + strlen(file);
46 s.append(std::to_string(line));
61 /** A testsuite class.
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
72 bool aborted_ = false;
73 runner* runner_ = nullptr;
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
80 what() const noexcept override
82 return "test suite aborted";
86 template<class CharT, class Traits, class Allocator>
88 : public std::basic_stringbuf<CharT, Traits, Allocator>
107 auto const& s = this->str();
109 suite_.runner_->log(s);
117 class Traits = std::char_traits<CharT>,
118 class Allocator = std::allocator<CharT>
120 class log_os : public std::basic_ostream<CharT, Traits>
122 log_buf<CharT, Traits, Allocator> buf_;
127 : std::basic_ostream<CharT, Traits>(&buf_)
133 class scoped_testcase;
138 std::stringstream ss_;
142 testcase_t(suite& self)
147 /** Open a new testcase.
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
155 @param abort Determines if suite continues running after a failure.
158 operator()(std::string const& name,
159 abort_t abort = no_abort_on_fail);
162 operator()(abort_t abort);
166 operator<<(T const& t);
170 /** Logging output stream.
172 Text sent to the log output stream will be forwarded to
173 the output stream associated with the runner.
177 /** Memberspace for declaring test cases. */
180 /** Returns the "current" running suite.
181 If no suite is running, nullptr is returned.
187 return *p_this_suite();
196 virtual ~suite() = default;
197 suite(suite const&) = delete;
198 suite& operator=(suite const&) = delete;
200 /** Invokes the test using the specified runner.
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.
207 template<class = void>
209 operator()(runner& r);
211 /** Record a successful test condition. */
212 template<class = void>
216 /** Record a failure.
218 @param reason Optional text added to the output on a failure.
220 @param file The source code file where the test failed.
222 @param line The source code line number where the test failed.
225 template<class String>
227 fail(String const& reason, char const* file, int line);
229 template<class = void>
231 fail(std::string const& reason = "");
234 /** Evaluate a test condition.
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.
240 @param shouldBeTrue The condition to test. The condition
241 is evaluated in a boolean context.
243 @param reason Optional added text to output on a failure.
245 @param file The source code file where the test failed.
247 @param line The source code line number where the test failed.
249 @return `true` if the test condition indicates success.
252 template<class Condition>
254 expect(Condition const& shouldBeTrue)
256 return expect(shouldBeTrue, "");
259 template<class Condition, class String>
261 expect(Condition const& shouldBeTrue, String const& reason);
263 template<class Condition>
265 expect(Condition const& shouldBeTrue,
266 char const* file, int line)
268 return expect(shouldBeTrue, "", file, line);
271 template<class Condition, class String>
273 expect(Condition const& shouldBeTrue,
274 String const& reason, char const* file, int line);
280 // Expect an exception from f()
281 template<class F, class String>
283 except(F&& f, String const& reason);
288 return except(f, "");
290 template<class E, class F, class String>
292 except(F&& f, String const& reason);
293 template<class E, class F>
297 return except<E>(f, "");
299 template<class F, class String>
301 unexcept(F&& f, String const& reason);
306 return unexcept(f, "");
309 /** Return the argument associated with the runner. */
313 return runner_->arg();
317 // @return `true` if the test condition indicates success(a false value)
318 template<class Condition, class String>
320 unexpected(Condition shouldBeFalse,
321 String const& reason);
323 template<class Condition>
325 unexpected(Condition shouldBeFalse)
327 return unexpected(shouldBeFalse, "");
337 static suite* pts = nullptr;
341 /** Runs the suite. */
349 template<class = void>
354 //------------------------------------------------------------------------------
356 // Helper for streaming testcase names
357 class suite::scoped_testcase
361 std::stringstream& ss_;
364 scoped_testcase& operator=(scoped_testcase const&) = delete;
368 auto const& name = ss_.str();
370 suite_.runner_->testcase(name);
373 scoped_testcase(suite& self, std::stringstream& ss)
382 scoped_testcase(suite& self,
383 std::stringstream& ss, T const& t)
394 operator<<(T const& t)
401 //------------------------------------------------------------------------------
405 suite::testcase_t::operator()(
406 std::string const& name, abort_t abort)
408 suite_.abort_ = abort == abort_on_fail;
409 suite_.runner_->testcase(name);
413 suite::scoped_testcase
414 suite::testcase_t::operator()(abort_t abort)
416 suite_.abort_ = abort == abort_on_fail;
417 return { suite_, ss_ };
422 suite::scoped_testcase
423 suite::testcase_t::operator<<(T const& t)
425 return { suite_, ss_, t };
428 //------------------------------------------------------------------------------
433 operator()(runner& r)
435 *p_this_suite() = this;
439 *p_this_suite() = nullptr;
443 *p_this_suite() = nullptr;
448 template<class Condition, class String>
452 Condition const& shouldBeTrue, String const& reason)
463 template<class Condition, class String>
466 expect(Condition const& shouldBeTrue,
467 String const& reason, char const* file, int line)
474 fail(detail::make_reason(reason, file, line));
480 template<class F, class String>
483 except(F&& f, String const& reason)
498 template<class E, class F, class String>
501 except(F&& f, String const& reason)
516 template<class F, class String>
519 unexcept(F&& f, String const& reason)
534 template<class Condition, class String>
538 Condition shouldBeFalse, String const& reason)
541 static_cast<bool>(shouldBeFalse);
562 fail(std::string const& reason)
565 runner_->fail(reason);
569 BOOST_THROW_EXCEPTION(abort_exception());
573 template<class String>
576 fail(String const& reason, char const* file, int line)
578 fail(detail::make_reason(reason, file, line));
586 if(abort_ && aborted_)
587 BOOST_THROW_EXCEPTION(abort_exception());
601 catch(abort_exception const&)
605 catch(std::exception const& e)
607 runner_->fail("unhandled exception: " +
608 std::string(e.what()));
612 runner_->fail("unhandled exception");
617 #define BEAST_PASS() ::boost::beast::unit_test::suite::this_suite()->pass()
621 #define BEAST_FAIL() ::boost::beast::unit_test::suite::this_suite()->fail("", __FILE__, __LINE__)
625 /** Check a precondition.
627 If the condition is false, the file and line number are reported.
629 #define BEAST_EXPECT(cond) ::boost::beast::unit_test::suite::this_suite()->expect(cond, __FILE__, __LINE__)
632 #ifndef BEAST_EXPECTS
633 /** Check a precondition.
635 If the condition is false, the file and line number are reported.
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))
642 /** Ensure an exception is thrown
644 #define BEAST_THROWS( EXPR, EXCEP ) \
649 catch(EXCEP const&) { \
660 //------------------------------------------------------------------------------
663 // This inserts the suite with the given manual flag
664 #define BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,manual) \
665 static ::boost::beast::unit_test::detail::insert_suite <Class##_test> \
666 Library ## Module ## Class ## _test_instance( \
667 #Class, #Module, #Library, manual)
669 //------------------------------------------------------------------------------
671 // Preprocessor directives for controlling unit test definitions.
673 // If this is already defined, don't redefine it. This allows
674 // programs to provide custom behavior for testsuite definitions
676 #ifndef BEAST_DEFINE_TESTSUITE
678 /** Enables insertion of test suites into the global container.
679 The default is to insert all test suite definitions into the global
680 container. If BEAST_DEFINE_TESTSUITE is user defined, this macro
683 #ifndef BEAST_NO_UNIT_TEST_INLINE
684 #define BEAST_NO_UNIT_TEST_INLINE 0
687 /** Define a unit test suite.
689 Library Identifies the library.
690 Module Identifies the module.
691 Class The type representing the class being tested.
693 The declaration for the class implementing the test should be the same
694 as Class ## _test. For example, if Class is aged_ordered_container, the
695 test class must be declared as:
699 struct aged_ordered_container_test : beast::unit_test::suite
706 The macro invocation must appear in the same namespace as the test class.
709 #if BEAST_NO_UNIT_TEST_INLINE
710 #define BEAST_DEFINE_TESTSUITE(Class,Module,Library)
713 #include <boost/beast/_experimental/unit_test/global_suites.hpp>
714 #define BEAST_DEFINE_TESTSUITE(Library,Module,Class) \
715 BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,false)
716 #define BEAST_DEFINE_TESTSUITE_MANUAL(Library,Module,Class) \
717 BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,true)
723 //------------------------------------------------------------------------------