blob: f399d0106b79241dd3ed664a00887070ba9585ae [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// 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::TimeDelta::FromMinutes(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::TimeDelta::FromSeconds(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::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;
clock_.SetNow(base::Time::Now());
// 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);
}
TEST_F(WallClockTimerTest, RestartRunningTimer) {
::testing::StrictMock<base::MockOnceClosure> first_callback;
::testing::StrictMock<base::MockOnceClosure> second_callback;
constexpr auto delay = base::TimeDelta::FromMinutes(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::TimeDelta::FromMinutes(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::TimeDelta::FromMinutes(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::TimeDelta::FromSeconds(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::TimeDelta::FromMinutes(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::TimeDelta::FromSeconds(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