// Copyright 2022, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 /*! * @file * @brief Utilities for tests involving time points and durations * @author Ryan Pavlik */ #pragma once #include #include #include #include #include using unanoseconds = std::chrono::duration; template static inline std::string stringifyNanos(std::chrono::duration value) { using namespace std::chrono; std::ostringstream oss; auto sec = duration_cast>(value); if (duration(sec) == value) { oss << sec.count() << "s"; return oss.str(); } auto millis = duration_cast>(value); if (duration(millis) == value) { oss << millis.count() << "ms"; return oss.str(); } auto micros = duration_cast>(value); if (duration(micros) == value) { oss << micros.count() << "us"; return oss.str(); } oss << value.count() << "ns"; return oss.str(); } static inline std::string stringifyTimePoint(std::chrono::steady_clock::time_point tp) { auto dur = tp.time_since_epoch(); auto hr = std::chrono::duration_cast(dur); dur -= hr; auto sec = std::chrono::duration_cast(dur); dur -= sec; std::ostringstream oss; if (hr.count() > 0) { oss << hr.count() << ":"; } oss << sec.count() << "."; using three_commas = std::ratio_multiply, std::ratio_multiply, std::ratio<1, 1000>>>; static_assert(std::ratio_equal::value); // 9 because of the preceding static assert: there's no compile-time rational log10? :-O oss << std::setfill('0') << std::setw(9); oss << dur.count(); return oss.str(); } namespace Catch { template <> struct StringMaker { static std::string convert(unanoseconds const &value) { return stringifyNanos(value); } }; template <> struct StringMaker { static std::string convert(std::chrono::nanoseconds const &value) { return stringifyNanos(value); } }; template <> struct StringMaker { static std::string convert(std::chrono::steady_clock::time_point const &value) { return stringifyTimePoint(value); } }; } // namespace Catch class MockClock { public: uint64_t now() const noexcept { return std::chrono::duration_cast(now_.time_since_epoch()).count(); } std::chrono::steady_clock::time_point now_typed() const noexcept { return now_; } void advance(unanoseconds ns) { now_ += ns; } void advance_to(uint64_t timestamp_ns) { CHECK(now() <= timestamp_ns); now_ = std::chrono::steady_clock::time_point( std::chrono::steady_clock::duration(unanoseconds(timestamp_ns))); } private: std::chrono::steady_clock::time_point now_{std::chrono::steady_clock::duration(std::chrono::seconds(1000000))}; }; struct FutureEvent { std::chrono::steady_clock::time_point time_point; std::function action; };