257 lines
10 KiB
C++
257 lines
10 KiB
C++
// Copyright 2018 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/timer/wall_clock_timer.h"
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "base/test/mock_callback.h"
|
|
#include "base/test/power_monitor_test.h"
|
|
#include "base/test/simple_test_clock.h"
|
|
#include "base/test/task_environment.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace base {
|
|
|
|
class WallClockTimerTest : public ::testing::Test {
|
|
protected:
|
|
// 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)
|
|
fake_power_monitor_source_.Suspend();
|
|
|
|
clock_.Advance(delay);
|
|
|
|
if (with_power) {
|
|
task_environment_.FastForwardBy(delay);
|
|
} else {
|
|
fake_power_monitor_source_.Resume();
|
|
task_environment_.RunUntilIdle();
|
|
}
|
|
}
|
|
|
|
base::test::ScopedPowerMonitorTestSource fake_power_monitor_source_;
|
|
base::test::SingleThreadTaskEnvironment task_environment_{
|
|
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
|
|
base::SimpleTestClock clock_;
|
|
};
|
|
|
|
TEST_F(WallClockTimerTest, PowerResume) {
|
|
::testing::StrictMock<base::MockOnceClosure> callback;
|
|
// Set up a WallClockTimer that will fire in one minute.
|
|
WallClockTimer wall_clock_timer(&clock_,
|
|
task_environment_.GetMockTickClock());
|
|
constexpr auto delay = base::Minutes(1);
|
|
const auto start_time = base::Time::Now();
|
|
const auto run_time = start_time + delay;
|
|
clock_.SetNow(start_time);
|
|
wall_clock_timer.Start(FROM_HERE, run_time, callback.Get());
|
|
EXPECT_EQ(wall_clock_timer.desired_run_time(), start_time + delay);
|
|
|
|
// Pretend that time jumps forward 30 seconds while the machine is suspended.
|
|
constexpr auto past_time = base::Seconds(30);
|
|
FastForwardBy(past_time, /*with_power=*/false);
|
|
// Ensure that the timer has not yet fired.
|
|
::testing::Mock::VerifyAndClearExpectations(&callback);
|
|
EXPECT_EQ(wall_clock_timer.desired_run_time(), start_time + delay);
|
|
|
|
// Expect that the timer fires at the desired run time.
|
|
EXPECT_CALL(callback, Run());
|
|
// Both Time::Now() and |task_environment_| MockTickClock::Now()
|
|
// go forward by (|delay| - |past_time|):
|
|
FastForwardBy(delay - past_time);
|
|
::testing::Mock::VerifyAndClearExpectations(&callback);
|
|
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::Minutes(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::Seconds(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;
|
|
clock_.SetNow(base::Time::Now());
|
|
|
|
// Set up a WallClockTimer.
|
|
WallClockTimer wall_clock_timer(&clock_,
|
|
task_environment_.GetMockTickClock());
|
|
constexpr auto delay = base::Minutes(1);
|
|
wall_clock_timer.Start(FROM_HERE, clock_.Now() + delay, callback.Get());
|
|
|
|
// After 20 seconds, timer is stopped.
|
|
constexpr auto past_time = base::Seconds(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);
|
|
}
|
|
|
|
TEST_F(WallClockTimerTest, RestartRunningTimer) {
|
|
::testing::StrictMock<base::MockOnceClosure> first_callback;
|
|
::testing::StrictMock<base::MockOnceClosure> second_callback;
|
|
constexpr auto delay = base::Minutes(1);
|
|
|
|
// Set up a WallClockTimer that will invoke |first_callback| in one minute.
|
|
clock_.SetNow(base::Time::Now());
|
|
WallClockTimer wall_clock_timer(&clock_,
|
|
task_environment_.GetMockTickClock());
|
|
wall_clock_timer.Start(FROM_HERE, clock_.Now() + delay, first_callback.Get());
|
|
|
|
// After 30 seconds, replace the timer with |second_callback| with new one
|
|
// minute delay.
|
|
constexpr auto past_time = delay / 2;
|
|
FastForwardBy(past_time);
|
|
wall_clock_timer.Start(FROM_HERE, clock_.Now() + delay,
|
|
second_callback.Get());
|
|
|
|
// |first_callback| is due but it won't be called because it's replaced.
|
|
FastForwardBy(past_time);
|
|
::testing::Mock::VerifyAndClearExpectations(&first_callback);
|
|
::testing::Mock::VerifyAndClearExpectations(&second_callback);
|
|
|
|
// Timer invokes the |second_callback|.
|
|
EXPECT_CALL(second_callback, Run());
|
|
FastForwardBy(past_time);
|
|
::testing::Mock::VerifyAndClearExpectations(&first_callback);
|
|
::testing::Mock::VerifyAndClearExpectations(&second_callback);
|
|
}
|
|
|
|
TEST_F(WallClockTimerTest, DoubleStop) {
|
|
::testing::StrictMock<base::MockOnceClosure> callback;
|
|
clock_.SetNow(base::Time::Now());
|
|
|
|
// Set up a WallClockTimer.
|
|
WallClockTimer wall_clock_timer(&clock_,
|
|
task_environment_.GetMockTickClock());
|
|
constexpr auto delay = base::Minutes(1);
|
|
wall_clock_timer.Start(FROM_HERE, clock_.Now() + delay, callback.Get());
|
|
|
|
// After 15 seconds, timer is stopped.
|
|
constexpr auto past_time = delay / 4;
|
|
FastForwardBy(past_time);
|
|
EXPECT_TRUE(wall_clock_timer.IsRunning());
|
|
wall_clock_timer.Stop();
|
|
EXPECT_FALSE(wall_clock_timer.IsRunning());
|
|
|
|
// And timer is stopped again later. The second stop should be a no-op.
|
|
FastForwardBy(past_time);
|
|
EXPECT_FALSE(wall_clock_timer.IsRunning());
|
|
wall_clock_timer.Stop();
|
|
EXPECT_FALSE(wall_clock_timer.IsRunning());
|
|
|
|
// Timer won't fire after stop.
|
|
FastForwardBy(past_time, /*with_power=*/false);
|
|
FastForwardBy(delay - past_time * 3);
|
|
::testing::Mock::VerifyAndClearExpectations(&callback);
|
|
}
|
|
|
|
// On some platforms, TickClock will never freeze. WallClockTimer are still
|
|
// supported on those platforms.
|
|
TEST_F(WallClockTimerTest, NonStopTickClock) {
|
|
::testing::StrictMock<base::MockOnceClosure> callback;
|
|
// Set up a WallClockTimer that will fire in one minute.
|
|
WallClockTimer wall_clock_timer(&clock_,
|
|
task_environment_.GetMockTickClock());
|
|
constexpr auto delay = base::Minutes(1);
|
|
const auto start_time = base::Time::Now();
|
|
const auto run_time = start_time + delay;
|
|
clock_.SetNow(start_time);
|
|
wall_clock_timer.Start(FROM_HERE, run_time, callback.Get());
|
|
EXPECT_EQ(wall_clock_timer.desired_run_time(), start_time + delay);
|
|
|
|
// Pretend that time jumps forward 30 seconds while the machine is suspended.
|
|
constexpr auto past_time = base::Seconds(30);
|
|
|
|
// Fastword with both clocks even the power is suspended.
|
|
fake_power_monitor_source_.Suspend();
|
|
clock_.SetNow(clock_.Now() + past_time);
|
|
task_environment_.FastForwardBy(past_time);
|
|
fake_power_monitor_source_.Resume();
|
|
|
|
// Ensure that the timer has not yet fired.
|
|
::testing::Mock::VerifyAndClearExpectations(&callback);
|
|
EXPECT_EQ(wall_clock_timer.desired_run_time(), start_time + delay);
|
|
|
|
// Expect that the timer fires at the desired run time.
|
|
EXPECT_CALL(callback, Run());
|
|
// Both Time::Now() and |task_environment_| MockTickClock::Now()
|
|
// go forward by (|delay| - |past_time|):
|
|
FastForwardBy(delay - past_time);
|
|
::testing::Mock::VerifyAndClearExpectations(&callback);
|
|
EXPECT_FALSE(wall_clock_timer.IsRunning());
|
|
}
|
|
|
|
TEST_F(WallClockTimerTest, NonStopTickClockWithLongPause) {
|
|
::testing::StrictMock<base::MockOnceClosure> callback;
|
|
// Set up a WallClockTimer that will fire in one minute.
|
|
WallClockTimer wall_clock_timer(&clock_,
|
|
task_environment_.GetMockTickClock());
|
|
constexpr auto delay = base::Minutes(1);
|
|
const auto start_time = base::Time::Now();
|
|
const auto run_time = start_time + delay;
|
|
clock_.SetNow(start_time);
|
|
wall_clock_timer.Start(FROM_HERE, run_time, callback.Get());
|
|
EXPECT_EQ(wall_clock_timer.desired_run_time(), start_time + delay);
|
|
|
|
// Pretend that time jumps forward 60 seconds while the machine is suspended.
|
|
constexpr auto past_time = base::Seconds(60);
|
|
|
|
// Fastword with both clocks even the power is suspended. Timer fires at the
|
|
// moment of power resume.
|
|
EXPECT_CALL(callback, Run());
|
|
fake_power_monitor_source_.Suspend();
|
|
clock_.SetNow(clock_.Now() + past_time);
|
|
task_environment_.FastForwardBy(past_time);
|
|
fake_power_monitor_source_.Resume();
|
|
|
|
::testing::Mock::VerifyAndClearExpectations(&callback);
|
|
EXPECT_FALSE(wall_clock_timer.IsRunning());
|
|
}
|
|
|
|
} // namespace base
|