// // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BEAST_UNIT_TEST_REPORTER_HPP #define BEAST_UNIT_TEST_REPORTER_HPP #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 = 0; std::size_t failed = 0; explicit case_results(std::string name_ = "") : name(std::move(name_)) { } }; struct suite_results { std::string name; 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 name_ = "") : name(std::move(name_)) { } void add(case_results const& r); }; struct results { using run_time = std::pair; 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); }; std::ostream& os_; results results_; suite_results suite_results_; case_results case_results_; public: reporter(reporter const&) = delete; reporter& operator=(reporter const&) = delete; ~reporter(); explicit reporter(std::ostream& os = std::cout); private: static std::string fmtdur(typename clock_type::duration const& d); virtual void on_suite_begin(suite_info const& info) override; virtual void on_suite_end() override; virtual void on_case_begin(std::string const& name) override; virtual void on_case_end() override; virtual void on_pass() override; virtual void on_fail(std::string const& reason) override; virtual void on_log(std::string const& s) override; }; //------------------------------------------------------------------------------ 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& os) : os_(os) { } template reporter<_>::~reporter() { if (results_.top.size() > 0) { os_ << "Longest suite times:\n"; for (auto const& i : results_.top) os_ << std::setw(8) << fmtdur(i.second) << " " << i.first << '\n'; } auto const elapsed = clock_type::now() - results_.start; os_ << fmtdur(elapsed) << ", " << amount{results_.suites, "suite"} << ", " << amount{results_.cases, "case"} << ", " << amount{results_.total, "test"} << " total, " << amount{results_.failed, "failure"} << std::endl; } 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 boost::lexical_cast(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); os_ << suite_results_.name << (case_results_.name.empty() ? "" : (" " + case_results_.name)) << std::endl; } 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; os_ << "#" << case_results_.total << " failed" << (reason.empty() ? "" : ": ") << reason << std::endl; } template void reporter<_>::on_log(std::string const& s) { os_ << s; } } // namespace detail using reporter = detail::reporter<>; } // namespace unit_test } // namespace beast #endif