| #include "utils/timer.h" |
| |
| #include <memory> |
| |
| #include "gmock.h" |
| #include "gunit.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/synchronization/notification.h" |
| #include "absl/time/clock.h" |
| #include "absl/time/time.h" |
| #include "thread/thread.h" |
| #include "thread/thread_options.h" |
| #include "utils/clock.h" |
| |
| namespace milotic { |
| namespace { |
| |
| using ::testing::Test; |
| |
| int call_count; |
| |
| class TimerTest : public Test { |
| protected: |
| util::SimulatedClock sim_clock_; |
| void SetUp() override { |
| call_count = 0; |
| sim_clock_.SetTime(absl::Now()); |
| } |
| |
| void AdvanceTime(absl::Duration duration) { |
| sim_clock_.AdvanceTime(duration); |
| absl::SleepFor(absl::Seconds(0.05)); |
| } |
| }; |
| |
| void TestCallback() { |
| call_count++; |
| LOG(INFO) << "TestCallback"; |
| } |
| |
| TEST_F(TimerTest, SimpleCallback) { |
| Timer test_timer = Timer(&TestCallback, "TestTimer", &sim_clock_); |
| |
| EXPECT_OK(test_timer.Start(absl::Seconds(1))); |
| EXPECT_TRUE(test_timer.IsRunning()); |
| |
| AdvanceTime(absl::Seconds(1)); |
| |
| EXPECT_TRUE(test_timer.IsCallbackDone()); |
| EXPECT_EQ(call_count, 1); |
| |
| test_timer.End(); |
| } |
| |
| TEST_F(TimerTest, SingleRestart) { |
| Timer test_timer = Timer(&TestCallback, "TestTimer", &sim_clock_); |
| |
| EXPECT_OK(test_timer.Start(absl::Seconds(2))); |
| EXPECT_TRUE(test_timer.IsRunning()); |
| EXPECT_FALSE(test_timer.IsCallbackDone()); |
| |
| EXPECT_OK(test_timer.Restart(absl::Seconds(1))); |
| |
| AdvanceTime(absl::Seconds(1)); |
| |
| EXPECT_TRUE(test_timer.IsCallbackDone()); |
| EXPECT_EQ(call_count, 1); |
| |
| test_timer.End(); |
| } |
| |
| TEST_F(TimerTest, MultipleStart) { |
| Timer test_timer = Timer(&TestCallback, "TestTimer", &sim_clock_); |
| |
| EXPECT_OK(test_timer.Start(absl::Seconds(1))); |
| |
| EXPECT_THAT(test_timer.Start(absl::Seconds(1)), |
| testing::status::StatusIs(absl::StatusCode::kFailedPrecondition, |
| "Timer already started/ended")); |
| |
| AdvanceTime(absl::Seconds(1)); |
| |
| EXPECT_TRUE(test_timer.IsCallbackDone()); |
| EXPECT_EQ(call_count, 1); |
| |
| test_timer.End(); |
| } |
| |
| TEST_F(TimerTest, RestartAfterCallback) { |
| Timer test_timer = Timer(&TestCallback, "TestTimer", &sim_clock_); |
| |
| EXPECT_OK(test_timer.Start(absl::Seconds(1))); |
| EXPECT_TRUE(test_timer.IsRunning()); |
| EXPECT_FALSE(test_timer.IsCallbackDone()); |
| |
| AdvanceTime(absl::Seconds(1)); |
| |
| EXPECT_TRUE(test_timer.IsCallbackDone()); |
| EXPECT_EQ(call_count, 1); |
| |
| EXPECT_THAT(test_timer.Restart(absl::Seconds(1)), |
| testing::status::StatusIs(absl::StatusCode::kFailedPrecondition, |
| "Callback already done")); |
| |
| test_timer.End(); |
| } |
| |
| TEST_F(TimerTest, RestartAfterForceEnd) { |
| Timer test_timer = Timer(&TestCallback, "TestTimer", &sim_clock_); |
| |
| EXPECT_OK(test_timer.Start(absl::Seconds(1))); |
| EXPECT_TRUE(test_timer.IsRunning()); |
| EXPECT_FALSE(test_timer.IsCallbackDone()); |
| |
| test_timer.End(); |
| |
| EXPECT_THAT(test_timer.Restart(absl::Seconds(1)), |
| testing::status::StatusIs(absl::StatusCode::kFailedPrecondition, |
| "Timer already ended")); |
| } |
| |
| TEST_F(TimerTest, RestartBeforeStart) { |
| Timer test_timer = Timer(&TestCallback, "TestTimer", &sim_clock_); |
| |
| EXPECT_THAT(test_timer.Restart(absl::Seconds(1)), |
| testing::status::StatusIs(absl::StatusCode::kFailedPrecondition, |
| "Timer has not started")); |
| } |
| |
| TEST_F(TimerTest, EndBeforeCallback) { |
| Timer test_timer = Timer(&TestCallback, "TestTimer", &sim_clock_); |
| |
| EXPECT_OK(test_timer.Start(absl::Seconds(1))); |
| EXPECT_TRUE(test_timer.IsRunning()); |
| EXPECT_FALSE(test_timer.IsCallbackDone()); |
| |
| AdvanceTime(absl::Seconds(0.5)); |
| |
| test_timer.End(); |
| EXPECT_FALSE(test_timer.IsCallbackDone()); |
| EXPECT_EQ(call_count, 0); |
| } |
| |
| // To test a scenario when End takes the lock before Tick but after Start waits |
| // for kTicking state |
| TEST_F(TimerTest, EndBeforeTicking) { |
| Timer test_timer = Timer(&TestCallback, "TestTimer", &sim_clock_); |
| absl::Notification ticker_notify; |
| |
| auto ticker = [&ticker_notify]() { |
| LOG(INFO) << "TestTicker"; |
| ticker_notify.Notify(); |
| }; |
| |
| auto timer_ender = [&test_timer, &ticker_notify]() { |
| LOG(INFO) << "TestTimerEnder"; |
| ticker_notify.WaitForNotification(); |
| test_timer.End(); |
| }; |
| |
| auto end_thread = std::make_unique<ClosureThread>( |
| thread::Options().set_joinable(true), "EndThread", timer_ender); |
| end_thread->Start(); |
| EXPECT_OK(test_timer.Start(absl::Seconds(1), ticker)); |
| end_thread->Join(); |
| EXPECT_FALSE(test_timer.IsRunning()); |
| EXPECT_FALSE(test_timer.IsCallbackDone()); |
| EXPECT_EQ(call_count, 0); |
| } |
| |
| TEST_F(TimerTest, ResetAfterSimpleCallback) { |
| Timer test_timer = Timer(&TestCallback, "TestTimer", &sim_clock_); |
| |
| EXPECT_OK(test_timer.Start(absl::Seconds(1))); |
| EXPECT_TRUE(test_timer.IsRunning()); |
| |
| AdvanceTime(absl::Seconds(1)); |
| |
| EXPECT_TRUE(test_timer.IsCallbackDone()); |
| EXPECT_EQ(call_count, 1); |
| |
| test_timer.End(); |
| |
| test_timer.Reset(); |
| EXPECT_FALSE(test_timer.IsCallbackDone()); |
| |
| EXPECT_OK(test_timer.Start(absl::Seconds(1))); |
| EXPECT_TRUE(test_timer.IsRunning()); |
| |
| AdvanceTime(absl::Seconds(1)); |
| |
| EXPECT_TRUE(test_timer.IsCallbackDone()); |
| } |
| |
| TEST_F(TimerTest, ResetBeforeCallback) { |
| Timer test_timer = Timer(&TestCallback, "TestTimer", &sim_clock_); |
| |
| EXPECT_OK(test_timer.Start(absl::Seconds(1))); |
| EXPECT_TRUE(test_timer.IsRunning()); |
| |
| AdvanceTime(absl::Seconds(0.5)); |
| |
| test_timer.Reset(); |
| EXPECT_FALSE(test_timer.IsCallbackDone()); |
| |
| EXPECT_OK(test_timer.Start(absl::Seconds(1))); |
| EXPECT_TRUE(test_timer.IsRunning()); |
| |
| AdvanceTime(absl::Seconds(1)); |
| |
| EXPECT_TRUE(test_timer.IsCallbackDone()); |
| } |
| |
| TEST_F(TimerTest, RestartAfterReset) { |
| Timer test_timer = Timer(&TestCallback, "TestTimer", &sim_clock_); |
| |
| EXPECT_OK(test_timer.Start(absl::Seconds(1))); |
| EXPECT_TRUE(test_timer.IsRunning()); |
| |
| AdvanceTime(absl::Seconds(1)); |
| |
| EXPECT_TRUE(test_timer.IsCallbackDone()); |
| EXPECT_EQ(call_count, 1); |
| |
| test_timer.End(); |
| |
| test_timer.Reset(); |
| EXPECT_THAT(test_timer.Restart(absl::Seconds(1)), |
| testing::status::StatusIs(absl::StatusCode::kFailedPrecondition, |
| "Timer has not started")); |
| } |
| |
| } // namespace |
| } // namespace milotic |