// 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 #include #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 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 first_callback; ::testing::StrictMock 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 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 first_callback; ::testing::StrictMock 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 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 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 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