Files
rippled/include/xrpl/beast/unit_test/reporter.h
Bart 1d42c4f6de refactor: Remove unnecessary copyright notices already covered by LICENSE.md (#5929)
Per XLS-0095, we are taking steps to rename ripple(d) to xrpl(d).

This change specifically removes all copyright notices referencing Ripple, XRPLF, and certain affiliated contributors upon mutual agreement, so the notice in the LICENSE.md file applies throughout. Copyright notices referencing external contributions remain as-is. Duplicate verbiage is also removed.
2025-11-04 08:33:42 +00:00

261 lines
5.7 KiB
C++

// 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 <xrpl/beast/unit_test/amount.h>
#include <xrpl/beast/unit_test/recorder.h>
#include <boost/lexical_cast.hpp>
#include <boost/optional.hpp>
#include <algorithm>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
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 = void>
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<std::string, typename clock_type::duration>;
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<run_time> 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 <class _>
void
reporter<_>::suite_results::add(case_results const& r)
{
++cases;
total += r.total;
failed += r.failed;
}
template <class _>
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 <class _>
reporter<_>::reporter(std::ostream& os) : os_(os)
{
}
template <class _>
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 <class _>
std::string
reporter<_>::fmtdur(typename clock_type::duration const& d)
{
using namespace std::chrono;
auto const ms = duration_cast<milliseconds>(d);
if (ms < seconds{1})
return boost::lexical_cast<std::string>(ms.count()) + "ms";
std::stringstream ss;
ss << std::fixed << std::setprecision(1) << (ms.count() / 1000.) << "s";
return ss.str();
}
template <class _>
void
reporter<_>::on_suite_begin(suite_info const& info)
{
suite_results_ = suite_results{info.full_name()};
}
template <class _>
void
reporter<_>::on_suite_end()
{
results_.add(suite_results_);
}
template <class _>
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 <class _>
void
reporter<_>::on_case_end()
{
suite_results_.add(case_results_);
}
template <class _>
void
reporter<_>::on_pass()
{
++case_results_.total;
}
template <class _>
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 <class _>
void
reporter<_>::on_log(std::string const& s)
{
os_ << s;
}
} // namespace detail
using reporter = detail::reporter<>;
} // namespace unit_test
} // namespace beast
#endif