diff --git a/beast/unit_test/suite.h b/beast/unit_test/suite.h index b9c4f7d657..2e70df7af3 100644 --- a/beast/unit_test/suite.h +++ b/beast/unit_test/suite.h @@ -29,6 +29,8 @@ namespace beast { namespace unit_test { +class thread; + /** A testsuite class. Derived classes execute a series of testcases, where each testcase is a series of pass/fail tests. To provide a unit test using this class, @@ -46,9 +48,19 @@ public: private: bool abort_ = false; + bool aborted_ = false; runner* runner_ = nullptr; - struct abort_exception; + // This exception is thrown internally to stop the current suite + // in the event of a failure, if the option to stop is set. + struct abort_exception : public std::exception + { + char const* + what() const noexcept override + { + return "suite aborted"; + } + }; // Memberspace class log_t @@ -105,16 +117,6 @@ private: /** @} */ }; - // Hacks to make this header-only - - template - void - run (runner& r); - - template - void - do_fail (std::string const& reason); - public: /** Type for scoped stream logging. To use this type, declare a local variable of the type @@ -180,42 +182,34 @@ public: unexpected (Condition shouldBeFalse, std::string const& reason = ""); /** Record a successful test condition. */ + template void - pass() - { - runner_->pass(); - } + pass(); /** Record a failure. */ + template void fail (std::string const& reason = ""); private: + friend class thread; + /** Runs the suite. */ virtual void run() = 0; + + template + void + propagate_abort(); + + template + void + run (runner& r); }; //------------------------------------------------------------------------------ -// This exception is thrown internally to stop the current suite -// in the event of a failure, if the option to stop is set. -struct suite::abort_exception : public std::exception -{ - char const* - what() const noexcept override; -}; - -inline -char const* -suite::abort_exception::what() const noexcept -{ - return "suite aborted on failed condition"; -} - -//------------------------------------------------------------------------------ - template inline abstract_ostream::scoped_stream_type @@ -323,6 +317,66 @@ suite::testcase_t::operator<< (T const& t) //------------------------------------------------------------------------------ +inline +void +suite::operator() (runner& r) +{ + run (r); +} + +template +inline +bool +suite::expect (Condition shouldBeTrue, std::string const& reason) +{ + if (shouldBeTrue) + pass(); + else + fail (reason); + return shouldBeTrue; +} + +template +inline +bool +suite::unexpected (Condition shouldBeFalse, std::string const& reason) +{ + if (! shouldBeFalse) + pass(); + else + fail (reason); + return ! shouldBeFalse; +} + +template +void +suite::pass() +{ + propagate_abort(); + runner_->pass(); +} + +template +void +suite::fail (std::string const& reason) +{ + propagate_abort(); + runner_->fail (reason); + if (abort_) + { + aborted_ = true; + throw abort_exception(); + } +} + +template +void +suite::propagate_abort() +{ + if (abort_ && aborted_) + throw abort_exception(); +} + template void suite::run (runner& r) @@ -341,62 +395,15 @@ suite::run (runner& r) } catch (std::exception const& e) { - fail (std::string ("unhandled exception: ") + + runner_->fail ("unhandled exception: " + std::string (e.what())); } catch (...) { - fail ("unhandled exception"); + runner_->fail ("unhandled exception"); } } -inline -void -suite::operator() (runner& r) -{ - run (r); -} - -template -inline -bool -suite::expect (Condition shouldBeTrue, std::string const& reason) -{ - if (shouldBeTrue) - pass(); - else - do_fail (reason); - return shouldBeTrue; -} - -template -inline -bool -suite::unexpected (Condition shouldBeFalse, std::string const& reason) -{ - if (! shouldBeFalse) - pass(); - else - do_fail (reason); - return ! shouldBeFalse; -} - -template -void -suite::do_fail (std::string const& reason) -{ - runner_->fail (reason); - if (abort_) - throw abort_exception(); -} - -inline -void -suite::fail (std::string const& reason) -{ - do_fail (reason); -} - } // unit_test } // beast diff --git a/beast/unit_test/thread.h b/beast/unit_test/thread.h new file mode 100644 index 0000000000..2f025c0f0b --- /dev/null +++ b/beast/unit_test/thread.h @@ -0,0 +1,122 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_UNIT_TEST_THREAD_H_INCLUDED +#define BEAST_UNIT_TEST_THREAD_H_INCLUDED + +#include +#include +#include +#include +#include + +namespace beast { +namespace unit_test { + +/** Replacement for std::thread that handles exceptions in unit tests. */ +class thread +{ +private: + suite* s_ = nullptr; + std::thread t_; + +public: + using id = std::thread::id; + using native_handle_type = std::thread::native_handle_type; + + thread() = default; + thread (thread const&) = delete; + thread& operator= (thread const&) = delete; + + thread (thread&& other) + : s_ (other.s_) + , t_ (std::move(other.t_)) + { + } + + thread& operator= (thread&& other) + { + s_ = other.s_; + t_ = std::move(other.t_); + return *this; + } + + template + explicit + thread (suite& s, F&& f, Args&&... args) + : s_ (&s) + { + std::function b = + std::bind(std::forward(f), + std::forward(args)...); + t_ = std::thread (&thread::run, this, + std::move(b)); + } + + bool + joinable() const + { + return t_.joinable(); + } + + std::thread::id + get_id() const + { + return t_.get_id(); + } + + static + unsigned + hardware_concurrency() noexcept + { + return std::thread::hardware_concurrency(); + } + + void + join() + { + t_.join(); + s_->propagate_abort(); + } + + void + swap (thread& other) + { + std::swap(s_, other.s_); + std::swap(t_, other.t_); + } + +private: + void + run (std::function f) + { + try + { + f(); + } + catch (suite::abort_exception const&) + { + } + } +}; + +} // unit_test +} // beast + +#endif