diff --git a/beast/unit_test/reporter.h b/beast/unit_test/reporter.h index ea203ffe2..e9952058e 100644 --- a/beast/unit_test/reporter.h +++ b/beast/unit_test/reporter.h @@ -25,179 +25,307 @@ #include #include #include +#include +#include #include +#include #include +#include +#include +#include namespace beast { namespace unit_test { +namespace detail { + /** A simple test runner that writes everything to a stream in real time. The totals are output when the object is destroyed. */ +template class reporter : public runner { private: + using clock_type = std::chrono::steady_clock; + struct case_results { std::string name; - std::size_t total; - std::size_t failed; + std::size_t total = 0; + std::size_t failed = 0; - case_results (std::string const& name_ = "") - : name (name_) - , total (0) - , failed (0) - { - } + case_results (std::string const& name_ = ""); }; struct suite_results { std::string name; - std::size_t cases; - std::size_t total; - std::size_t failed; + std::size_t cases = 0; + std::size_t total = 0; + std::size_t failed = 0; + typename clock_type::time_point start = + clock_type::now(); explicit - suite_results (std::string const& name_ = "") - : name (name_) - , cases (0) - , total (0) - , failed (0) - { - } + suite_results (std::string const& name_ = ""); void - add (case_results const& r) - { - ++cases; - total += r.total; - failed += r.failed; - } + add (case_results const& r); }; struct results { - std::size_t suites; - std::size_t cases; - std::size_t total; - std::size_t failed; + using run_time = std::pair; - results() - : suites (0) - , cases (0) - , total (0) - , failed (0) + enum { - } + max_top = 10 + }; + + std::size_t suites = 0; + std::size_t cases = 0; + std::size_t total = 0; + std::size_t failed = 0; + std::vector top; + typename clock_type::time_point start = + clock_type::now(); void - add (suite_results const& r) - { - ++suites; - total += r.total; - cases += r.cases; - failed += r.failed; - } + add (suite_results const& r); }; - boost::optional m_std_ostream; - std::reference_wrapper m_stream; - results m_results; - suite_results m_suite; - case_results m_case; + boost::optional std_ostream_; + std::reference_wrapper stream_; + results results_; + suite_results suite_results_; + case_results case_results_; public: reporter (reporter const&) = delete; reporter& operator= (reporter const&) = delete; - explicit reporter (std::ostream& stream = std::cout) - : m_std_ostream (std::ref (stream)) - , m_stream (*m_std_ostream) - { - } - - ~reporter() - { - m_stream.get() << - amount (m_results.suites, "suite") << ", " << - amount (m_results.cases, "case") << ", " << - amount (m_results.total, "test") << " total, " << - amount (m_results.failed, "failure") - ; - } + ~reporter(); explicit - reporter (abstract_ostream& stream) - : m_stream (stream) - { - } + reporter (std::ostream& stream = std::cout); + + explicit + reporter (abstract_ostream& stream); private: - virtual - void - on_suite_begin (suite_info const& info) override - { - m_suite = suite_results (info.full_name()); - } + static + std::string + fmtdur (typename clock_type::duration const& d); virtual void - on_suite_end() override - { - m_results.add (m_suite); - } + on_suite_begin (suite_info const& info) override; virtual void - on_case_begin (std::string const& name) override - { - m_case = case_results (name); - - m_stream.get() << - m_suite.name << - (m_case.name.empty() ? - "" : (" " + m_case.name)) - ; - } + on_suite_end() override; virtual void - on_case_end() override - { - m_suite.add (m_case); - } + on_case_begin (std::string const& name) override; virtual void - on_pass() override - { - ++m_case.total; - } + on_case_end() override; virtual void - on_fail (std::string const& reason) override - { - ++m_case.failed; - ++m_case.total; - - m_stream.get() << - "#" << m_case.total << - " failed" << - (reason.empty() ? "" : ": ") << reason - ; - } + on_pass() override; virtual void - on_log (std::string const& s) override - { - m_stream.get() << - s; - } + on_fail (std::string const& reason) override; + + virtual + void + on_log (std::string const& s) override; }; +//------------------------------------------------------------------------------ + +template +reporter<_>::case_results::case_results ( + std::string const& name_) + : name (name_) +{ +} + +template +reporter<_>::suite_results::suite_results ( + std::string const& name_) + : name (name_) +{ +} + +template +void +reporter<_>::suite_results::add (case_results const& r) +{ + ++cases; + total += r.total; + failed += r.failed; +} + +template +void +reporter<_>::results::add ( + suite_results const& r) +{ + ++suites; + total += r.total; + cases += r.cases; + failed += r.failed; + + auto const elapsed = + clock_type::now() - r.start; + if (elapsed >= std::chrono::seconds(1)) + { + auto const iter = std::lower_bound(top.begin(), + top.end(), elapsed, + [](run_time const& t1, + typename clock_type::duration const& t2) + { + return t1.second > t2; + }); + if (iter != top.end()) + { + if (top.size() == max_top) + top.resize(top.size() - 1); + top.emplace(iter, r.name, elapsed); + } + else if (top.size() < max_top) + { + top.emplace_back(r.name, elapsed); + } + } +} + +//------------------------------------------------------------------------------ + +template +reporter<_>::reporter ( + std::ostream& stream) + : std_ostream_ (std::ref (stream)) + , stream_ (*std_ostream_) +{ +} + +template +reporter<_>::~reporter() +{ + if (results_.top.size() > 0) + { + stream_.get() << "Longest suite times:"; + for (auto const& i : results_.top) + stream_.get() << std::setw(8) << + fmtdur(i.second) << " " << i.first; + } + auto const elapsed = + clock_type::now() - results_.start; + stream_.get() << + fmtdur(elapsed) << ", " << + amount (results_.suites, "suite") << ", " << + amount (results_.cases, "case") << ", " << + amount (results_.total, "test") << " total, " << + amount (results_.failed, "failure"); +} + +template +reporter<_>::reporter ( + abstract_ostream& stream) + : stream_ (stream) +{ +} + +template +std::string +reporter<_>::fmtdur ( + typename clock_type::duration const& d) +{ + using namespace std::chrono; + auto const ms = + duration_cast(d); + if (ms < seconds(1)) + return std::to_string(ms.count()) + "ms"; + std::stringstream ss; + ss << std::fixed << std::setprecision(1) << + (ms.count()/1000.) << "s"; + return ss.str(); +} + +template +void +reporter<_>::on_suite_begin ( + suite_info const& info) +{ + suite_results_ = suite_results (info.full_name()); +} + +template +void +reporter<_>::on_suite_end() +{ + results_.add (suite_results_); +} + +template +void +reporter<_>::on_case_begin ( + std::string const& name) +{ + case_results_ = case_results (name); + + stream_.get() << + suite_results_.name << + (case_results_.name.empty() ? + "" : (" " + case_results_.name)); +} + +template +void +reporter<_>::on_case_end() +{ + suite_results_.add (case_results_); +} + +template +void +reporter<_>::on_pass() +{ + ++case_results_.total; +} + +template +void +reporter<_>::on_fail ( + std::string const& reason) +{ + ++case_results_.failed; + ++case_results_.total; + stream_.get() << + "#" << case_results_.total << + " failed" << + (reason.empty() ? "" : ": ") << reason; +} + +template +void +reporter<_>::on_log ( + std::string const& s) +{ + stream_.get() << s; +} + +} // detail + +using reporter = detail::reporter<>; + } // unit_test } // beast