Commit 5eda9bf0 authored by Owen Min's avatar Owen Min Committed by Commit Bot

Improves WallClockTimer

1) Reorder the function in .cpp to match the header file.
2) Add a unit test for Stop() function.
3) When time is up, remove the observer before running the |user_task|.
   This allows user restart the timer inside the |user_task|.

Bug: 1062410
Change-Id: I6472c97198d63f4745a6ee5f2af7f376c8555894
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2140715Reviewed-by: default avatarGreg Thompson <grt@chromium.org>
Commit-Queue: Owen Min <zmin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#757829}
parent 489321c7
...@@ -35,10 +35,6 @@ void WallClockTimer::Start(const base::Location& posted_from, ...@@ -35,10 +35,6 @@ void WallClockTimer::Start(const base::Location& posted_from,
&WallClockTimer::RunUserTask); &WallClockTimer::RunUserTask);
} }
base::Time WallClockTimer::Now() const {
return clock_->Now();
}
void WallClockTimer::Stop() { void WallClockTimer::Stop() {
timer_.Stop(); timer_.Stop();
user_task_.Reset(); user_task_.Reset();
...@@ -49,14 +45,6 @@ bool WallClockTimer::IsRunning() const { ...@@ -49,14 +45,6 @@ bool WallClockTimer::IsRunning() const {
return timer_.IsRunning(); return timer_.IsRunning();
} }
void WallClockTimer::RunUserTask() {
DCHECK(user_task_);
std::exchange(user_task_, {}).Run();
// TODO(crbug.com/1062410): Remove observer before running |task| so that
//|task| is able to call Start().
RemoveObserver();
}
void WallClockTimer::OnResume() { void WallClockTimer::OnResume() {
// This will actually restart timer with smaller delay // This will actually restart timer with smaller delay
timer_.Start(posted_from_, desired_run_time_ - Now(), this, timer_.Start(posted_from_, desired_run_time_ - Now(), this,
...@@ -75,4 +63,14 @@ void WallClockTimer::RemoveObserver() { ...@@ -75,4 +63,14 @@ void WallClockTimer::RemoveObserver() {
} }
} }
void WallClockTimer::RunUserTask() {
DCHECK(user_task_);
RemoveObserver();
std::exchange(user_task_, {}).Run();
}
base::Time WallClockTimer::Now() const {
return clock_->Now();
}
} // namespace util } // namespace util
...@@ -49,15 +49,15 @@ class WallClockTimer : public base::PowerObserver { ...@@ -49,15 +49,15 @@ class WallClockTimer : public base::PowerObserver {
~WallClockTimer() override; ~WallClockTimer() override;
// Start the timer to run at the given |delay| from now. If the timer is // Starts the timer to run at the given |desired_run_time|. If the timer is
// already running, it will be replaced to call the given |user_task|. // already running, it will be replaced to call the given |user_task|.
virtual void Start(const base::Location& posted_from, virtual void Start(const base::Location& posted_from,
base::Time desired_run_time, base::Time desired_run_time,
base::OnceClosure user_task); base::OnceClosure user_task);
// Start the timer to run at the given |delay| from now. If the timer is // Starts the timer to run at the given |desired_run_time|. If the timer is
// already running, it will be replaced to call a task formed from // already running, it will be replaced to call a task formed from
// |reviewer->*method|. // |receiver|->*|method|.
template <class Receiver> template <class Receiver>
void Start(const base::Location& posted_from, void Start(const base::Location& posted_from,
base::Time desired_run_time, base::Time desired_run_time,
...@@ -67,8 +67,7 @@ class WallClockTimer : public base::PowerObserver { ...@@ -67,8 +67,7 @@ class WallClockTimer : public base::PowerObserver {
base::BindOnce(method, base::Unretained(receiver))); base::BindOnce(method, base::Unretained(receiver)));
} }
// Call this method to stop and cancle the timer. It is a no-op if the timer // Stops the timer. It is a no-op if the timer is not running.
// is not running.
void Stop(); void Stop();
// Returns true if the timer is running. // Returns true if the timer is running.
......
...@@ -43,30 +43,47 @@ class WallClockTimerTest : public ::testing::Test { ...@@ -43,30 +43,47 @@ class WallClockTimerTest : public ::testing::Test {
~WallClockTimerTest() override { base::PowerMonitor::ShutdownForTesting(); } ~WallClockTimerTest() override { base::PowerMonitor::ShutdownForTesting(); }
// Fast-forwards virtual time by |delta|. If |with_power| is true, both
// |clock_| and |task_environment_| time will be fast-forwarded. Otherwise,
// only |clock_| time will be changed to mimic the behavior when machine is
// suspended.
// Power event will be triggered if |with_power| is set to false.
void FastForwardBy(base::TimeDelta delay, bool with_power = true) {
if (!with_power)
mock_power_monitor_source_->Suspend();
clock_.SetNow(clock_.Now() + delay);
if (with_power) {
task_environment_.FastForwardBy(delay);
} else {
mock_power_monitor_source_->Resume();
task_environment_.RunUntilIdle();
}
}
// Owned by power_monitor_. Use this to simulate a power suspend and resume. // Owned by power_monitor_. Use this to simulate a power suspend and resume.
StubPowerMonitorSource* mock_power_monitor_source_ = nullptr; StubPowerMonitorSource* mock_power_monitor_source_ = nullptr;
base::test::SingleThreadTaskEnvironment task_environment_{ base::test::SingleThreadTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME}; base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::SimpleTestClock clock_;
}; };
TEST_F(WallClockTimerTest, PowerResume) { TEST_F(WallClockTimerTest, PowerResume) {
::testing::StrictMock<base::MockOnceClosure> callback; ::testing::StrictMock<base::MockOnceClosure> callback;
base::SimpleTestClock clock;
// Set up a WallClockTimer that will fire in one minute. // Set up a WallClockTimer that will fire in one minute.
WallClockTimer wall_clock_timer(&clock, task_environment_.GetMockTickClock()); WallClockTimer wall_clock_timer(&clock_,
task_environment_.GetMockTickClock());
constexpr auto delay = base::TimeDelta::FromMinutes(1); constexpr auto delay = base::TimeDelta::FromMinutes(1);
const auto start_time = base::Time::Now(); const auto start_time = base::Time::Now();
const auto run_time = start_time + delay; const auto run_time = start_time + delay;
clock.SetNow(start_time); clock_.SetNow(start_time);
wall_clock_timer.Start(FROM_HERE, run_time, callback.Get()); wall_clock_timer.Start(FROM_HERE, run_time, callback.Get());
EXPECT_EQ(wall_clock_timer.desired_run_time(), start_time + delay); EXPECT_EQ(wall_clock_timer.desired_run_time(), start_time + delay);
mock_power_monitor_source_->Suspend();
// Pretend that time jumps forward 30 seconds while the machine is suspended. // Pretend that time jumps forward 30 seconds while the machine is suspended.
constexpr auto past_time = base::TimeDelta::FromSeconds(30); constexpr auto past_time = base::TimeDelta::FromSeconds(30);
clock.SetNow(start_time + past_time); FastForwardBy(past_time, /*with_power=*/false);
mock_power_monitor_source_->Resume();
task_environment_.RunUntilIdle();
// Ensure that the timer has not yet fired. // Ensure that the timer has not yet fired.
::testing::Mock::VerifyAndClearExpectations(&callback); ::testing::Mock::VerifyAndClearExpectations(&callback);
EXPECT_EQ(wall_clock_timer.desired_run_time(), start_time + delay); EXPECT_EQ(wall_clock_timer.desired_run_time(), start_time + delay);
...@@ -75,10 +92,70 @@ TEST_F(WallClockTimerTest, PowerResume) { ...@@ -75,10 +92,70 @@ TEST_F(WallClockTimerTest, PowerResume) {
EXPECT_CALL(callback, Run()); EXPECT_CALL(callback, Run());
// Both Time::Now() and |task_environment_| MockTickClock::Now() // Both Time::Now() and |task_environment_| MockTickClock::Now()
// go forward by (|delay| - |past_time|): // go forward by (|delay| - |past_time|):
clock.SetNow(start_time + delay); FastForwardBy(delay - past_time);
task_environment_.FastForwardBy(delay - past_time);
::testing::Mock::VerifyAndClearExpectations(&callback); ::testing::Mock::VerifyAndClearExpectations(&callback);
EXPECT_FALSE(wall_clock_timer.IsRunning()); EXPECT_FALSE(wall_clock_timer.IsRunning());
} }
TEST_F(WallClockTimerTest, UseTimerTwiceInRow) {
::testing::StrictMock<base::MockOnceClosure> first_callback;
::testing::StrictMock<base::MockOnceClosure> second_callback;
const auto start_time = base::Time::Now();
clock_.SetNow(start_time);
// Set up a WallClockTimer that will invoke |first_callback| in one minute.
// Once it's done, it will invoke |second_callback| after the other minute.
WallClockTimer wall_clock_timer(&clock_,
task_environment_.GetMockTickClock());
constexpr auto delay = base::TimeDelta::FromMinutes(1);
wall_clock_timer.Start(FROM_HERE, clock_.Now() + delay, first_callback.Get());
EXPECT_CALL(first_callback, Run())
.WillOnce(::testing::InvokeWithoutArgs(
[this, &wall_clock_timer, &second_callback, delay]() {
wall_clock_timer.Start(FROM_HERE, clock_.Now() + delay,
second_callback.Get());
}));
FastForwardBy(delay);
::testing::Mock::VerifyAndClearExpectations(&first_callback);
::testing::Mock::VerifyAndClearExpectations(&second_callback);
// When the |wall_clock_time| is used for the second time, it can still handle
// power suspension properly.
constexpr auto past_time = base::TimeDelta::FromSeconds(30);
FastForwardBy(past_time, /*with_power=*/false);
::testing::Mock::VerifyAndClearExpectations(&second_callback);
EXPECT_CALL(second_callback, Run());
FastForwardBy(delay - past_time);
::testing::Mock::VerifyAndClearExpectations(&second_callback);
}
TEST_F(WallClockTimerTest, Stop) {
::testing::StrictMock<base::MockOnceClosure> callback;
const auto start_time = base::Time::Now();
clock_.SetNow(start_time);
// Set up a WallClockTimer.
WallClockTimer wall_clock_timer(&clock_,
task_environment_.GetMockTickClock());
constexpr auto delay = base::TimeDelta::FromMinutes(1);
wall_clock_timer.Start(FROM_HERE, clock_.Now() + delay, callback.Get());
// After 20 seconds, timer is stopped.
constexpr auto past_time = base::TimeDelta::FromSeconds(20);
FastForwardBy(past_time);
EXPECT_TRUE(wall_clock_timer.IsRunning());
wall_clock_timer.Stop();
EXPECT_FALSE(wall_clock_timer.IsRunning());
// When power is suspends and resumed, timer won't be resumed.
FastForwardBy(past_time, /*with_power=*/false);
EXPECT_FALSE(wall_clock_timer.IsRunning());
// Timer won't fire when desired run time is reached.
FastForwardBy(delay - past_time * 2);
::testing::Mock::VerifyAndClearExpectations(&callback);
}
} // namespace util } // namespace util
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment