| // Copyright 2015 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 "platform/Timer.h" |
| |
| #include "platform/scheduler/base/task_queue_impl.h" |
| #include "platform/scheduler/child/web_task_runner_impl.h" |
| #include "platform/scheduler/renderer/renderer_scheduler_impl.h" |
| #include "platform/testing/TestingPlatformSupport.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebScheduler.h" |
| #include "public/platform/WebThread.h" |
| #include "public/platform/WebViewScheduler.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "wtf/CurrentTime.h" |
| #include "wtf/PtrUtil.h" |
| #include "wtf/RefCounted.h" |
| #include <memory> |
| #include <queue> |
| |
| using testing::ElementsAre; |
| |
| namespace blink { |
| namespace { |
| |
| class TimerTest : public testing::Test { |
| public: |
| void SetUp() override |
| { |
| m_runTimes.clear(); |
| m_platform.advanceClockSeconds(10.0); |
| m_startTime = monotonicallyIncreasingTime(); |
| } |
| |
| void countingTask(TimerBase*) |
| { |
| m_runTimes.append(monotonicallyIncreasingTime()); |
| } |
| |
| void recordNextFireTimeTask(TimerBase* timer) |
| { |
| m_nextFireTimes.append(monotonicallyIncreasingTime() + timer->nextFireInterval()); |
| } |
| |
| void runUntilDeadline(double deadline) |
| { |
| double period = deadline - monotonicallyIncreasingTime(); |
| EXPECT_GE(period, 0.0); |
| m_platform.runForPeriodSeconds(period); |
| |
| // We may have stopped before the clock advanced to |deadline|. |
| double timeToAdvance = deadline - monotonicallyIncreasingTime(); |
| m_platform.advanceClockSeconds(timeToAdvance); |
| } |
| |
| // Returns false if there are no pending delayed tasks, otherwise sets |time| to |
| // the delay in seconds till the next pending delayed task is scheduled to fire. |
| bool timeTillNextDelayedTask(double* time) const |
| { |
| base::TimeTicks nextRunTime; |
| if (!m_platform.rendererScheduler()->TimerTaskRunner()->GetTimeDomain()->NextScheduledRunTime(&nextRunTime)) |
| return false; |
| *time = (nextRunTime - m_platform.rendererScheduler()->TimerTaskRunner()->GetTimeDomain()->Now()).InSecondsF(); |
| return true; |
| } |
| |
| protected: |
| double m_startTime; |
| WTF::Vector<double> m_runTimes; |
| WTF::Vector<double> m_nextFireTimes; |
| TestingPlatformSupportWithMockScheduler m_platform; |
| }; |
| |
| TEST_F(TimerTest, StartOneShot_Zero) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_FALSE(timeTillNextDelayedTask(&runTime)); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime)); |
| } |
| |
| TEST_F(TimerTest, StartOneShot_ZeroAndCancel) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_FALSE(timeTillNextDelayedTask(&runTime)); |
| |
| timer.stop(); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_FALSE(m_runTimes.size()); |
| } |
| |
| TEST_F(TimerTest, StartOneShot_ZeroAndCancelThenRepost) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_FALSE(timeTillNextDelayedTask(&runTime)); |
| |
| timer.stop(); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_FALSE(m_runTimes.size()); |
| |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| EXPECT_FALSE(timeTillNextDelayedTask(&runTime)); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime)); |
| } |
| |
| TEST_F(TimerTest, StartOneShot_Zero_RepostingAfterRunning) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_FALSE(timeTillNextDelayedTask(&runTime)); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime)); |
| |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| EXPECT_FALSE(timeTillNextDelayedTask(&runTime)); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime, m_startTime)); |
| } |
| |
| TEST_F(TimerTest, StartOneShot_NonZero) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(10.0, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| EXPECT_FLOAT_EQ(10.0, runTime); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 10.0)); |
| } |
| |
| TEST_F(TimerTest, StartOneShot_NonZeroAndCancel) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| EXPECT_FLOAT_EQ(10.0, runTime); |
| |
| timer.stop(); |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_FALSE(m_runTimes.size()); |
| } |
| |
| TEST_F(TimerTest, StartOneShot_NonZeroAndCancelThenRepost) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| EXPECT_FLOAT_EQ(10.0, runTime); |
| |
| timer.stop(); |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_FALSE(m_runTimes.size()); |
| |
| double secondPostTime = monotonicallyIncreasingTime(); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| EXPECT_FLOAT_EQ(10.0, runTime); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_THAT(m_runTimes, ElementsAre(secondPostTime + 10.0)); |
| } |
| |
| TEST_F(TimerTest, StartOneShot_NonZero_RepostingAfterRunning) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| EXPECT_FLOAT_EQ(10.0, runTime); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 10.0)); |
| |
| timer.startOneShot(20, BLINK_FROM_HERE); |
| |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| EXPECT_FLOAT_EQ(20.0, runTime); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 10.0, m_startTime + 30.0)); |
| } |
| |
| TEST_F(TimerTest, PostingTimerTwiceWithSameRunTimeDoesNothing) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| EXPECT_FLOAT_EQ(10.0, runTime); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 10.0)); |
| } |
| |
| TEST_F(TimerTest, PostingTimerTwiceWithNewerRunTimeCancelsOriginalTask) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 0.0)); |
| } |
| |
| TEST_F(TimerTest, PostingTimerTwiceWithLaterRunTimeCancelsOriginalTask) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 10.0)); |
| } |
| |
| TEST_F(TimerTest, StartRepeatingTask) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startRepeating(1.0, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| EXPECT_FLOAT_EQ(1.0, runTime); |
| |
| runUntilDeadline(m_startTime + 5.5); |
| EXPECT_THAT(m_runTimes, ElementsAre( |
| m_startTime + 1.0, m_startTime + 2.0, m_startTime + 3.0, m_startTime + 4.0, m_startTime + 5.0)); |
| } |
| |
| TEST_F(TimerTest, StartRepeatingTask_ThenCancel) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startRepeating(1.0, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| EXPECT_FLOAT_EQ(1.0, runTime); |
| |
| runUntilDeadline(m_startTime + 2.5); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 1.0, m_startTime + 2.0)); |
| |
| timer.stop(); |
| m_platform.runUntilIdle(); |
| |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 1.0, m_startTime + 2.0)); |
| } |
| |
| TEST_F(TimerTest, StartRepeatingTask_ThenPostOneShot) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startRepeating(1.0, BLINK_FROM_HERE); |
| |
| double runTime; |
| EXPECT_TRUE(timeTillNextDelayedTask(&runTime)); |
| EXPECT_FLOAT_EQ(1.0, runTime); |
| |
| runUntilDeadline(m_startTime + 2.5); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 1.0, m_startTime + 2.0)); |
| |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| m_platform.runUntilIdle(); |
| |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 1.0, m_startTime + 2.0, m_startTime + 2.5)); |
| } |
| |
| TEST_F(TimerTest, IsActive_NeverPosted) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| |
| EXPECT_FALSE(timer.isActive()); |
| } |
| |
| TEST_F(TimerTest, IsActive_AfterPosting_OneShotZero) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| EXPECT_TRUE(timer.isActive()); |
| } |
| |
| TEST_F(TimerTest, IsActive_AfterPosting_OneShotNonZero) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| |
| EXPECT_TRUE(timer.isActive()); |
| } |
| |
| TEST_F(TimerTest, IsActive_AfterPosting_Repeating) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startRepeating(1.0, BLINK_FROM_HERE); |
| |
| EXPECT_TRUE(timer.isActive()); |
| } |
| |
| TEST_F(TimerTest, IsActive_AfterRunning_OneShotZero) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_FALSE(timer.isActive()); |
| } |
| |
| TEST_F(TimerTest, IsActive_AfterRunning_OneShotNonZero) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| |
| m_platform.runUntilIdle(); |
| EXPECT_FALSE(timer.isActive()); |
| } |
| |
| TEST_F(TimerTest, IsActive_AfterRunning_Repeating) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startRepeating(1.0, BLINK_FROM_HERE); |
| |
| runUntilDeadline(m_startTime + 10); |
| EXPECT_TRUE(timer.isActive()); // It should run until cancelled. |
| } |
| |
| TEST_F(TimerTest, NextFireInterval_OneShotZero) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| EXPECT_FLOAT_EQ(0.0, timer.nextFireInterval()); |
| } |
| |
| TEST_F(TimerTest, NextFireInterval_OneShotNonZero) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| |
| EXPECT_FLOAT_EQ(10.0, timer.nextFireInterval()); |
| } |
| |
| TEST_F(TimerTest, NextFireInterval_OneShotNonZero_AfterAFewSeconds) |
| { |
| m_platform.setAutoAdvanceNowToPendingTasks(false); |
| |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| |
| m_platform.advanceClockSeconds(2.0); |
| EXPECT_FLOAT_EQ(8.0, timer.nextFireInterval()); |
| } |
| |
| TEST_F(TimerTest, NextFireInterval_Repeating) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startRepeating(20, BLINK_FROM_HERE); |
| |
| EXPECT_FLOAT_EQ(20.0, timer.nextFireInterval()); |
| } |
| |
| TEST_F(TimerTest, RepeatInterval_NeverStarted) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| |
| EXPECT_FLOAT_EQ(0.0, timer.repeatInterval()); |
| } |
| |
| TEST_F(TimerTest, RepeatInterval_OneShotZero) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| EXPECT_FLOAT_EQ(0.0, timer.repeatInterval()); |
| } |
| |
| TEST_F(TimerTest, RepeatInterval_OneShotNonZero) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startOneShot(10, BLINK_FROM_HERE); |
| |
| EXPECT_FLOAT_EQ(0.0, timer.repeatInterval()); |
| } |
| |
| TEST_F(TimerTest, RepeatInterval_Repeating) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startRepeating(20, BLINK_FROM_HERE); |
| |
| EXPECT_FLOAT_EQ(20.0, timer.repeatInterval()); |
| } |
| |
| TEST_F(TimerTest, AugmentRepeatInterval) |
| { |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startRepeating(10, BLINK_FROM_HERE); |
| EXPECT_FLOAT_EQ(10.0, timer.repeatInterval()); |
| EXPECT_FLOAT_EQ(10.0, timer.nextFireInterval()); |
| |
| m_platform.advanceClockSeconds(2.0); |
| timer.augmentRepeatInterval(10); |
| |
| EXPECT_FLOAT_EQ(20.0, timer.repeatInterval()); |
| EXPECT_FLOAT_EQ(18.0, timer.nextFireInterval()); |
| |
| // NOTE setAutoAdvanceNowToPendingTasks(true) (which uses cc::OrderedSimpleTaskRunner) |
| // results in somewhat strange behavior of the test clock which breaks this test. |
| // Specifically the test clock advancing logic ignores newly posted delayed tasks and |
| // advances too far. |
| runUntilDeadline(m_startTime + 50.0); |
| EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 20.0, m_startTime + 40.0)); |
| } |
| |
| TEST_F(TimerTest, AugmentRepeatInterval_TimerFireDelayed) |
| { |
| m_platform.setAutoAdvanceNowToPendingTasks(false); |
| |
| Timer<TimerTest> timer(this, &TimerTest::countingTask); |
| timer.startRepeating(10, BLINK_FROM_HERE); |
| EXPECT_FLOAT_EQ(10.0, timer.repeatInterval()); |
| EXPECT_FLOAT_EQ(10.0, timer.nextFireInterval()); |
| |
| m_platform.advanceClockSeconds(123.0); // Make the timer long overdue. |
| timer.augmentRepeatInterval(10); |
| |
| EXPECT_FLOAT_EQ(20.0, timer.repeatInterval()); |
| // The timer is overdue so it should be scheduled to fire immediatly. |
| EXPECT_FLOAT_EQ(0.0, timer.nextFireInterval()); |
| } |
| |
| TEST_F(TimerTest, RepeatingTimerDoesNotDrift) |
| { |
| m_platform.setAutoAdvanceNowToPendingTasks(false); |
| |
| Timer<TimerTest> timer(this, &TimerTest::recordNextFireTimeTask); |
| timer.startRepeating(2.0, BLINK_FROM_HERE); |
| |
| recordNextFireTimeTask(&timer); // Next scheduled task to run at m_startTime + 2.0 |
| |
| // Simulate timer firing early. Next scheduled task to run at m_startTime + 4.0 |
| m_platform.advanceClockSeconds(1.9); |
| runUntilDeadline(monotonicallyIncreasingTime() + 0.2); |
| |
| m_platform.runForPeriodSeconds(2.0); // Next scheduled task to run at m_startTime + 6.0 |
| m_platform.runForPeriodSeconds(2.1); // Next scheduled task to run at m_startTime + 8.0 |
| m_platform.runForPeriodSeconds(2.9); // Next scheduled task to run at m_startTime + 10.0 |
| m_platform.runForPeriodSeconds(3.1); // Next scheduled task to run at m_startTime + 14.0 (skips a beat) |
| m_platform.runForPeriodSeconds(4.0); // Next scheduled task to run at m_startTime + 18.0 (skips a beat) |
| m_platform.runForPeriodSeconds(10.0); // Next scheduled task to run at m_startTime + 28.0 (skips 5 beats) |
| |
| EXPECT_THAT(m_nextFireTimes, ElementsAre( |
| m_startTime + 2.0, |
| m_startTime + 4.0, |
| m_startTime + 6.0, |
| m_startTime + 8.0, |
| m_startTime + 10.0, |
| m_startTime + 14.0, |
| m_startTime + 18.0, |
| m_startTime + 28.0)); |
| } |
| |
| template <typename TimerFiredClass> |
| class TimerForTest : public TaskRunnerTimer<TimerFiredClass> { |
| public: |
| using TimerFiredFunction = typename TaskRunnerTimer<TimerFiredClass>::TimerFiredFunction; |
| |
| ~TimerForTest() override { } |
| |
| TimerForTest(WebTaskRunner* webTaskRunner, TimerFiredClass* timerFiredClass, TimerFiredFunction timerFiredFunction) |
| : TaskRunnerTimer<TimerFiredClass>(webTaskRunner, timerFiredClass, timerFiredFunction) |
| { |
| } |
| }; |
| |
| TEST_F(TimerTest, UserSuppliedWebTaskRunner) |
| { |
| scoped_refptr<scheduler::TaskQueue> taskRunner(m_platform.rendererScheduler()->NewTimerTaskRunner("test")); |
| scheduler::WebTaskRunnerImpl webTaskRunner(taskRunner); |
| TimerForTest<TimerTest> timer(&webTaskRunner, this, &TimerTest::countingTask); |
| timer.startOneShot(0, BLINK_FROM_HERE); |
| |
| // Make sure the task was posted on taskRunner. |
| EXPECT_FALSE(taskRunner->IsEmpty()); |
| } |
| |
| |
| } // namespace |
| } // namespace blink |