// 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 "base/task/sequence_manager/time_domain.h" #include #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/task/sequence_manager/sequence_manager_impl.h" #include "base/task/sequence_manager/task_queue_impl.h" #include "base/task/sequence_manager/work_queue.h" #include "base/test/simple_test_tick_clock.h" #include "testing/gmock/include/gmock/gmock.h" using testing::_; using testing::AnyNumber; using testing::Mock; namespace base { namespace sequence_manager { class TaskQueueImplForTest : public internal::TaskQueueImpl { public: TaskQueueImplForTest(internal::SequenceManagerImpl* sequence_manager, TimeDomain* time_domain, const TaskQueue::Spec& spec) : TaskQueueImpl(sequence_manager, time_domain, spec) {} ~TaskQueueImplForTest() {} using TaskQueueImpl::SetDelayedWakeUpForTesting; }; class TestTimeDomain : public TimeDomain { public: TestTimeDomain() : now_(TimeTicks() + TimeDelta::FromSeconds(1)) {} ~TestTimeDomain() override = default; using TimeDomain::NextScheduledRunTime; using TimeDomain::SetNextWakeUpForQueue; using TimeDomain::UnregisterQueue; using TimeDomain::WakeUpReadyDelayedQueues; LazyNow CreateLazyNow() const override { return LazyNow(now_); } TimeTicks Now() const override { return now_; } Optional DelayTillNextTask(LazyNow* lazy_now) override { return Optional(); } void AsValueIntoInternal(trace_event::TracedValue* state) const override {} const char* GetName() const override { return "Test"; } internal::TaskQueueImpl* NextScheduledTaskQueue() const { if (delayed_wake_up_queue_.empty()) return nullptr; return delayed_wake_up_queue_.Min().queue; } MOCK_METHOD2(SetNextDelayedDoWork, void(LazyNow* lazy_now, TimeTicks run_time)); void SetNow(TimeTicks now) { now_ = now; } private: TimeTicks now_; DISALLOW_COPY_AND_ASSIGN(TestTimeDomain); }; class TimeDomainTest : public testing::Test { public: void SetUp() final { time_domain_ = WrapUnique(CreateTestTimeDomain()); task_queue_ = std::make_unique( nullptr, time_domain_.get(), TaskQueue::Spec("test")); } void TearDown() final { if (task_queue_) task_queue_->UnregisterTaskQueue(); } virtual TestTimeDomain* CreateTestTimeDomain() { return new TestTimeDomain(); } std::unique_ptr time_domain_; std::unique_ptr task_queue_; }; TEST_F(TimeDomainTest, ScheduleWakeUpForQueue) { TimeDelta delay = TimeDelta::FromMilliseconds(10); TimeTicks delayed_runtime = time_domain_->Now() + delay; EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, delayed_runtime)); TimeTicks now = time_domain_->Now(); LazyNow lazy_now(now); task_queue_->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{now + delay, 0}); EXPECT_EQ(delayed_runtime, time_domain_->NextScheduledRunTime()); EXPECT_EQ(task_queue_.get(), time_domain_->NextScheduledTaskQueue()); Mock::VerifyAndClearExpectations(time_domain_.get()); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, TimeTicks::Max())) .Times(AnyNumber()); } TEST_F(TimeDomainTest, ScheduleWakeUpForQueueSupersedesPreviousWakeUp) { TimeDelta delay1 = TimeDelta::FromMilliseconds(10); TimeDelta delay2 = TimeDelta::FromMilliseconds(100); TimeTicks delayed_runtime1 = time_domain_->Now() + delay1; TimeTicks delayed_runtime2 = time_domain_->Now() + delay2; EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, delayed_runtime1)); TimeTicks now = time_domain_->Now(); LazyNow lazy_now(now); task_queue_->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{delayed_runtime1, 0}); EXPECT_EQ(delayed_runtime1, time_domain_->NextScheduledRunTime()); Mock::VerifyAndClearExpectations(time_domain_.get()); // Now schedule a later wake_up, which should replace the previously // requested one. EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, delayed_runtime2)); task_queue_->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{delayed_runtime2, 0}); EXPECT_EQ(delayed_runtime2, time_domain_->NextScheduledRunTime()); Mock::VerifyAndClearExpectations(time_domain_.get()); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, TimeTicks::Max())) .Times(AnyNumber()); } TEST_F(TimeDomainTest, SetNextDelayedDoWork_OnlyCalledForEarlierTasks) { std::unique_ptr task_queue2 = std::make_unique(nullptr, time_domain_.get(), TaskQueue::Spec("test")); std::unique_ptr task_queue3 = std::make_unique(nullptr, time_domain_.get(), TaskQueue::Spec("test")); std::unique_ptr task_queue4 = std::make_unique(nullptr, time_domain_.get(), TaskQueue::Spec("test")); TimeDelta delay1 = TimeDelta::FromMilliseconds(10); TimeDelta delay2 = TimeDelta::FromMilliseconds(20); TimeDelta delay3 = TimeDelta::FromMilliseconds(30); TimeDelta delay4 = TimeDelta::FromMilliseconds(1); // SetNextDelayedDoWork should always be called if there are no other // wake-ups. TimeTicks now = time_domain_->Now(); LazyNow lazy_now(now); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, now + delay1)); task_queue_->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{now + delay1, 0}); Mock::VerifyAndClearExpectations(time_domain_.get()); // SetNextDelayedDoWork should not be called when scheduling later tasks. EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, _)).Times(0); task_queue2->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{now + delay2, 0}); task_queue3->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{now + delay3, 0}); // SetNextDelayedDoWork should be called when scheduling earlier tasks. Mock::VerifyAndClearExpectations(time_domain_.get()); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, now + delay4)); task_queue4->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{now + delay4, 0}); Mock::VerifyAndClearExpectations(time_domain_.get()); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, _)).Times(2); task_queue2->UnregisterTaskQueue(); task_queue3->UnregisterTaskQueue(); task_queue4->UnregisterTaskQueue(); } TEST_F(TimeDomainTest, UnregisterQueue) { std::unique_ptr task_queue2_ = std::make_unique(nullptr, time_domain_.get(), TaskQueue::Spec("test")); TimeTicks now = time_domain_->Now(); LazyNow lazy_now(now); TimeTicks wake_up1 = now + TimeDelta::FromMilliseconds(10); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, wake_up1)).Times(1); task_queue_->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{wake_up1, 0}); TimeTicks wake_up2 = now + TimeDelta::FromMilliseconds(100); task_queue2_->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{wake_up2, 0}); EXPECT_EQ(task_queue_.get(), time_domain_->NextScheduledTaskQueue()); testing::Mock::VerifyAndClearExpectations(time_domain_.get()); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, wake_up2)).Times(1); time_domain_->UnregisterQueue(task_queue_.get()); task_queue_ = std::unique_ptr(); EXPECT_EQ(task_queue2_.get(), time_domain_->NextScheduledTaskQueue()); testing::Mock::VerifyAndClearExpectations(time_domain_.get()); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, TimeTicks::Max())) .Times(1); time_domain_->UnregisterQueue(task_queue2_.get()); EXPECT_FALSE(time_domain_->NextScheduledTaskQueue()); } TEST_F(TimeDomainTest, WakeUpReadyDelayedQueues) { TimeDelta delay = TimeDelta::FromMilliseconds(50); TimeTicks now = time_domain_->Now(); LazyNow lazy_now_1(now); TimeTicks delayed_runtime = now + delay; EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, delayed_runtime)); task_queue_->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{delayed_runtime, 0}); EXPECT_EQ(delayed_runtime, time_domain_->NextScheduledRunTime()); time_domain_->WakeUpReadyDelayedQueues(&lazy_now_1); EXPECT_EQ(delayed_runtime, time_domain_->NextScheduledRunTime()); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, TimeTicks::Max())); time_domain_->SetNow(delayed_runtime); LazyNow lazy_now_2(time_domain_->CreateLazyNow()); time_domain_->WakeUpReadyDelayedQueues(&lazy_now_2); ASSERT_FALSE(time_domain_->NextScheduledRunTime()); } TEST_F(TimeDomainTest, WakeUpReadyDelayedQueuesWithIdenticalRuntimes) { int sequence_num = 0; TimeDelta delay = TimeDelta::FromMilliseconds(50); TimeTicks now = time_domain_->Now(); LazyNow lazy_now(now); TimeTicks delayed_runtime = now + delay; EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, delayed_runtime)); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, TimeTicks::Max())); std::unique_ptr task_queue2 = std::make_unique(nullptr, time_domain_.get(), TaskQueue::Spec("test")); task_queue2->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{delayed_runtime, ++sequence_num}); task_queue_->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{delayed_runtime, ++sequence_num}); time_domain_->WakeUpReadyDelayedQueues(&lazy_now); // The second task queue should wake up first since it has a lower sequence // number. EXPECT_EQ(task_queue2.get(), time_domain_->NextScheduledTaskQueue()); task_queue2->UnregisterTaskQueue(); } TEST_F(TimeDomainTest, CancelDelayedWork) { TimeTicks now = time_domain_->Now(); LazyNow lazy_now(now); TimeTicks run_time = now + TimeDelta::FromMilliseconds(20); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, run_time)); task_queue_->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{run_time, 0}); EXPECT_EQ(task_queue_.get(), time_domain_->NextScheduledTaskQueue()); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, TimeTicks::Max())); task_queue_->SetDelayedWakeUpForTesting(nullopt); EXPECT_FALSE(time_domain_->NextScheduledTaskQueue()); } TEST_F(TimeDomainTest, CancelDelayedWork_TwoQueues) { std::unique_ptr task_queue2 = std::make_unique(nullptr, time_domain_.get(), TaskQueue::Spec("test")); TimeTicks now = time_domain_->Now(); LazyNow lazy_now(now); TimeTicks run_time1 = now + TimeDelta::FromMilliseconds(20); TimeTicks run_time2 = now + TimeDelta::FromMilliseconds(40); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, run_time1)); task_queue_->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{run_time1, 0}); Mock::VerifyAndClearExpectations(time_domain_.get()); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, _)).Times(0); task_queue2->SetDelayedWakeUpForTesting( internal::TaskQueueImpl::DelayedWakeUp{run_time2, 0}); Mock::VerifyAndClearExpectations(time_domain_.get()); EXPECT_EQ(task_queue_.get(), time_domain_->NextScheduledTaskQueue()); EXPECT_EQ(run_time1, time_domain_->NextScheduledRunTime()); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, run_time2)); task_queue_->SetDelayedWakeUpForTesting(nullopt); EXPECT_EQ(task_queue2.get(), time_domain_->NextScheduledTaskQueue()); EXPECT_EQ(run_time2, time_domain_->NextScheduledRunTime()); Mock::VerifyAndClearExpectations(time_domain_.get()); EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, _)) .Times(AnyNumber()); // Tidy up. task_queue2->UnregisterTaskQueue(); } } // namespace sequence_manager } // namespace base