| // Copyright 2014 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 "content/renderer/scheduler/renderer_scheduler_impl.h" |
| |
| #include "base/callback.h" |
| #include "cc/output/begin_frame_args.h" |
| #include "cc/test/ordered_simple_task_runner.h" |
| #include "content/child/scheduler/nestable_task_runner_for_test.h" |
| #include "content/child/scheduler/scheduler_message_loop_delegate.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace content { |
| |
| namespace { |
| class FakeInputEvent : public blink::WebInputEvent { |
| public: |
| explicit FakeInputEvent(blink::WebInputEvent::Type event_type) |
| : WebInputEvent(sizeof(FakeInputEvent)) { |
| type = event_type; |
| } |
| |
| FakeInputEvent(blink::WebInputEvent::Type event_type, int event_modifiers) |
| : WebInputEvent(sizeof(FakeInputEvent)) { |
| type = event_type; |
| modifiers = event_modifiers; |
| } |
| }; |
| |
| void AppendToVectorTestTask(std::vector<std::string>* vector, |
| std::string value) { |
| vector->push_back(value); |
| } |
| |
| void AppendToVectorIdleTestTask(std::vector<std::string>* vector, |
| std::string value, |
| base::TimeTicks deadline) { |
| AppendToVectorTestTask(vector, value); |
| } |
| |
| void NullTask() { |
| } |
| |
| void AppendToVectorReentrantTask( |
| base::SingleThreadTaskRunner* task_runner, |
| std::vector<int>* vector, |
| int* reentrant_count, |
| int max_reentrant_count) { |
| vector->push_back((*reentrant_count)++); |
| if (*reentrant_count < max_reentrant_count) { |
| task_runner->PostTask( |
| FROM_HERE, base::Bind(AppendToVectorReentrantTask, |
| base::Unretained(task_runner), vector, |
| reentrant_count, max_reentrant_count)); |
| } |
| } |
| |
| void IdleTestTask(int* run_count, |
| base::TimeTicks* deadline_out, |
| base::TimeTicks deadline) { |
| (*run_count)++; |
| *deadline_out = deadline; |
| } |
| |
| int max_idle_task_reposts = 2; |
| |
| void RepostingIdleTestTask( |
| SingleThreadIdleTaskRunner* idle_task_runner, |
| int* run_count, |
| base::TimeTicks deadline) { |
| if ((*run_count + 1) < max_idle_task_reposts) { |
| idle_task_runner->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&RepostingIdleTestTask, base::Unretained(idle_task_runner), |
| run_count)); |
| } |
| (*run_count)++; |
| } |
| |
| void UpdateClockToDeadlineIdleTestTask( |
| cc::TestNowSource* clock, |
| base::SingleThreadTaskRunner* task_runner, |
| int* run_count, |
| base::TimeTicks deadline) { |
| clock->SetNow(deadline); |
| // Due to the way in which OrderedSimpleTestRunner orders tasks and the fact |
| // that we updated the time within a task, the delayed pending task to call |
| // EndIdlePeriod will not happen until after a TaskQueueManager DoWork, so |
| // post a normal task here to ensure it runs before the next idle task. |
| task_runner->PostTask(FROM_HERE, base::Bind(NullTask)); |
| (*run_count)++; |
| } |
| |
| void PostingYieldingTestTask( |
| RendererSchedulerImpl* scheduler, |
| base::SingleThreadTaskRunner* task_runner, |
| bool simulate_input, |
| bool* should_yield_before, |
| bool* should_yield_after) { |
| *should_yield_before = scheduler->ShouldYieldForHighPriorityWork(); |
| task_runner->PostTask(FROM_HERE, base::Bind(NullTask)); |
| if (simulate_input) { |
| scheduler->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| } |
| *should_yield_after = scheduler->ShouldYieldForHighPriorityWork(); |
| } |
| |
| void AnticipationTestTask(RendererSchedulerImpl* scheduler, |
| bool simulate_input, |
| bool* is_anticipated_before, |
| bool* is_anticipated_after) { |
| *is_anticipated_before = scheduler->IsHighPriorityWorkAnticipated(); |
| if (simulate_input) { |
| scheduler->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| } |
| *is_anticipated_after = scheduler->IsHighPriorityWorkAnticipated(); |
| } |
| }; // namespace |
| |
| class RendererSchedulerImplTest : public testing::Test { |
| public: |
| using Policy = RendererSchedulerImpl::Policy; |
| |
| RendererSchedulerImplTest() |
| : clock_(cc::TestNowSource::Create(5000)), |
| mock_task_runner_(new cc::OrderedSimpleTaskRunner(clock_, false)), |
| nestable_task_runner_( |
| NestableTaskRunnerForTest::Create(mock_task_runner_)), |
| scheduler_(new RendererSchedulerImpl(nestable_task_runner_)), |
| default_task_runner_(scheduler_->DefaultTaskRunner()), |
| compositor_task_runner_(scheduler_->CompositorTaskRunner()), |
| loading_task_runner_(scheduler_->LoadingTaskRunner()), |
| idle_task_runner_(scheduler_->IdleTaskRunner()) { |
| scheduler_->SetTimeSourceForTesting(clock_); |
| } |
| |
| RendererSchedulerImplTest(base::MessageLoop* message_loop) |
| : clock_(cc::TestNowSource::Create(5000)), |
| message_loop_(message_loop), |
| nestable_task_runner_( |
| SchedulerMessageLoopDelegate::Create(message_loop)), |
| scheduler_(new RendererSchedulerImpl(nestable_task_runner_)), |
| default_task_runner_(scheduler_->DefaultTaskRunner()), |
| compositor_task_runner_(scheduler_->CompositorTaskRunner()), |
| loading_task_runner_(scheduler_->LoadingTaskRunner()), |
| idle_task_runner_(scheduler_->IdleTaskRunner()) { |
| scheduler_->SetTimeSourceForTesting(clock_); |
| } |
| ~RendererSchedulerImplTest() override {} |
| |
| void TearDown() override { |
| DCHECK(!mock_task_runner_.get() || !message_loop_.get()); |
| if (mock_task_runner_.get()) { |
| // Check that all tests stop posting tasks. |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| while (mock_task_runner_->RunUntilIdle()) { |
| } |
| } else { |
| message_loop_->RunUntilIdle(); |
| } |
| } |
| |
| void RunUntilIdle() { |
| // Only one of mock_task_runner_ or message_loop_ should be set. |
| DCHECK(!mock_task_runner_.get() || !message_loop_.get()); |
| if (mock_task_runner_.get()) |
| mock_task_runner_->RunUntilIdle(); |
| else |
| message_loop_->RunUntilIdle(); |
| } |
| |
| void DoMainFrame() { |
| scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->Now(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); |
| scheduler_->DidCommitFrameToCompositor(); |
| } |
| |
| void EnableIdleTasks() { DoMainFrame(); } |
| |
| Policy CurrentPolicy() { return scheduler_->current_policy_; } |
| |
| void EnsureUrgentPolicyUpdatePostedOnMainThread() { |
| base::AutoLock lock(scheduler_->incoming_signals_lock_); |
| scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread(FROM_HERE); |
| } |
| |
| void ScheduleDelayedPolicyUpdate(base::TimeDelta delay) { |
| scheduler_->delayed_update_policy_runner_.SetDeadline(FROM_HERE, delay, |
| clock_->Now()); |
| } |
| |
| // Helper for posting several tasks of specific types. |task_descriptor| is a |
| // string with space delimited task identifiers. The first letter of each |
| // task identifier specifies the task type: |
| // - 'D': Default task |
| // - 'C': Compositor task |
| // - 'L': Loading task |
| // - 'I': Idle task |
| void PostTestTasks(std::vector<std::string>* run_order, |
| const std::string& task_descriptor) { |
| std::istringstream stream(task_descriptor); |
| while (!stream.eof()) { |
| std::string task; |
| stream >> task; |
| switch (task[0]) { |
| case 'D': |
| default_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&AppendToVectorTestTask, run_order, task)); |
| break; |
| case 'C': |
| compositor_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&AppendToVectorTestTask, run_order, task)); |
| break; |
| case 'L': |
| loading_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&AppendToVectorTestTask, run_order, task)); |
| break; |
| case 'I': |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&AppendToVectorIdleTestTask, run_order, task)); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| } |
| |
| protected: |
| static base::TimeDelta priority_escalation_after_input_duration() { |
| return base::TimeDelta::FromMilliseconds( |
| RendererSchedulerImpl::kPriorityEscalationAfterInputMillis); |
| } |
| |
| static base::TimeDelta maximum_idle_period_duration() { |
| return base::TimeDelta::FromMilliseconds( |
| SchedulerHelper::kMaximumIdlePeriodMillis); |
| } |
| |
| scoped_refptr<cc::TestNowSource> clock_; |
| // Only one of mock_task_runner_ or message_loop_ will be set. |
| scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_; |
| scoped_ptr<base::MessageLoop> message_loop_; |
| |
| scoped_refptr<NestableSingleThreadTaskRunner> nestable_task_runner_; |
| scoped_ptr<RendererSchedulerImpl> scheduler_; |
| scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_; |
| scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_; |
| scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner_; |
| scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(RendererSchedulerImplTest); |
| }; |
| |
| TEST_F(RendererSchedulerImplTest, TestPostDefaultTask) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "D1 D2 D3 D4"); |
| |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("D1"), std::string("D2"), |
| std::string("D3"), std::string("D4"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestPostDefaultAndCompositor) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "D1 C1"); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, testing::Contains("D1")); |
| EXPECT_THAT(run_order, testing::Contains("C1")); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestRentrantTask) { |
| int count = 0; |
| std::vector<int> run_order; |
| default_task_runner_->PostTask( |
| FROM_HERE, base::Bind(AppendToVectorReentrantTask, default_task_runner_, |
| &run_order, &count, 5)); |
| RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, testing::ElementsAre(0, 1, 2, 3, 4)); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestPostIdleTask) { |
| int run_count = 0; |
| base::TimeTicks expected_deadline = |
| clock_->Now() + base::TimeDelta::FromMilliseconds(2300); |
| base::TimeTicks deadline_in_task; |
| |
| clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(100)); |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| |
| RunUntilIdle(); |
| EXPECT_EQ(0, run_count); // Shouldn't run yet as no WillBeginFrame. |
| |
| scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->Now(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); |
| RunUntilIdle(); |
| EXPECT_EQ(0, run_count); // Shouldn't run as no DidCommitFrameToCompositor. |
| |
| clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(1200)); |
| scheduler_->DidCommitFrameToCompositor(); |
| RunUntilIdle(); |
| EXPECT_EQ(0, run_count); // We missed the deadline. |
| |
| scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->Now(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); |
| clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(800)); |
| scheduler_->DidCommitFrameToCompositor(); |
| RunUntilIdle(); |
| EXPECT_EQ(1, run_count); |
| EXPECT_EQ(expected_deadline, deadline_in_task); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestRepostingIdleTask) { |
| int run_count = 0; |
| |
| max_idle_task_reposts = 2; |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&RepostingIdleTestTask, idle_task_runner_, &run_count)); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| EXPECT_EQ(1, run_count); |
| |
| // Reposted tasks shouldn't run until next idle period. |
| RunUntilIdle(); |
| EXPECT_EQ(1, run_count); |
| |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| EXPECT_EQ(2, run_count); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestIdleTaskExceedsDeadline) { |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| int run_count = 0; |
| |
| // Post two UpdateClockToDeadlineIdleTestTask tasks. |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, base::Bind(&UpdateClockToDeadlineIdleTestTask, clock_, |
| default_task_runner_, &run_count)); |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, base::Bind(&UpdateClockToDeadlineIdleTestTask, clock_, |
| default_task_runner_, &run_count)); |
| |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| // Only the first idle task should execute since it's used up the deadline. |
| EXPECT_EQ(1, run_count); |
| |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| // Second task should be run on the next idle period. |
| EXPECT_EQ(2, run_count); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestPostIdleTaskAfterWakeup) { |
| base::TimeTicks deadline_in_task; |
| int run_count = 0; |
| |
| idle_task_runner_->PostIdleTaskAfterWakeup( |
| FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| // Shouldn't run yet as no other task woke up the scheduler. |
| EXPECT_EQ(0, run_count); |
| |
| idle_task_runner_->PostIdleTaskAfterWakeup( |
| FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| // Another after wakeup idle task shouldn't wake the scheduler. |
| EXPECT_EQ(0, run_count); |
| |
| default_task_runner_->PostTask(FROM_HERE, base::Bind(&NullTask)); |
| |
| RunUntilIdle(); |
| EnableIdleTasks(); // Must start a new idle period before idle task runs. |
| RunUntilIdle(); |
| // Execution of default task queue task should trigger execution of idle task. |
| EXPECT_EQ(2, run_count); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestPostIdleTaskAfterWakeupWhileAwake) { |
| base::TimeTicks deadline_in_task; |
| int run_count = 0; |
| |
| idle_task_runner_->PostIdleTaskAfterWakeup( |
| FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| default_task_runner_->PostTask(FROM_HERE, base::Bind(&NullTask)); |
| |
| RunUntilIdle(); |
| EnableIdleTasks(); // Must start a new idle period before idle task runs. |
| RunUntilIdle(); |
| // Should run as the scheduler was already awakened by the normal task. |
| EXPECT_EQ(1, run_count); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestPostIdleTaskWakesAfterWakeupIdleTask) { |
| base::TimeTicks deadline_in_task; |
| int run_count = 0; |
| |
| idle_task_runner_->PostIdleTaskAfterWakeup( |
| FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| // Must start a new idle period before after-wakeup idle task runs. |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| // Normal idle task should wake up after-wakeup idle task. |
| EXPECT_EQ(2, run_count); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestDelayedEndIdlePeriodCanceled) { |
| int run_count = 0; |
| |
| base::TimeTicks deadline_in_task; |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| |
| // Trigger the beginning of an idle period for 1000ms. |
| scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->Now(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); |
| DoMainFrame(); |
| |
| // End the idle period early (after 500ms), and send a WillBeginFrame which |
| // specifies that the next idle period should end 1000ms from now. |
| clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(500)); |
| scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->Now(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); |
| |
| RunUntilIdle(); |
| EXPECT_EQ(0, run_count); // Not currently in an idle period. |
| |
| // Trigger the start of the idle period before the task to end the previous |
| // idle period has been triggered. |
| clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(400)); |
| scheduler_->DidCommitFrameToCompositor(); |
| |
| // Post a task which simulates running until after the previous end idle |
| // period delayed task was scheduled for |
| scheduler_->DefaultTaskRunner()->PostTask(FROM_HERE, base::Bind(NullTask)); |
| clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(300)); |
| |
| RunUntilIdle(); |
| EXPECT_EQ(1, run_count); // We should still be in the new idle period. |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestDefaultPolicy) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2"); |
| |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("L1"), std::string("D1"), |
| std::string("C1"), std::string("D2"), |
| std::string("C2"), std::string("I1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestCompositorPolicy) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2"); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("C2"), |
| std::string("D1"), std::string("D2"), |
| std::string("L1"), std::string("I1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestCompositorPolicy_DidAnimateForInput) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| scheduler_->DidAnimateForInputOnCompositorThread(); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("C2"), |
| std::string("D1"), std::string("D2"), |
| std::string("I1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestTouchstartPolicy) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "L1 D1 C1 D2 C2"); |
| |
| // Observation of touchstart should defer execution of loading tasks. |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("C2"), |
| std::string("D1"), std::string("D2"))); |
| |
| // Meta events like TapDown/FlingCancel shouldn't affect the priority. |
| run_order.clear(); |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingCancel)); |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureTapDown)); |
| RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| // Action events like ScrollBegin will kick us back into compositor priority, |
| // allowing servie of the loading and idle queues. |
| run_order.clear(); |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollBegin)); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, testing::ElementsAre(std::string("L1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| DidReceiveInputEventOnCompositorThread_IgnoresMouseMove_WhenMouseUp) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::MouseMove)); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| // Note compositor tasks are not prioritized. |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("D1"), std::string("C1"), |
| std::string("D2"), std::string("C2"), |
| std::string("I1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| DidReceiveInputEventOnCompositorThread_MouseMove_WhenMouseDown) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread(FakeInputEvent( |
| blink::WebInputEvent::MouseMove, blink::WebInputEvent::LeftButtonDown)); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| // Note compositor tasks are prioritized. |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("C2"), |
| std::string("D1"), std::string("D2"), |
| std::string("I1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| DidReceiveInputEventOnCompositorThread_MouseWheel) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::MouseWheel)); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| // Note compositor tasks are prioritized. |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("C2"), |
| std::string("D1"), std::string("D2"), |
| std::string("I1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| DidReceiveInputEventOnCompositorThread_IgnoresKeyboardEvents) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::KeyDown)); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| // Note compositor tasks are not prioritized. |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("D1"), std::string("C1"), |
| std::string("D2"), std::string("C2"), |
| std::string("I1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| TestCompositorPolicyDoesNotStarveDefaultTasks) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "D1 C1"); |
| |
| for (int i = 0; i < 20; i++) { |
| compositor_task_runner_->PostTask(FROM_HERE, base::Bind(&NullTask)); |
| } |
| PostTestTasks(&run_order, "C2"); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| RunUntilIdle(); |
| // Ensure that the default D1 task gets to run at some point before the final |
| // C2 compositor task. |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("D1"), |
| std::string("C2"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestCompositorPolicyEnds) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "D1 C1 D2 C2"); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| DoMainFrame(); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("C2"), |
| std::string("D1"), std::string("D2"))); |
| |
| run_order.clear(); |
| clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(1000)); |
| PostTestTasks(&run_order, "D1 C1 D2 C2"); |
| |
| // Compositor policy mode should have ended now that the clock has advanced. |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("D1"), std::string("C1"), |
| std::string("D2"), std::string("C2"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestTouchstartPolicyEndsAfterTimeout) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "L1 D1 C1 D2 C2"); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("C2"), |
| std::string("D1"), std::string("D2"))); |
| |
| run_order.clear(); |
| clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(1000)); |
| |
| // Don't post any compositor tasks to simulate a very long running event |
| // handler. |
| PostTestTasks(&run_order, "D1 D2"); |
| |
| // Touchstart policy mode should have ended now that the clock has advanced. |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("L1"), std::string("D1"), |
| std::string("D2"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| TestTouchstartPolicyEndsAfterConsecutiveTouchmoves) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "L1 D1 C1 D2 C2"); |
| |
| // Observation of touchstart should defer execution of idle and loading tasks. |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| DoMainFrame(); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("C2"), |
| std::string("D1"), std::string("D2"))); |
| |
| // Receiving the first touchmove will not affect scheduler priority. |
| run_order.clear(); |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove)); |
| DoMainFrame(); |
| RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| // Receiving the second touchmove will kick us back into compositor priority. |
| run_order.clear(); |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove)); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, testing::ElementsAre(std::string("L1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestIsHighPriorityWorkAnticipated) { |
| bool is_anticipated_before = false; |
| bool is_anticipated_after = false; |
| |
| bool simulate_input = false; |
| default_task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind(&AnticipationTestTask, scheduler_.get(), simulate_input, |
| &is_anticipated_before, &is_anticipated_after)); |
| RunUntilIdle(); |
| // In its default state, without input receipt, the scheduler should indicate |
| // that no high-priority is anticipated. |
| EXPECT_FALSE(is_anticipated_before); |
| EXPECT_FALSE(is_anticipated_after); |
| |
| simulate_input = true; |
| default_task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind(&AnticipationTestTask, scheduler_.get(), simulate_input, |
| &is_anticipated_before, &is_anticipated_after)); |
| RunUntilIdle(); |
| // When input is received, the scheduler should indicate that high-priority |
| // work is anticipated. |
| EXPECT_FALSE(is_anticipated_before); |
| EXPECT_TRUE(is_anticipated_after); |
| |
| clock_->AdvanceNow(priority_escalation_after_input_duration() * 2); |
| simulate_input = false; |
| default_task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind(&AnticipationTestTask, scheduler_.get(), simulate_input, |
| &is_anticipated_before, &is_anticipated_after)); |
| RunUntilIdle(); |
| // Without additional input, the scheduler should indicate that high-priority |
| // work is no longer anticipated. |
| EXPECT_FALSE(is_anticipated_before); |
| EXPECT_FALSE(is_anticipated_after); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestShouldYield) { |
| bool should_yield_before = false; |
| bool should_yield_after = false; |
| |
| default_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&PostingYieldingTestTask, scheduler_.get(), |
| default_task_runner_, false, &should_yield_before, |
| &should_yield_after)); |
| RunUntilIdle(); |
| // Posting to default runner shouldn't cause yielding. |
| EXPECT_FALSE(should_yield_before); |
| EXPECT_FALSE(should_yield_after); |
| |
| default_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&PostingYieldingTestTask, scheduler_.get(), |
| compositor_task_runner_, false, |
| &should_yield_before, &should_yield_after)); |
| RunUntilIdle(); |
| // Posting while not in compositor priority shouldn't cause yielding. |
| EXPECT_FALSE(should_yield_before); |
| EXPECT_FALSE(should_yield_after); |
| |
| default_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&PostingYieldingTestTask, scheduler_.get(), |
| compositor_task_runner_, true, &should_yield_before, |
| &should_yield_after)); |
| RunUntilIdle(); |
| // We should be able to switch to compositor priority mid-task. |
| EXPECT_FALSE(should_yield_before); |
| EXPECT_TRUE(should_yield_after); |
| |
| // Receiving a touchstart should immediately trigger yielding, even if |
| // there's no immediately pending work in the compositor queue. |
| EXPECT_FALSE(scheduler_->ShouldYieldForHighPriorityWork()); |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| EXPECT_TRUE(scheduler_->ShouldYieldForHighPriorityWork()); |
| RunUntilIdle(); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, SlowInputEvent) { |
| EXPECT_EQ(Policy::NORMAL, CurrentPolicy()); |
| |
| // An input event should bump us into input priority. |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::COMPOSITOR_PRIORITY, CurrentPolicy()); |
| |
| // Simulate the input event being queued for a very long time. The compositor |
| // task we post here represents the enqueued input task. |
| clock_->AdvanceNow(priority_escalation_after_input_duration() * 2); |
| compositor_task_runner_->PostTask(FROM_HERE, base::Bind(&NullTask)); |
| RunUntilIdle(); |
| |
| // Even though we exceeded the input priority escalation period, we should |
| // still be in compositor priority since the input remains queued. |
| EXPECT_EQ(Policy::COMPOSITOR_PRIORITY, CurrentPolicy()); |
| |
| // Simulate the input event triggering a composition. This should start the |
| // countdown for going back into normal policy. |
| DoMainFrame(); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::COMPOSITOR_PRIORITY, CurrentPolicy()); |
| |
| // After the escalation period ends we should go back into normal mode. |
| clock_->AdvanceNow(priority_escalation_after_input_duration() * 2); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::NORMAL, CurrentPolicy()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, SlowNoOpInputEvent) { |
| EXPECT_EQ(Policy::NORMAL, CurrentPolicy()); |
| |
| // An input event should bump us into input priority. |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::COMPOSITOR_PRIORITY, CurrentPolicy()); |
| |
| // Simulate the input event being queued for a very long time. The compositor |
| // task we post here represents the enqueued input task. |
| clock_->AdvanceNow(priority_escalation_after_input_duration() * 2); |
| compositor_task_runner_->PostTask(FROM_HERE, base::Bind(&NullTask)); |
| RunUntilIdle(); |
| |
| // Even though we exceeded the input priority escalation period, we should |
| // still be in compositor priority since the input remains queued. |
| EXPECT_EQ(Policy::COMPOSITOR_PRIORITY, CurrentPolicy()); |
| |
| // If we let the compositor queue drain, we should fall out of input |
| // priority. |
| clock_->AdvanceNow(priority_escalation_after_input_duration() * 2); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::NORMAL, CurrentPolicy()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, NoOpInputEvent) { |
| EXPECT_EQ(Policy::NORMAL, CurrentPolicy()); |
| |
| // An input event should bump us into input priority. |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::COMPOSITOR_PRIORITY, CurrentPolicy()); |
| |
| // If nothing else happens after this, we should drop out of compositor |
| // priority after the escalation period ends and stop polling. |
| clock_->AdvanceNow(priority_escalation_after_input_duration() * 2); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::NORMAL, CurrentPolicy()); |
| EXPECT_FALSE(mock_task_runner_->HasPendingTasks()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, NoOpInputEventExtendsEscalationPeriod) { |
| EXPECT_EQ(Policy::NORMAL, CurrentPolicy()); |
| |
| // Simulate one handled input event. |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollBegin)); |
| RunUntilIdle(); |
| DoMainFrame(); |
| EXPECT_EQ(Policy::COMPOSITOR_PRIORITY, CurrentPolicy()); |
| |
| // Send a no-op input event in the middle of the escalation period. |
| clock_->AdvanceNow(priority_escalation_after_input_duration() / 2); |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollUpdate)); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::COMPOSITOR_PRIORITY, CurrentPolicy()); |
| |
| // The escalation period should have been extended by the new input event. |
| clock_->AdvanceNow(3 * priority_escalation_after_input_duration() / 4); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::COMPOSITOR_PRIORITY, CurrentPolicy()); |
| |
| clock_->AdvanceNow(priority_escalation_after_input_duration() / 2); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::NORMAL, CurrentPolicy()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, InputArrivesAfterBeginFrame) { |
| EXPECT_EQ(Policy::NORMAL, CurrentPolicy()); |
| |
| cc::BeginFrameArgs args = cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->Now(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL); |
| clock_->AdvanceNow(priority_escalation_after_input_duration() / 2); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollBegin)); |
| |
| // Simulate a BeginMainFrame task from the past. |
| clock_->AdvanceNow(2 * priority_escalation_after_input_duration()); |
| scheduler_->WillBeginFrame(args); |
| scheduler_->DidCommitFrameToCompositor(); |
| |
| // This task represents the queued-up input event. |
| compositor_task_runner_->PostTask(FROM_HERE, base::Bind(&NullTask)); |
| |
| // Should remain in input priority policy since the input event hasn't been |
| // processed yet. |
| clock_->AdvanceNow(2 * priority_escalation_after_input_duration()); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::COMPOSITOR_PRIORITY, CurrentPolicy()); |
| |
| // Process the input event with a new BeginMainFrame. |
| DoMainFrame(); |
| clock_->AdvanceNow(2 * priority_escalation_after_input_duration()); |
| RunUntilIdle(); |
| EXPECT_EQ(Policy::NORMAL, CurrentPolicy()); |
| } |
| |
| class RendererSchedulerImplForTest : public RendererSchedulerImpl { |
| public: |
| RendererSchedulerImplForTest( |
| scoped_refptr<NestableSingleThreadTaskRunner> main_task_runner) |
| : RendererSchedulerImpl(main_task_runner), update_policy_count_(0) {} |
| |
| void UpdatePolicyLocked() override { |
| update_policy_count_++; |
| RendererSchedulerImpl::UpdatePolicyLocked(); |
| } |
| |
| int update_policy_count_; |
| }; |
| |
| TEST_F(RendererSchedulerImplTest, OnlyOnePendingUrgentPolicyUpdatey) { |
| RendererSchedulerImplForTest* mock_scheduler = |
| new RendererSchedulerImplForTest(nestable_task_runner_); |
| scheduler_.reset(mock_scheduler); |
| |
| EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| |
| RunUntilIdle(); |
| |
| EXPECT_EQ(1, mock_scheduler->update_policy_count_); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, OnePendingDelayedAndOneUrgentUpdatePolicy) { |
| RendererSchedulerImplForTest* mock_scheduler = |
| new RendererSchedulerImplForTest(nestable_task_runner_); |
| scheduler_.reset(mock_scheduler); |
| scheduler_->SetTimeSourceForTesting(clock_); |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| ScheduleDelayedPolicyUpdate(base::TimeDelta::FromMilliseconds(1)); |
| EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| |
| RunUntilIdle(); |
| |
| // We expect both the urgent and the delayed updates to run. |
| EXPECT_EQ(2, mock_scheduler->update_policy_count_); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, OneUrgentAndOnePendingDelayedUpdatePolicy) { |
| RendererSchedulerImplForTest* mock_scheduler = |
| new RendererSchedulerImplForTest(nestable_task_runner_); |
| scheduler_.reset(mock_scheduler); |
| scheduler_->SetTimeSourceForTesting(clock_); |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| ScheduleDelayedPolicyUpdate(base::TimeDelta::FromMilliseconds(1)); |
| |
| RunUntilIdle(); |
| |
| // We expect both the urgent and the delayed updates to run. |
| EXPECT_EQ(2, mock_scheduler->update_policy_count_); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, UpdatePolicyCountTriggeredByOneInputEvent) { |
| RendererSchedulerImplForTest* mock_scheduler = |
| new RendererSchedulerImplForTest(nestable_task_runner_); |
| scheduler_.reset(mock_scheduler); |
| scheduler_->SetTimeSourceForTesting(clock_); |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| |
| RunUntilIdle(); |
| |
| // We expect an urgent policy update followed by a delayed one 100ms later. |
| EXPECT_EQ(2, mock_scheduler->update_policy_count_); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, UpdatePolicyCountTriggeredByTwoInputEvents) { |
| RendererSchedulerImplForTest* mock_scheduler = |
| new RendererSchedulerImplForTest(nestable_task_runner_); |
| scheduler_.reset(mock_scheduler); |
| scheduler_->SetTimeSourceForTesting(clock_); |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove)); |
| |
| RunUntilIdle(); |
| |
| // We expect an urgent policy update followed by a delayed one 100ms later. |
| EXPECT_EQ(2, mock_scheduler->update_policy_count_); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, EnsureUpdatePolicyNotTriggeredTooOften) { |
| RendererSchedulerImplForTest* mock_scheduler = |
| new RendererSchedulerImplForTest(nestable_task_runner_); |
| scheduler_.reset(mock_scheduler); |
| scheduler_->SetTimeSourceForTesting(clock_); |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove)); |
| |
| // We expect the first call to IsHighPriorityWorkAnticipated to be called |
| // after recieving an input event (but before the UpdateTask was processed) to |
| // call UpdatePolicy. |
| EXPECT_EQ(0, mock_scheduler->update_policy_count_); |
| scheduler_->IsHighPriorityWorkAnticipated(); |
| EXPECT_EQ(1, mock_scheduler->update_policy_count_); |
| // Subsequent calls should not call UpdatePolicy. |
| scheduler_->IsHighPriorityWorkAnticipated(); |
| scheduler_->IsHighPriorityWorkAnticipated(); |
| scheduler_->IsHighPriorityWorkAnticipated(); |
| scheduler_->ShouldYieldForHighPriorityWork(); |
| scheduler_->ShouldYieldForHighPriorityWork(); |
| scheduler_->ShouldYieldForHighPriorityWork(); |
| scheduler_->ShouldYieldForHighPriorityWork(); |
| |
| EXPECT_EQ(1, mock_scheduler->update_policy_count_); |
| |
| RunUntilIdle(); |
| // We expect both the urgent and the delayed updates to run in addition to the |
| // earlier updated cause by IsHighPriorityWorkAnticipated. |
| EXPECT_EQ(3, mock_scheduler->update_policy_count_); |
| } |
| |
| class RendererSchedulerImplWithMessageLoopTest |
| : public RendererSchedulerImplTest { |
| public: |
| RendererSchedulerImplWithMessageLoopTest() |
| : RendererSchedulerImplTest(new base::MessageLoop()) {} |
| ~RendererSchedulerImplWithMessageLoopTest() override {} |
| |
| void PostFromNestedRunloop(std::vector< |
| std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>>* tasks) { |
| base::MessageLoop::ScopedNestableTaskAllower allow(message_loop_.get()); |
| for (std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>& pair : *tasks) { |
| if (pair.second) { |
| idle_task_runner_->PostIdleTask(FROM_HERE, pair.first); |
| } else { |
| idle_task_runner_->PostNonNestableIdleTask(FROM_HERE, pair.first); |
| } |
| } |
| EnableIdleTasks(); |
| message_loop_->RunUntilIdle(); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(RendererSchedulerImplWithMessageLoopTest); |
| }; |
| |
| TEST_F(RendererSchedulerImplWithMessageLoopTest, |
| NonNestableIdleTaskDoesntExecuteInNestedLoop) { |
| std::vector<std::string> order; |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&AppendToVectorIdleTestTask, &order, std::string("1"))); |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&AppendToVectorIdleTestTask, &order, std::string("2"))); |
| |
| std::vector<std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>> |
| tasks_to_post_from_nested_loop; |
| tasks_to_post_from_nested_loop.push_back(std::make_pair( |
| base::Bind(&AppendToVectorIdleTestTask, &order, std::string("3")), |
| false)); |
| tasks_to_post_from_nested_loop.push_back(std::make_pair( |
| base::Bind(&AppendToVectorIdleTestTask, &order, std::string("4")), true)); |
| tasks_to_post_from_nested_loop.push_back(std::make_pair( |
| base::Bind(&AppendToVectorIdleTestTask, &order, std::string("5")), true)); |
| |
| default_task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind( |
| &RendererSchedulerImplWithMessageLoopTest::PostFromNestedRunloop, |
| base::Unretained(this), |
| base::Unretained(&tasks_to_post_from_nested_loop))); |
| |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| // Note we expect task 3 to run last because it's non-nestable. |
| EXPECT_THAT(order, testing::ElementsAre(std::string("1"), std::string("2"), |
| std::string("4"), std::string("5"), |
| std::string("3"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestLongIdlePeriod) { |
| base::TimeTicks expected_deadline = |
| clock_->Now() + maximum_idle_period_duration(); |
| base::TimeTicks deadline_in_task; |
| int run_count = 0; |
| |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| |
| RunUntilIdle(); |
| EXPECT_EQ(0, run_count); // Shouldn't run yet as no idle period. |
| |
| scheduler_->BeginFrameNotExpectedSoon(); |
| RunUntilIdle(); |
| EXPECT_EQ(1, run_count); // Should have run in a long idle time. |
| EXPECT_EQ(expected_deadline, deadline_in_task); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestLongIdlePeriodWithPendingDelayedTask) { |
| base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(30); |
| base::TimeTicks expected_deadline = clock_->Now() + pending_task_delay; |
| base::TimeTicks deadline_in_task; |
| int run_count = 0; |
| |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| default_task_runner_->PostDelayedTask( |
| FROM_HERE, base::Bind(&NullTask), pending_task_delay); |
| |
| scheduler_->BeginFrameNotExpectedSoon(); |
| RunUntilIdle(); |
| EXPECT_EQ(1, run_count); // Should have run in a long idle time. |
| EXPECT_EQ(expected_deadline, deadline_in_task); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| TestLongIdlePeriodWithLatePendingDelayedTask) { |
| base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks deadline_in_task; |
| int run_count = 0; |
| |
| default_task_runner_->PostDelayedTask( |
| FROM_HERE, base::Bind(&NullTask), pending_task_delay); |
| |
| // Advance clock until after delayed task was meant to be run. |
| clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(20)); |
| |
| // Post an idle task and BeginFrameNotExpectedSoon to initiate a long idle |
| // period. Since there is a late pending delayed task this shouldn't actually |
| // start an idle period. |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| scheduler_->BeginFrameNotExpectedSoon(); |
| RunUntilIdle(); |
| EXPECT_EQ(0, run_count); |
| |
| // After the delayed task has been run we should trigger an idle period. |
| clock_->AdvanceNow(maximum_idle_period_duration()); |
| RunUntilIdle(); |
| EXPECT_EQ(1, run_count); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestLongIdlePeriodRepeating) { |
| int run_count = 0; |
| |
| max_idle_task_reposts = 3; |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&RepostingIdleTestTask, idle_task_runner_, &run_count)); |
| |
| scheduler_->BeginFrameNotExpectedSoon(); |
| RunUntilIdle(); |
| EXPECT_EQ(1, run_count); // Should only run once per idle period. |
| |
| // Advance time to start of next long idle period and check task reposted task |
| // gets run. |
| clock_->AdvanceNow(maximum_idle_period_duration()); |
| RunUntilIdle(); |
| EXPECT_EQ(2, run_count); |
| |
| // Advance time to start of next long idle period then end idle period with a |
| // new BeginMainFrame and check idle task doesn't run. |
| clock_->AdvanceNow(maximum_idle_period_duration()); |
| scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->Now(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); |
| RunUntilIdle(); |
| EXPECT_EQ(2, run_count); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestLongIdlePeriodDoesNotWakeScheduler) { |
| base::TimeTicks deadline_in_task; |
| int run_count = 0; |
| |
| // Start a long idle period and get the time it should end. |
| scheduler_->BeginFrameNotExpectedSoon(); |
| // The scheduler should not run the initiate_next_long_idle_period task if |
| // there are no idle tasks and no other task woke up the scheduler, thus |
| // the idle period deadline shouldn't update at the end of the current long |
| // idle period. |
| base::TimeTicks idle_period_deadline = |
| scheduler_->CurrentIdleTaskDeadlineForTesting(); |
| clock_->AdvanceNow(maximum_idle_period_duration()); |
| RunUntilIdle(); |
| |
| base::TimeTicks new_idle_period_deadline = |
| scheduler_->CurrentIdleTaskDeadlineForTesting(); |
| EXPECT_EQ(idle_period_deadline, new_idle_period_deadline); |
| |
| // Posting a after-wakeup idle task also shouldn't wake the scheduler or |
| // initiate the next long idle period. |
| idle_task_runner_->PostIdleTaskAfterWakeup( |
| FROM_HERE, |
| base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| RunUntilIdle(); |
| new_idle_period_deadline = scheduler_->CurrentIdleTaskDeadlineForTesting(); |
| EXPECT_EQ(idle_period_deadline, new_idle_period_deadline); |
| EXPECT_EQ(0, run_count); |
| |
| // Running a normal task should initiate a new long idle period though. |
| default_task_runner_->PostTask(FROM_HERE, base::Bind(&NullTask)); |
| RunUntilIdle(); |
| new_idle_period_deadline = scheduler_->CurrentIdleTaskDeadlineForTesting(); |
| EXPECT_EQ(idle_period_deadline + maximum_idle_period_duration(), |
| new_idle_period_deadline); |
| |
| EXPECT_EQ(1, run_count); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestLongIdlePeriodInTouchStartPolicy) { |
| base::TimeTicks deadline_in_task; |
| int run_count = 0; |
| |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&IdleTestTask, &run_count, &deadline_in_task)); |
| |
| // Observation of touchstart should defer the start of the long idle period. |
| scheduler_->DidReceiveInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| scheduler_->BeginFrameNotExpectedSoon(); |
| RunUntilIdle(); |
| EXPECT_EQ(0, run_count); |
| |
| // The long idle period should start after the touchstart policy has finished. |
| clock_->AdvanceNow(priority_escalation_after_input_duration()); |
| RunUntilIdle(); |
| EXPECT_EQ(1, run_count); |
| } |
| |
| void TestCanExceedIdleDeadlineIfRequiredTask(RendererScheduler* scheduler, |
| bool* can_exceed_idle_deadline_out, |
| int* run_count, |
| base::TimeTicks deadline) { |
| *can_exceed_idle_deadline_out = scheduler->CanExceedIdleDeadlineIfRequired(); |
| (*run_count)++; |
| } |
| |
| TEST_F(RendererSchedulerImplTest, CanExceedIdleDeadlineIfRequired) { |
| int run_count = 0; |
| bool can_exceed_idle_deadline = false; |
| |
| // Should return false if not in an idle period. |
| EXPECT_FALSE(scheduler_->CanExceedIdleDeadlineIfRequired()); |
| |
| // Should return false for short idle periods. |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&TestCanExceedIdleDeadlineIfRequiredTask, scheduler_.get(), |
| &can_exceed_idle_deadline, &run_count)); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| EXPECT_EQ(1, run_count); |
| EXPECT_FALSE(can_exceed_idle_deadline); |
| |
| // Should return false for a long idle period which is shortened due to a |
| // pending delayed task. |
| default_task_runner_->PostDelayedTask(FROM_HERE, base::Bind(&NullTask), |
| base::TimeDelta::FromMilliseconds(10)); |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&TestCanExceedIdleDeadlineIfRequiredTask, scheduler_.get(), |
| &can_exceed_idle_deadline, &run_count)); |
| scheduler_->BeginFrameNotExpectedSoon(); |
| RunUntilIdle(); |
| EXPECT_EQ(2, run_count); |
| EXPECT_FALSE(can_exceed_idle_deadline); |
| |
| // Next long idle period will be for the maximum time, so |
| // CanExceedIdleDeadlineIfRequired should return true. |
| clock_->AdvanceNow(maximum_idle_period_duration()); |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&TestCanExceedIdleDeadlineIfRequiredTask, scheduler_.get(), |
| &can_exceed_idle_deadline, &run_count)); |
| RunUntilIdle(); |
| EXPECT_EQ(3, run_count); |
| EXPECT_TRUE(can_exceed_idle_deadline); |
| |
| // Next long idle period will be for the maximum time, so |
| // CanExceedIdleDeadlineIfRequired should return true. |
| scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->Now(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); |
| EXPECT_FALSE(scheduler_->CanExceedIdleDeadlineIfRequired()); |
| } |
| |
| } // namespace content |