blob: 96b3afc87ccc64474d80bf5f706f771e282fc2a2 [file] [log] [blame] [edit]
#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