diff --git a/beast/crypto/tests/BinaryEncoding.cpp b/beast/crypto/tests/BinaryEncoding.cpp index 74836d1b5..af85c98df 100644 --- a/beast/crypto/tests/BinaryEncoding.cpp +++ b/beast/crypto/tests/BinaryEncoding.cpp @@ -362,10 +362,14 @@ public: { typedef UnsignedInteger UInt; +#if 0 std::stringstream ss; ss << c.name() << " <" << Bytes << ">"; testcase (ss.str()); +#else + testcase << c.name() << " <" << Bytes << ">"; +#endif Random r; for (int i = 0; i < 50; ++i) diff --git a/beast/module/asio/tests/PeerTest.cpp b/beast/module/asio/tests/PeerTest.cpp index 54bc467c5..95b32fedb 100644 --- a/beast/module/asio/tests/PeerTest.cpp +++ b/beast/module/asio/tests/PeerTest.cpp @@ -97,7 +97,7 @@ bool PeerTest::Results::report (unit_test::suite& suite, bool beginTestCase) const { if (beginTestCase) - suite.testcase (name.toStdString()); + suite.testcase << name.toStdString(); bool success = true; if (! client.report (suite)) success = false; diff --git a/beast/unit_test/print.h b/beast/unit_test/print.h index db36c9f45..01d325d62 100644 --- a/beast/unit_test/print.h +++ b/beast/unit_test/print.h @@ -34,7 +34,7 @@ namespace unit_test { /** Write test results to the specified output stream. */ /** @{ */ -inline +template void print (results const& r, abstract_ostream& stream) { @@ -66,7 +66,7 @@ print (results const& r, abstract_ostream& stream) ; } -inline +template void print (results const& r, std::ostream& stream = std::cout) { diff --git a/beast/unit_test/recorder.h b/beast/unit_test/recorder.h index 153f8f17f..fd404faa6 100644 --- a/beast/unit_test/recorder.h +++ b/beast/unit_test/recorder.h @@ -35,15 +35,13 @@ private: case_results m_case; public: + recorder() = default; recorder (recorder const&) = default; recorder& operator= (recorder const&) = default; - recorder() - { - } - /** Returns a report with the results of all completed suites. */ - results const& report() const + results const& + report() const { return m_results; } diff --git a/beast/unit_test/reporter.h b/beast/unit_test/reporter.h index 0503f11a1..e28e88f5c 100644 --- a/beast/unit_test/reporter.h +++ b/beast/unit_test/reporter.h @@ -127,7 +127,8 @@ public: ; } - explicit reporter (abstract_ostream& stream) + explicit + reporter (abstract_ostream& stream) : m_stream (stream) { } diff --git a/beast/unit_test/runner.h b/beast/unit_test/runner.h index d4499f3e1..befd69978 100644 --- a/beast/unit_test/runner.h +++ b/beast/unit_test/runner.h @@ -41,59 +41,43 @@ private: class stream_t : public abstract_ostream { private: - runner& m_owner; + runner& owner_; public: stream_t() = delete; stream_t& operator= (stream_t const&) = delete; stream_t (runner& owner) - : m_owner (owner) + : owner_ (owner) { } void write (string_type const& s) { - m_owner.log (s); + owner_.log (s); } }; - stream_t m_stream; - bool m_default; - bool m_failed; - bool m_cond; + stream_t stream_; + bool default_; + bool failed_; + bool cond_; public: virtual ~runner() = default; runner (runner const&) = default; runner& operator= (runner const&) = default; - runner() - : m_stream (*this) - , m_default (false) - , m_failed (false) - , m_cond (false) - { - } + template + runner(); /** Run the specified suite. @return `true` if any conditions failed. */ + template bool - run (suite_info const& s) - { - // Enable 'default' testcase - m_default = true; - m_failed = false; - on_suite_begin (s); - s.run (*this); - // Forgot to call pass or fail. - assert (m_cond); - on_case_end(); - on_suite_end(); - return m_failed; - } + run (suite_info const& s); /** Run a sequence of suites. The expression @@ -103,13 +87,7 @@ public: */ template bool - run (FwdIter first, FwdIter last) - { - bool failed (false); - for (;first != last; ++first) - failed = run (*first) || failed; - return failed; - } + run (FwdIter first, FwdIter last); /** Conditionally run a sequence of suites. pred will be called as: @@ -120,27 +98,14 @@ public: */ template bool - run_if (FwdIter first, FwdIter last, Pred pred = Pred()) - { - bool failed (false); - for (;first != last; ++first) - if (pred (*first)) - failed = run (*first) || failed; - return failed; - } + run_if (FwdIter first, FwdIter last, Pred pred = Pred{}); /** Run all suites in a container. @return `true` if any conditions failed. */ template bool - run_each (SequenceContainer const& c) - { - bool failed (false); - for (auto const& s : c) - failed = run (s) || failed; - return failed; - } + run_each (SequenceContainer const& c); /** Conditionally run suites in a container. pred will be called as: @@ -151,14 +116,7 @@ public: */ template bool - run_each_if (SequenceContainer const& c, Pred pred = Pred()) - { - bool failed (false); - for (auto const& s : c) - if (pred (s)) - failed = run (s) || failed; - return failed; - } + run_each_if (SequenceContainer const& c, Pred pred = Pred{}); private: // @@ -220,52 +178,141 @@ private: abstract_ostream& stream() { - return m_stream; + return stream_; } // Start a new testcase. + template void - testcase (std::string const& name) - { - // Name may not be empty - assert (m_default || ! name.empty()); - // Forgot to call pass or fail - assert (m_default || m_cond); - if (! m_default) - on_case_end(); - m_default = false; - m_cond = false; - on_case_begin (name); - } + testcase (std::string const& name); + template void - pass() - { - if (m_default) - testcase (""); - on_pass(); - m_cond = true; - } + pass(); + template void - fail (std::string const& reason) - { - if (m_default) - testcase (""); - on_fail (reason); - m_failed = true; - m_cond = true; - } + fail (std::string const& reason); + template void - log (std::string const& s) - { - if (m_default) - testcase (""); - on_log (s); - } + log (std::string const& s); }; +//------------------------------------------------------------------------------ + +template +runner::runner() + : stream_ (*this) + , default_ (false) + , failed_ (false) + , cond_ (false) +{ +} + +template +bool +runner::run (suite_info const& s) +{ + // Enable 'default' testcase + default_ = true; + failed_ = false; + on_suite_begin (s); + s.run (*this); + // Forgot to call pass or fail. + assert (cond_); + on_case_end(); + on_suite_end(); + return failed_; +} + +template +bool +runner::run (FwdIter first, FwdIter last) +{ + bool failed (false); + for (;first != last; ++first) + failed = run (*first) || failed; + return failed; +} + +template +bool +runner::run_if (FwdIter first, FwdIter last, Pred pred) +{ + bool failed (false); + for (;first != last; ++first) + if (pred (*first)) + failed = run (*first) || failed; + return failed; +} + +template +bool +runner::run_each (SequenceContainer const& c) +{ + bool failed (false); + for (auto const& s : c) + failed = run (s) || failed; + return failed; +} + +template +bool +runner::run_each_if (SequenceContainer const& c, Pred pred) +{ + bool failed (false); + for (auto const& s : c) + if (pred (s)) + failed = run (s) || failed; + return failed; +} + +template +void +runner::testcase (std::string const& name) +{ + // Name may not be empty + assert (default_ || ! name.empty()); + // Forgot to call pass or fail + assert (default_ || cond_); + if (! default_) + on_case_end(); + default_ = false; + cond_ = false; + on_case_begin (name); +} + +template +void +runner::pass() +{ + if (default_) + testcase (""); + on_pass(); + cond_ = true; +} + +template +void +runner::fail (std::string const& reason) +{ + if (default_) + testcase (""); + on_fail (reason); + failed_ = true; + cond_ = true; +} + +template +void +runner::log (std::string const& s) +{ + if (default_) + testcase (""); + on_log (s); +} + } // unit_test } // beast diff --git a/beast/unit_test/suite.h b/beast/unit_test/suite.h index e905fb565..9254ba30f 100644 --- a/beast/unit_test/suite.h +++ b/beast/unit_test/suite.h @@ -24,6 +24,7 @@ #include #include +#include namespace beast { namespace unit_test { @@ -36,53 +37,83 @@ namespace unit_test { */ class suite { -private: - // 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 +public: + enum abort_t { - char const* - what() const noexcept override - { - return "suite aborted on failed condition"; - } + no_abort_on_fail, + abort_on_fail }; +private: + bool abort_ = false; + runner* runner_ = nullptr; + + struct abort_exception; + // Memberspace class log_t { private: friend class suite; - runner* m_runner; - - runner* - operator-> () - { - return m_runner; - } + suite* suite_ = nullptr; public: - log_t () - : m_runner (nullptr) - { - } + log_t () = default; template abstract_ostream::scoped_stream_type - operator<< (T const& t) - { - return m_runner->stream() << t; - } + operator<< (T const& t); /** Returns the raw stream used for output. */ abstract_ostream& - stream() - { - return m_runner->stream(); - } + stream(); }; - bool m_abort = false; + class scoped_testcase; + + // Memberspace + class testcase_t + { + private: + friend class suite; + suite* suite_ = nullptr; + std::stringstream ss_; + + public: + testcase_t() = default; + + /** Open a new testcase. + A testcase is a series of evaluated test conditions. A test suite + may have multiple test cases. A test is associated with the last + opened testcase. When the test first runs, a default unnamed + case is opened. Tests with only one case may omit the call + to testcase. + @param abort If `true`, the suite will be stopped on first failure. + */ + void + operator() (std::string const& name, + abort_t abort = no_abort_on_fail); + + /** Stream style composition of testcase names. */ + /** @{ */ + scoped_testcase + operator() (abort_t abort); + + template + scoped_testcase + operator<< (T const& t); + /** @} */ + }; + + // 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. @@ -113,6 +144,9 @@ public: /** Memberspace for logging. */ log_t log; + /** Memberspace for declaring test cases. */ + testcase_t testcase; + /** Invokes the test using the specified runner. Data members are set up here instead of the constructor as a convenience to writing the derived class to avoid repetition of @@ -120,50 +154,7 @@ public: Normally this is called by the framework for you. */ void - operator() (runner& r) - { - log.m_runner = &r; - - try - { - run(); - } - catch (abort_exception const&) - { - // ends the suite - } - catch (std::exception const& e) - { - fail (std::string ("unhandled exception: ") + - std::string (e.what())); - } - catch (...) - { - fail ("unhandled exception"); - } - } - - enum abort_t - { - no_abort_on_fail, - abort_on_fail - }; - - /** Open a new testcase. - A testcase is a series of evaluated test conditions. A test suite - may have multiple test cases. A test is associated with the last - opened testcase. When the test first runs, a default unnamed - case is opened. Tests with only one case may omit the call - to testcase. - @param abort If `true`, the suite will be stopped on first failure. - */ - void - testcase (std::string const& name, - abort_t abort = no_abort_on_fail) - { - m_abort = abort == abort_on_fail; - log->testcase (name); - } + operator() (runner& r); /** Evaluate a test condition. The condition is passed as a template argument instead of `bool` so @@ -173,43 +164,24 @@ public: */ template bool - expect (Condition shouldBeTrue, std::string const& reason = "") - { - if (shouldBeTrue) - pass(); - else - fail (reason); - return shouldBeTrue; - } + expect (Condition shouldBeTrue, std::string const& reason = ""); // DEPRECATED // @return `true` if the test condition indicates success (a false value) template bool - unexpected (Condition shouldBeFalse, std::string const& reason = "") - { - if (! shouldBeFalse) - pass(); - else - fail (reason); - return ! shouldBeFalse; - } + unexpected (Condition shouldBeFalse, std::string const& reason = ""); /** Record a successful test condition. */ void pass() { - log->pass(); + runner_->pass(); } /** Record a failure. */ void - fail (std::string const& reason= "") - { - log->fail (reason); - if (m_abort) - throw abort_exception(); - } + fail (std::string const& reason = ""); private: /** Runs the suite. */ @@ -218,6 +190,206 @@ private: run() = 0; }; +//------------------------------------------------------------------------------ + +// 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 +suite::log_t::operator<< (T const& t) +{ + return suite_->runner_->stream() << t; +} + +/** Returns the raw stream used for output. */ +inline +abstract_ostream& +suite::log_t::stream() +{ + return suite_->runner_->stream(); +} + +//------------------------------------------------------------------------------ + +// Helper for streaming testcase names +class suite::scoped_testcase +{ +private: + suite* suite_; + std::stringstream* ss_; + +public: + ~scoped_testcase(); + + scoped_testcase (suite* s, std::stringstream* ss); + + template + scoped_testcase (suite* s, std::stringstream* ss, T const& t); + + scoped_testcase& operator= (scoped_testcase const&) = delete; + + template + scoped_testcase& + operator<< (T const& t); +}; + +inline +suite::scoped_testcase::~scoped_testcase() +{ + auto const& name (ss_->str()); + if (! name.empty()) + suite_->runner_->testcase (name); +} + +inline +suite::scoped_testcase::scoped_testcase (suite* s, std::stringstream* ss) + : suite_ (s) + , ss_ (ss) +{ + ss_->clear(); + ss_->str({}); + +} + +template +inline +suite::scoped_testcase::scoped_testcase (suite* s, std::stringstream* ss, T const& t) + : suite_ (s) + , ss_ (ss) +{ + ss_->clear(); + ss_->str({}); + *ss_ << t; +} + +template +inline +suite::scoped_testcase& +suite::scoped_testcase::operator<< (T const& t) +{ + *ss_ << t; + return *this; +} + +//------------------------------------------------------------------------------ + +inline +void +suite::testcase_t::operator() (std::string const& name, + abort_t abort) +{ + suite_->abort_ = abort == abort_on_fail; + suite_->runner_->testcase (name); +} + +inline +suite::scoped_testcase +suite::testcase_t::operator() (abort_t abort) +{ + suite_->abort_ = abort == abort_on_fail; + return { suite_, &ss_ }; +} + +template +inline +suite::scoped_testcase +suite::testcase_t::operator<< (T const& t) +{ + return { suite_, &ss_, t }; +} + +//------------------------------------------------------------------------------ + +template +void +suite::run (runner& r) +{ + runner_ = &r; + log.suite_ = this; + testcase.suite_ = this; + + try + { + run(); + } + catch (abort_exception const&) + { + // ends the suite + } + catch (std::exception const& e) + { + fail (std::string ("unhandled exception: ") + + std::string (e.what())); + } + catch (...) + { + 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/suite_info.h b/beast/unit_test/suite_info.h index ca86236f7..e49582d71 100644 --- a/beast/unit_test/suite_info.h +++ b/beast/unit_test/suite_info.h @@ -42,19 +42,9 @@ private: run_type m_run; public: - suite_info ( - char const* name, - char const* module, - char const* library, - bool manual, - run_type run) - : m_name (name) - , m_module (module) - , m_library (library) - , m_manual (manual) - , m_run (std::move (run)) - { - } + template + suite_info (char const* name, char const* module, char const* library, + bool manual, run_type run); char const* name() const @@ -82,14 +72,9 @@ public: } /** Return the canonical suite name as a string. */ + template std::string - full_name() const - { - return - std::string (m_library) + "." + - std::string (m_module) + "." + - std::string (m_name); - } + full_name() const; /** Run a new instance of the associated test suite. */ void @@ -99,6 +84,33 @@ public: } }; +//------------------------------------------------------------------------------ + +template +suite_info::suite_info ( + char const* name, + char const* module, + char const* library, + bool manual, + run_type run) + : m_name (name) + , m_module (module) + , m_library (library) + , m_manual (manual) + , m_run (std::move (run)) +{ +} + +template +std::string +suite_info::full_name() const +{ + return + std::string (m_library) + "." + + std::string (m_module) + "." + + std::string (m_name); +} + inline bool operator< (suite_info const& lhs, suite_info const& rhs) @@ -108,8 +120,9 @@ operator< (suite_info const& lhs, suite_info const& rhs) /** Convenience for producing suite_info for a given test type. */ template -suite_info make_suite_info (char const* name, char const* module, - char const* library, bool manual) +suite_info +make_suite_info (char const* name, char const* module, char const* library, + bool manual) { return suite_info (name, module, library, manual, [](runner& r) diff --git a/beast/unit_test/suite_list.h b/beast/unit_test/suite_list.h index 6194e93fa..5efb9ee5c 100644 --- a/beast/unit_test/suite_list.h +++ b/beast/unit_test/suite_list.h @@ -35,15 +35,12 @@ namespace unit_test { /** A container of test suites. */ class suite_list - : public const_container < - std::set - //std::list - > + : public const_container > { private: #ifndef NDEBUG - std::unordered_set m_names; - std::unordered_set m_classes; + std::unordered_set names_; + std::unordered_set classes_; #endif public: @@ -52,28 +49,34 @@ public: */ template void - insert (char const* name, - char const* module, char const* library, - bool manual) - { - #ifndef NDEBUG - { - auto const result (m_names.insert (name)); - assert (result.second); // Duplicate name - } - - { - auto const result (m_classes.insert ( - std::type_index (typeid(Suite)))); - assert (result.second); // Duplicate type - } - #endif - - cont().emplace (std::move (make_suite_info ( - name, module, library, manual))); - } + insert (char const* name, char const* module, char const* library, + bool manual); }; +//------------------------------------------------------------------------------ + +template +void +suite_list::insert (char const* name, char const* module, char const* library, + bool manual) +{ +#ifndef NDEBUG + { + auto const result (names_.insert (name)); + assert (result.second); // Duplicate name + } + + { + auto const result (classes_.insert ( + std::type_index (typeid(Suite)))); + assert (result.second); // Duplicate type + } +#endif + + cont().emplace (std::move (make_suite_info ( + name, module, library, manual))); +} + } // unit_test } // beast