#include #include #include #include #include #include #include #include #include namespace aethera::web::detail { TEST(Gallery_Frame_Clock, Produces_One_Monotonic_Absolute_Timeline) { asio::thread_pool pool(1); std::mutex mutex; std::condition_variable condition; std::vector ticks; std::exception_ptr failure; { Gallery_Frame_Clock clock( pool.get_executor(), 100.0, [&](Plot_Render_Tick tick) { std::lock_guard lock(mutex); ticks.push_back(tick); condition.notify_all(); }, [&](std::exception_ptr value) { std::lock_guard lock(mutex); failure = std::move(value); condition.notify_all(); }); clock.start(); clock.start(); { std::unique_lock lock(mutex); ASSERT_TRUE(condition.wait_for(lock, std::chrono::seconds(2), [&] { return ticks.size() >= 8 || failure; })); ASSERT_FALSE(failure); ASSERT_GE(ticks.size(), 8U); for (std::size_t index = 1; index < ticks.size(); ++index) { EXPECT_GT(ticks[index].sequence, ticks[index - 1].sequence); EXPECT_GT(ticks[index].time_milliseconds, ticks[index - 1].time_milliseconds); } EXPECT_NEAR(ticks.front().time_milliseconds, static_cast(ticks.front().sequence) * 10.0, 0.001); } clock.stop(); std::this_thread::sleep_for(std::chrono::milliseconds(30)); std::size_t stopped_count{}; { std::lock_guard lock(mutex); stopped_count = ticks.size(); } std::this_thread::sleep_for(std::chrono::milliseconds(30)); { std::lock_guard lock(mutex); EXPECT_EQ(ticks.size(), stopped_count); } } pool.stop(); pool.join(); } }