| // 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 "components/scheduler/renderer/renderer_scheduler_impl.h" |
| |
| #include "base/callback.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "cc/output/begin_frame_args.h" |
| #include "cc/test/ordered_simple_task_runner.h" |
| #include "components/scheduler/base/test_time_source.h" |
| #include "components/scheduler/child/scheduler_tqm_delegate_for_test.h" |
| #include "components/scheduler/child/scheduler_tqm_delegate_impl.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace scheduler { |
| |
| 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 RepostingUpdateClockIdleTestTask( |
| SingleThreadIdleTaskRunner* idle_task_runner, |
| int* run_count, |
| base::SimpleTestTickClock* clock, |
| base::TimeDelta advance_time, |
| std::vector<base::TimeTicks>* deadlines, |
| base::TimeTicks deadline) { |
| if ((*run_count + 1) < max_idle_task_reposts) { |
| idle_task_runner->PostIdleTask( |
| FROM_HERE, base::Bind(&RepostingUpdateClockIdleTestTask, |
| base::Unretained(idle_task_runner), run_count, |
| clock, advance_time, deadlines)); |
| } |
| deadlines->push_back(deadline); |
| (*run_count)++; |
| clock->Advance(advance_time); |
| } |
| |
| void WillBeginFrameIdleTask(RendererScheduler* scheduler, |
| base::SimpleTestTickClock* clock, |
| base::TimeTicks deadline) { |
| scheduler->WillBeginFrame(cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock->NowTicks(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); |
| } |
| |
| void UpdateClockToDeadlineIdleTestTask(base::SimpleTestTickClock* clock, |
| int* run_count, |
| base::TimeTicks deadline) { |
| clock->Advance(deadline - clock->NowTicks()); |
| (*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->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| } |
| *should_yield_after = scheduler->ShouldYieldForHighPriorityWork(); |
| } |
| |
| enum class SimulateInputType { |
| None, |
| TouchStart, |
| TouchEnd, |
| GestureScrollBegin, |
| GestureScrollEnd |
| }; |
| |
| void AnticipationTestTask(RendererSchedulerImpl* scheduler, |
| SimulateInputType simulate_input, |
| bool* is_anticipated_before, |
| bool* is_anticipated_after) { |
| *is_anticipated_before = scheduler->IsHighPriorityWorkAnticipated(); |
| switch (simulate_input) { |
| case SimulateInputType::None: |
| break; |
| |
| case SimulateInputType::TouchStart: |
| scheduler->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| break; |
| |
| case SimulateInputType::TouchEnd: |
| scheduler->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchEnd), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| break; |
| |
| case SimulateInputType::GestureScrollBegin: |
| scheduler->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollBegin), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| break; |
| |
| case SimulateInputType::GestureScrollEnd: |
| scheduler->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollEnd), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| break; |
| } |
| *is_anticipated_after = scheduler->IsHighPriorityWorkAnticipated(); |
| } |
| |
| }; // namespace |
| |
| class RendererSchedulerImplForTest : public RendererSchedulerImpl { |
| public: |
| using RendererSchedulerImpl::OnIdlePeriodEnded; |
| using RendererSchedulerImpl::OnIdlePeriodStarted; |
| |
| RendererSchedulerImplForTest( |
| scoped_refptr<SchedulerTqmDelegate> main_task_runner) |
| : RendererSchedulerImpl(main_task_runner), update_policy_count_(0) {} |
| |
| void UpdatePolicyLocked(UpdateType update_type) override { |
| update_policy_count_++; |
| RendererSchedulerImpl::UpdatePolicyLocked(update_type); |
| |
| std::string use_case = |
| RendererScheduler::UseCaseToString(MainThreadOnly().current_use_case); |
| if (MainThreadOnly().touchstart_expected_soon) { |
| use_cases_.push_back(use_case + " scroll expected"); |
| } else { |
| use_cases_.push_back(use_case); |
| } |
| } |
| |
| void EnsureUrgentPolicyUpdatePostedOnMainThread() { |
| base::AutoLock lock(any_thread_lock_); |
| RendererSchedulerImpl::EnsureUrgentPolicyUpdatePostedOnMainThread( |
| FROM_HERE); |
| } |
| |
| void ScheduleDelayedPolicyUpdate(base::TimeTicks now, base::TimeDelta delay) { |
| delayed_update_policy_runner_.SetDeadline(FROM_HERE, delay, now); |
| } |
| |
| bool BeginMainFrameOnCriticalPath() { |
| base::AutoLock lock(any_thread_lock_); |
| return AnyThread().begin_main_frame_on_critical_path; |
| } |
| |
| int update_policy_count_; |
| std::vector<std::string> use_cases_; |
| }; |
| |
| // Lets gtest print human readable Policy values. |
| ::std::ostream& operator<<(::std::ostream& os, |
| const RendererScheduler::UseCase& use_case) { |
| return os << RendererScheduler::UseCaseToString(use_case); |
| } |
| |
| class RendererSchedulerImplTest : public testing::Test { |
| public: |
| using UseCase = RendererSchedulerImpl::UseCase; |
| |
| RendererSchedulerImplTest() : clock_(new base::SimpleTestTickClock()) { |
| clock_->Advance(base::TimeDelta::FromMicroseconds(5000)); |
| } |
| |
| RendererSchedulerImplTest(base::MessageLoop* message_loop) |
| : clock_(new base::SimpleTestTickClock()), message_loop_(message_loop) { |
| clock_->Advance(base::TimeDelta::FromMicroseconds(5000)); |
| } |
| |
| ~RendererSchedulerImplTest() override {} |
| |
| void SetUp() override { |
| if (message_loop_) { |
| main_task_runner_ = SchedulerTqmDelegateImpl::Create( |
| message_loop_.get(), |
| make_scoped_ptr(new TestTimeSource(clock_.get()))); |
| } else { |
| mock_task_runner_ = make_scoped_refptr( |
| new cc::OrderedSimpleTaskRunner(clock_.get(), false)); |
| main_task_runner_ = SchedulerTqmDelegateForTest::Create( |
| mock_task_runner_, make_scoped_ptr(new TestTimeSource(clock_.get()))); |
| } |
| Initialize( |
| make_scoped_ptr(new RendererSchedulerImplForTest(main_task_runner_))); |
| } |
| |
| void Initialize(scoped_ptr<RendererSchedulerImplForTest> scheduler) { |
| scheduler_ = scheduler.Pass(); |
| default_task_runner_ = scheduler_->DefaultTaskRunner(); |
| compositor_task_runner_ = scheduler_->CompositorTaskRunner(); |
| loading_task_runner_ = scheduler_->LoadingTaskRunner(); |
| idle_task_runner_ = scheduler_->IdleTaskRunner(); |
| timer_task_runner_ = scheduler_->TimerTaskRunner(); |
| } |
| |
| void TearDown() override { |
| DCHECK(!mock_task_runner_.get() || !message_loop_.get()); |
| scheduler_->Shutdown(); |
| 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(); |
| } |
| scheduler_.reset(); |
| } |
| |
| 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() { |
| cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); |
| begin_frame_args.on_critical_path = false; |
| scheduler_->WillBeginFrame(begin_frame_args); |
| scheduler_->DidCommitFrameToCompositor(); |
| } |
| |
| void ForceMainThreadScrollingUseCase() { |
| cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); |
| begin_frame_args.on_critical_path = true; |
| scheduler_->WillBeginFrame(begin_frame_args); |
| } |
| |
| void ForceTouchStartToBeExpectedSoon() { |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollEnd), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| clock_->Advance(priority_escalation_after_input_duration() * 2); |
| scheduler_->ForceUpdatePolicy(); |
| } |
| |
| void SimulateExpensiveTasks( |
| const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { |
| // RunUntilIdle won't actually run all of the SimpleTestTickClock::Advance |
| // tasks unless we set AutoAdvanceNow to true :/ |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| // Simulate a bunch of expensive tasks |
| for (int i = 0; i < 10; i++) { |
| task_runner->PostTask(FROM_HERE, |
| base::Bind(&base::SimpleTestTickClock::Advance, |
| base::Unretained(clock_.get()), |
| base::TimeDelta::FromMilliseconds(500))); |
| } |
| |
| RunUntilIdle(); |
| |
| // Switch back to not auto-advancing because we want to be in control of |
| // when time advances. |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(false); |
| } |
| |
| void WillBeginMainThreadGestureFrame() { |
| scheduler_->DidAnimateForInputOnCompositorThread(); |
| cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); |
| begin_frame_args.on_critical_path = true; |
| scheduler_->WillBeginFrame(begin_frame_args); |
| } |
| |
| void SimulateMainThreadGestureCompositorTask( |
| base::TimeDelta begin_main_frame_duration) { |
| WillBeginMainThreadGestureFrame(); |
| clock_->Advance(begin_main_frame_duration); |
| scheduler_->DidCommitFrameToCompositor(); |
| } |
| |
| void SimulateTimerTask(base::TimeDelta duration) { |
| clock_->Advance(duration); |
| simulate_timer_task_ran_ = true; |
| } |
| |
| void EnableIdleTasks() { DoMainFrame(); } |
| |
| UseCase CurrentUseCase() { |
| return scheduler_->MainThreadOnly().current_use_case; |
| } |
| |
| UseCase ForceUpdatePolicyAndGetCurrentUseCase() { |
| scheduler_->ForceUpdatePolicy(); |
| return scheduler_->MainThreadOnly().current_use_case; |
| } |
| |
| bool TouchStartExpectedSoon() { |
| return scheduler_->MainThreadOnly().touchstart_expected_soon; |
| } |
| |
| bool HaveSeenABeginMainframe() { |
| return scheduler_->MainThreadOnly().have_seen_a_begin_main_frame; |
| } |
| |
| bool LoadingTasksSeemExpensive() { |
| return scheduler_->MainThreadOnly().loading_tasks_seem_expensive; |
| } |
| |
| bool TimerTasksSeemExpensive() { |
| return scheduler_->MainThreadOnly().timer_tasks_seem_expensive; |
| } |
| |
| base::TimeTicks EstimatedNextFrameBegin() { |
| return scheduler_->MainThreadOnly().estimated_next_frame_begin; |
| } |
| |
| // 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 |
| // - 'T': Timer 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; |
| case 'T': |
| timer_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&AppendToVectorTestTask, run_order, task)); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| } |
| |
| protected: |
| static base::TimeDelta priority_escalation_after_input_duration() { |
| return base::TimeDelta::FromMilliseconds( |
| UserModel::kGestureEstimationLimitMillis); |
| } |
| |
| static base::TimeDelta subsequent_input_expected_after_input_duration() { |
| return base::TimeDelta::FromMilliseconds( |
| UserModel::kExpectSubsequentGestureMillis); |
| } |
| |
| static base::TimeDelta maximum_idle_period_duration() { |
| return base::TimeDelta::FromMilliseconds( |
| IdleHelper::kMaximumIdlePeriodMillis); |
| } |
| |
| static base::TimeDelta end_idle_when_hidden_delay() { |
| return base::TimeDelta::FromMilliseconds( |
| RendererSchedulerImpl::kEndIdleWhenHiddenDelayMillis); |
| } |
| |
| static base::TimeDelta idle_period_starvation_threshold() { |
| return base::TimeDelta::FromMilliseconds( |
| RendererSchedulerImpl::kIdlePeriodStarvationThresholdMillis); |
| } |
| |
| static base::TimeDelta suspend_timers_when_backgrounded_delay() { |
| return base::TimeDelta::FromMilliseconds( |
| RendererSchedulerImpl::kSuspendTimersWhenBackgroundedDelayMillis); |
| } |
| |
| template <typename E> |
| static void CallForEachEnumValue(E first, |
| E last, |
| const char* (*function)(E)) { |
| for (E val = first; val < last; |
| val = static_cast<E>(static_cast<int>(val) + 1)) { |
| (*function)(val); |
| } |
| } |
| |
| static void CheckAllUseCaseToString() { |
| CallForEachEnumValue<RendererSchedulerImpl::UseCase>( |
| RendererSchedulerImpl::UseCase::FIRST_USE_CASE, |
| RendererSchedulerImpl::UseCase::USE_CASE_COUNT, |
| &RendererSchedulerImpl::UseCaseToString); |
| } |
| |
| scoped_ptr<base::SimpleTestTickClock> 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<SchedulerTqmDelegate> main_task_runner_; |
| scoped_ptr<RendererSchedulerImplForTest> 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_; |
| scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner_; |
| bool simulate_timer_task_ran_; |
| |
| 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_->NowTicks() + base::TimeDelta::FromMilliseconds(2300); |
| base::TimeTicks deadline_in_task; |
| |
| clock_->Advance(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_->NowTicks(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); |
| RunUntilIdle(); |
| EXPECT_EQ(0, run_count); // Shouldn't run as no DidCommitFrameToCompositor. |
| |
| clock_->Advance(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_->NowTicks(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); |
| clock_->Advance(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_.get(), &run_count)); |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&UpdateClockToDeadlineIdleTestTask, clock_.get(), &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_->NowTicks(), 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_->Advance(base::TimeDelta::FromMilliseconds(500)); |
| scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->NowTicks(), 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_->Advance(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_->Advance(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"))); |
| EXPECT_EQ(RendererScheduler::UseCase::NONE, CurrentUseCase()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestCompositorPolicy_CompositorHandlesInput) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2"); |
| |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| 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"))); |
| EXPECT_EQ(RendererScheduler::UseCase::COMPOSITOR_GESTURE, CurrentUseCase()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestCompositorPolicy_MainThreadHandlesInput) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2"); |
| |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| 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"))); |
| EXPECT_EQ(RendererScheduler::UseCase::COMPOSITOR_GESTURE, CurrentUseCase()); |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestCompositorPolicy_DidAnimateForInput) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); |
| scheduler_->DidAnimateForInputOnCompositorThread(); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("D1"), std::string("C1"), |
| std::string("D2"), std::string("C2"), |
| std::string("I1"))); |
| EXPECT_EQ(RendererScheduler::UseCase::COMPOSITOR_GESTURE, CurrentUseCase()); |
| } |
| |
| TEST_F( |
| RendererSchedulerImplTest, |
| TestCompositorPolicy_ExpensiveTimersDontRunWhenMainThreadOnCriticalPath) { |
| std::vector<std::string> run_order; |
| |
| SimulateExpensiveTasks(timer_task_runner_); |
| |
| // Timers should now be disabled during main thread user user interactions. |
| PostTestTasks(&run_order, "C1 T1"); |
| |
| // Trigger main_thread_gesture UseCase |
| WillBeginMainThreadGestureFrame(); |
| RunUntilIdle(); |
| EXPECT_EQ(RendererScheduler::UseCase::MAIN_THREAD_GESTURE, CurrentUseCase()); |
| |
| EXPECT_THAT(run_order, testing::ElementsAre(std::string("C1"))); |
| clock_->Advance(subsequent_input_expected_after_input_duration() * 2); |
| |
| run_order.clear(); |
| RunUntilIdle(); |
| EXPECT_EQ(RendererScheduler::UseCase::NONE, CurrentUseCase()); |
| EXPECT_THAT(run_order, testing::ElementsAre(std::string("T1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, Navigation_ResetsTaskCostEstimations) { |
| std::vector<std::string> run_order; |
| |
| SimulateExpensiveTasks(timer_task_runner_); |
| scheduler_->OnNavigationStarted(); |
| PostTestTasks(&run_order, "C1 T1"); |
| |
| WillBeginMainThreadGestureFrame(); |
| scheduler_->DidCommitFrameToCompositor(); // Starts Idle Period |
| RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("T1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| TestCompositorPolicy_TimersAlwaysRun_MainThreadNotOnCriticalPath) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "C1 T1"); |
| |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); |
| scheduler_->DidAnimateForInputOnCompositorThread(); |
| cc::BeginFrameArgs begin_frame_args1 = cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL); |
| begin_frame_args1.on_critical_path = false; |
| scheduler_->WillBeginFrame(begin_frame_args1); |
| scheduler_->DidCommitFrameToCompositor(); // Starts Idle Period |
| RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("T1"))); |
| |
| // End the idle period. |
| clock_->Advance(base::TimeDelta::FromMilliseconds(500)); |
| scheduler_->DidAnimateForInputOnCompositorThread(); |
| cc::BeginFrameArgs begin_frame_args2 = cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL); |
| begin_frame_args2.on_critical_path = false; |
| scheduler_->WillBeginFrame(begin_frame_args2); |
| |
| run_order.clear(); |
| PostTestTasks(&run_order, "C1 T1"); |
| RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("T1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestTouchstartPolicy_Compositor) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "L1 D1 C1 D2 C2 T1 T2"); |
| |
| // Observation of touchstart should defer execution of timer, idle and loading |
| // tasks. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("C2"), |
| std::string("D1"), std::string("D2"))); |
| |
| // Animation or meta events like TapDown/FlingCancel shouldn't affect the |
| // priority. |
| run_order.clear(); |
| scheduler_->DidAnimateForInputOnCompositorThread(); |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingCancel), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureTapDown), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| // Action events like ScrollBegin will kick us back into compositor priority, |
| // allowing service of the timer, loading and idle queues. |
| run_order.clear(); |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollBegin), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("L1"), std::string("T1"), |
| std::string("T2"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestTouchstartPolicy_MainThread) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "L1 D1 C1 D2 C2 T1 T2"); |
| |
| // Observation of touchstart should defer execution of timer, idle and loading |
| // tasks. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| EnableIdleTasks(); |
| 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_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingCancel), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingCancel)); |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureTapDown), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::GestureTapDown)); |
| RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| // Action events like ScrollBegin will kick us back into compositor priority, |
| // allowing service of the timer, loading and idle queues. |
| run_order.clear(); |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollBegin), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollBegin)); |
| RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("L1"), std::string("T1"), |
| std::string("T2"))); |
| } |
| |
| // TODO(alexclarke): Reenable once we've reinstaed the Loading UseCase. |
| TEST_F(RendererSchedulerImplTest, DISABLED_LoadingUseCase) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 T1 L1 D2 C2 T2 L2"); |
| |
| scheduler_->OnNavigationStarted(); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| |
| // In loading policy, loading tasks are prioritized other others. |
| std::string loading_policy_expected[] = { |
| std::string("D1"), std::string("L1"), std::string("D2"), |
| std::string("L2"), std::string("C1"), std::string("T1"), |
| std::string("C2"), std::string("T2"), std::string("I1")}; |
| EXPECT_THAT(run_order, testing::ElementsAreArray(loading_policy_expected)); |
| EXPECT_EQ(RendererScheduler::UseCase::LOADING, CurrentUseCase()); |
| |
| // Advance 15s and try again, the loading policy should have ended and the |
| // task order should return to the NONE use case where loading tasks are no |
| // longer prioritized. |
| clock_->Advance(base::TimeDelta::FromMilliseconds(150000)); |
| run_order.clear(); |
| PostTestTasks(&run_order, "I1 D1 C1 T1 L1 D2 C2 T2 L2"); |
| EnableIdleTasks(); |
| RunUntilIdle(); |
| |
| std::string default_order_expected[] = { |
| std::string("D1"), std::string("C1"), std::string("T1"), |
| std::string("L1"), std::string("D2"), std::string("C2"), |
| std::string("T2"), std::string("L2"), std::string("I1")}; |
| EXPECT_THAT(run_order, testing::ElementsAreArray(default_order_expected)); |
| EXPECT_EQ(RendererScheduler::UseCase::NONE, CurrentUseCase()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| EventConsumedOnCompositorThread_IgnoresMouseMove_WhenMouseUp) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| ForceMainThreadScrollingUseCase(); |
| scheduler_->DidCommitFrameToCompositor(); // Enable Idle tasks. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::MouseMove), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| RunUntilIdle(); |
| // Note compositor tasks are not prioritized. |
| EXPECT_EQ(RendererScheduler::UseCase::NONE, CurrentUseCase()); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("D1"), std::string("C1"), |
| std::string("D2"), std::string("C2"), |
| std::string("I1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| EventForwardedToMainThread_IgnoresMouseMove_WhenMouseUp) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| ForceMainThreadScrollingUseCase(); |
| scheduler_->DidCommitFrameToCompositor(); // Enable Idle tasks. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::MouseMove), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| RunUntilIdle(); |
| // Note compositor tasks are not prioritized. |
| EXPECT_EQ(RendererScheduler::UseCase::NONE, CurrentUseCase()); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("D1"), std::string("C1"), |
| std::string("D2"), std::string("C2"), |
| std::string("I1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| EventConsumedOnCompositorThread_MouseMove_WhenMouseDown) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| ForceMainThreadScrollingUseCase(); |
| scheduler_->DidCommitFrameToCompositor(); // Enable Idle tasks. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::MouseMove, |
| blink::WebInputEvent::LeftButtonDown), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| 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, |
| EventForwardedToMainThread_MouseMove_WhenMouseDown) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| ForceMainThreadScrollingUseCase(); |
| scheduler_->DidCommitFrameToCompositor(); // Enable Idle tasks. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::MouseMove, |
| blink::WebInputEvent::LeftButtonDown), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| 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"))); |
| scheduler_->DidHandleInputEventOnMainThread(FakeInputEvent( |
| blink::WebInputEvent::MouseMove, blink::WebInputEvent::LeftButtonDown)); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, EventConsumedOnCompositorThread_MouseWheel) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| ForceMainThreadScrollingUseCase(); |
| scheduler_->DidCommitFrameToCompositor(); // Enable Idle tasks. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::MouseWheel), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| 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, EventForwardedToMainThread_MouseWheel) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| ForceMainThreadScrollingUseCase(); |
| scheduler_->DidCommitFrameToCompositor(); // Enable Idle tasks. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::MouseWheel), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| 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"))); |
| EXPECT_EQ(RendererScheduler::UseCase::MAIN_THREAD_GESTURE, CurrentUseCase()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| EventConsumedOnCompositorThread_IgnoresKeyboardEvents) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| ForceMainThreadScrollingUseCase(); |
| scheduler_->DidCommitFrameToCompositor(); // Enable Idle tasks. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::KeyDown), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| 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"))); |
| EXPECT_EQ(RendererScheduler::UseCase::NONE, CurrentUseCase()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| EventForwardedToMainThread_IgnoresKeyboardEvents) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); |
| |
| ForceMainThreadScrollingUseCase(); |
| scheduler_->DidCommitFrameToCompositor(); // Enable Idle tasks. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::KeyDown), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| 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"))); |
| EXPECT_EQ(RendererScheduler::UseCase::NONE, CurrentUseCase()); |
| // Note compositor tasks are not prioritized. |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::KeyDown)); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| TestMainthreadScrollingUseCaseDoesNotStarveDefaultTasks) { |
| ForceMainThreadScrollingUseCase(); |
| scheduler_->DidCommitFrameToCompositor(); // Enable Idle tasks. |
| |
| 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_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| 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_CompositorHandlesInput) { |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| EXPECT_EQ(UseCase::COMPOSITOR_GESTURE, |
| ForceUpdatePolicyAndGetCurrentUseCase()); |
| |
| clock_->Advance(base::TimeDelta::FromMilliseconds(1000)); |
| EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| TestCompositorPolicyEnds_MainThreadHandlesInput) { |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| EXPECT_EQ(UseCase::COMPOSITOR_GESTURE, |
| ForceUpdatePolicyAndGetCurrentUseCase()); |
| |
| clock_->Advance(base::TimeDelta::FromMilliseconds(1000)); |
| EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestTouchstartPolicyEndsAfterTimeout) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "L1 D1 C1 D2 C2"); |
| |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("C2"), |
| std::string("D1"), std::string("D2"))); |
| |
| run_order.clear(); |
| clock_->Advance(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_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| 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_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| // Receiving the second touchmove will kick us back into compositor priority. |
| run_order.clear(); |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, testing::ElementsAre(std::string("L1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestIsHighPriorityWorkAnticipated) { |
| bool is_anticipated_before = false; |
| bool is_anticipated_after = false; |
| |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); |
| default_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&AnticipationTestTask, scheduler_.get(), |
| SimulateInputType::None, &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); |
| |
| default_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&AnticipationTestTask, scheduler_.get(), |
| SimulateInputType::TouchStart, |
| &is_anticipated_before, &is_anticipated_after)); |
| bool dummy; |
| default_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&AnticipationTestTask, scheduler_.get(), |
| SimulateInputType::TouchEnd, &dummy, &dummy)); |
| default_task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind(&AnticipationTestTask, scheduler_.get(), |
| SimulateInputType::GestureScrollBegin, &dummy, &dummy)); |
| default_task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind(&AnticipationTestTask, scheduler_.get(), |
| SimulateInputType::GestureScrollEnd, &dummy, &dummy)); |
| |
| 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_->Advance(priority_escalation_after_input_duration() * 2); |
| default_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&AnticipationTestTask, scheduler_.get(), |
| SimulateInputType::None, &is_anticipated_before, |
| &is_anticipated_after)); |
| RunUntilIdle(); |
| // Without additional input, the scheduler should go into NONE |
| // use case but with scrolling expected where high-priority work is still |
| // anticipated. |
| EXPECT_EQ(UseCase::NONE, CurrentUseCase()); |
| EXPECT_TRUE(TouchStartExpectedSoon()); |
| EXPECT_TRUE(is_anticipated_before); |
| EXPECT_TRUE(is_anticipated_after); |
| |
| clock_->Advance(subsequent_input_expected_after_input_duration() * 2); |
| default_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&AnticipationTestTask, scheduler_.get(), |
| SimulateInputType::None, &is_anticipated_before, |
| &is_anticipated_after)); |
| RunUntilIdle(); |
| // Eventually the scheduler should go into the default use case where |
| // high-priority work is no longer anticipated. |
| EXPECT_EQ(UseCase::NONE, CurrentUseCase()); |
| EXPECT_FALSE(TouchStartExpectedSoon()); |
| EXPECT_FALSE(is_anticipated_before); |
| EXPECT_FALSE(is_anticipated_after); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestShouldYield) { |
| bool should_yield_before = false; |
| bool should_yield_after = false; |
| |
| ForceMainThreadScrollingUseCase(); |
| |
| 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 mainthread scrolling 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); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestShouldYield_TouchStart) { |
| // Receiving a touchstart should immediately trigger yielding, even if |
| // there's no immediately pending work in the compositor queue. |
| EXPECT_FALSE(scheduler_->ShouldYieldForHighPriorityWork()); |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| EXPECT_TRUE(scheduler_->ShouldYieldForHighPriorityWork()); |
| RunUntilIdle(); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, SlowMainThreadInputEvent) { |
| EXPECT_EQ(UseCase::NONE, CurrentUseCase()); |
| |
| // An input event should bump us into input priority. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| RunUntilIdle(); |
| EXPECT_EQ(UseCase::COMPOSITOR_GESTURE, CurrentUseCase()); |
| |
| // Simulate the input event being queued for a very long time. The compositor |
| // task we post here represents the enqueued input task. |
| clock_->Advance(priority_escalation_after_input_duration() * 2); |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| RunUntilIdle(); |
| |
| // Even though we exceeded the input priority escalation period, we should |
| // still be in compositor priority since the input remains queued. |
| EXPECT_EQ(UseCase::COMPOSITOR_GESTURE, CurrentUseCase()); |
| |
| // After the escalation period ends we should go back into normal mode. |
| clock_->Advance(priority_escalation_after_input_duration() * 2); |
| RunUntilIdle(); |
| EXPECT_EQ(UseCase::NONE, CurrentUseCase()); |
| } |
| |
| class RendererSchedulerImplWithMockSchedulerTest |
| : public RendererSchedulerImplTest { |
| public: |
| void SetUp() override { |
| mock_task_runner_ = make_scoped_refptr( |
| new cc::OrderedSimpleTaskRunner(clock_.get(), false)); |
| main_task_runner_ = SchedulerTqmDelegateForTest::Create( |
| mock_task_runner_, make_scoped_ptr(new TestTimeSource(clock_.get()))); |
| mock_scheduler_ = new RendererSchedulerImplForTest(main_task_runner_); |
| Initialize(make_scoped_ptr(mock_scheduler_)); |
| } |
| |
| protected: |
| RendererSchedulerImplForTest* mock_scheduler_; |
| }; |
| |
| TEST_F(RendererSchedulerImplWithMockSchedulerTest, |
| OnlyOnePendingUrgentPolicyUpdatey) { |
| mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| |
| RunUntilIdle(); |
| |
| EXPECT_EQ(1, mock_scheduler_->update_policy_count_); |
| } |
| |
| TEST_F(RendererSchedulerImplWithMockSchedulerTest, |
| OnePendingDelayedAndOneUrgentUpdatePolicy) { |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| mock_scheduler_->ScheduleDelayedPolicyUpdate( |
| clock_->NowTicks(), base::TimeDelta::FromMilliseconds(1)); |
| mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| |
| RunUntilIdle(); |
| |
| // We expect both the urgent and the delayed updates to run. |
| EXPECT_EQ(2, mock_scheduler_->update_policy_count_); |
| } |
| |
| TEST_F(RendererSchedulerImplWithMockSchedulerTest, |
| OneUrgentAndOnePendingDelayedUpdatePolicy) { |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread(); |
| mock_scheduler_->ScheduleDelayedPolicyUpdate( |
| clock_->NowTicks(), 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(RendererSchedulerImplWithMockSchedulerTest, |
| UpdatePolicyCountTriggeredByOneInputEvent) { |
| // We expect DidHandleInputEventOnCompositorThread to post an urgent policy |
| // update. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| EXPECT_EQ(0, mock_scheduler_->update_policy_count_); |
| mock_task_runner_->RunPendingTasks(); |
| EXPECT_EQ(1, mock_scheduler_->update_policy_count_); |
| |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| EXPECT_EQ(1, mock_scheduler_->update_policy_count_); |
| |
| clock_->Advance(base::TimeDelta::FromMilliseconds(1000)); |
| RunUntilIdle(); |
| |
| // We finally expect a delayed policy update 100ms later. |
| EXPECT_EQ(2, mock_scheduler_->update_policy_count_); |
| } |
| |
| TEST_F(RendererSchedulerImplWithMockSchedulerTest, |
| UpdatePolicyCountTriggeredByThreeInputEvents) { |
| // We expect DidHandleInputEventOnCompositorThread to post an urgent policy |
| // update. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| EXPECT_EQ(0, mock_scheduler_->update_policy_count_); |
| mock_task_runner_->RunPendingTasks(); |
| EXPECT_EQ(1, mock_scheduler_->update_policy_count_); |
| |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| EXPECT_EQ(1, mock_scheduler_->update_policy_count_); |
| |
| // The second call to DidHandleInputEventOnCompositorThread should not post a |
| // policy update because we are already in compositor priority. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| mock_task_runner_->RunPendingTasks(); |
| EXPECT_EQ(1, mock_scheduler_->update_policy_count_); |
| |
| // We expect DidHandleInputEvent to trigger a policy update. |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove)); |
| EXPECT_EQ(1, mock_scheduler_->update_policy_count_); |
| |
| // The third call to DidHandleInputEventOnCompositorThread should post a |
| // policy update because the awaiting_touch_start_response_ flag changed. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| EXPECT_EQ(1, mock_scheduler_->update_policy_count_); |
| mock_task_runner_->RunPendingTasks(); |
| EXPECT_EQ(2, mock_scheduler_->update_policy_count_); |
| |
| // We expect DidHandleInputEvent to trigger a policy update. |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove)); |
| EXPECT_EQ(2, mock_scheduler_->update_policy_count_); |
| |
| clock_->Advance(base::TimeDelta::FromMilliseconds(1000)); |
| RunUntilIdle(); |
| |
| // We finally expect a delayed policy update. |
| EXPECT_EQ(3, mock_scheduler_->update_policy_count_); |
| } |
| |
| TEST_F(RendererSchedulerImplWithMockSchedulerTest, |
| UpdatePolicyCountTriggeredByTwoInputEventsWithALongSeparatingDelay) { |
| // We expect DidHandleInputEventOnCompositorThread to post an urgent policy |
| // update. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| EXPECT_EQ(0, mock_scheduler_->update_policy_count_); |
| mock_task_runner_->RunPendingTasks(); |
| EXPECT_EQ(1, mock_scheduler_->update_policy_count_); |
| |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| EXPECT_EQ(1, mock_scheduler_->update_policy_count_); |
| |
| clock_->Advance(base::TimeDelta::FromMilliseconds(1000)); |
| RunUntilIdle(); |
| // We expect a delayed policy update. |
| EXPECT_EQ(2, mock_scheduler_->update_policy_count_); |
| |
| // We expect the second call to DidHandleInputEventOnCompositorThread to post |
| // an urgent policy update because we are no longer in compositor priority. |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| EXPECT_EQ(2, mock_scheduler_->update_policy_count_); |
| mock_task_runner_->RunPendingTasks(); |
| EXPECT_EQ(3, mock_scheduler_->update_policy_count_); |
| |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::TouchMove)); |
| EXPECT_EQ(3, mock_scheduler_->update_policy_count_); |
| |
| clock_->Advance(base::TimeDelta::FromMilliseconds(1000)); |
| RunUntilIdle(); |
| |
| // We finally expect a delayed policy update. |
| EXPECT_EQ(4, mock_scheduler_->update_policy_count_); |
| } |
| |
| TEST_F(RendererSchedulerImplWithMockSchedulerTest, |
| EnsureUpdatePolicyNotTriggeredTooOften) { |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| EXPECT_EQ(0, mock_scheduler_->update_policy_count_); |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); |
| EXPECT_EQ(1, mock_scheduler_->update_policy_count_); |
| |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| scheduler_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollBegin), |
| RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD); |
| |
| // 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(1, mock_scheduler_->update_policy_count_); |
| scheduler_->IsHighPriorityWorkAnticipated(); |
| EXPECT_EQ(2, 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(); |
| |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart)); |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::GestureScrollBegin)); |
| |
| EXPECT_EQ(2, mock_scheduler_->update_policy_count_); |
| |
| // We expect both the urgent and the delayed updates to run in addition to the |
| // earlier updated cause by IsHighPriorityWorkAnticipated, a final update |
| // transitions from 'not_scrolling scroll expected' to 'not_scrolling'. |
| RunUntilIdle(); |
| EXPECT_THAT(mock_scheduler_->use_cases_, |
| testing::ElementsAre( |
| std::string("none"), std::string("compositor_gesture"), |
| std::string("compositor_gesture"), |
| std::string("none scroll expected"), std::string("none"))); |
| } |
| |
| 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_->NowTicks() + 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_->NowTicks() + 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_->Advance(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_->Advance(maximum_idle_period_duration()); |
| RunUntilIdle(); |
| EXPECT_EQ(1, run_count); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestLongIdlePeriodRepeating) { |
| mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| std::vector<base::TimeTicks> actual_deadlines; |
| int run_count = 0; |
| |
| max_idle_task_reposts = 3; |
| base::TimeTicks clock_before(clock_->NowTicks()); |
| base::TimeDelta idle_task_runtime(base::TimeDelta::FromMilliseconds(10)); |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, base::Bind(&RepostingUpdateClockIdleTestTask, |
| idle_task_runner_, &run_count, clock_.get(), |
| idle_task_runtime, &actual_deadlines)); |
| scheduler_->BeginFrameNotExpectedSoon(); |
| RunUntilIdle(); |
| EXPECT_EQ(3, run_count); |
| EXPECT_THAT( |
| actual_deadlines, |
| testing::ElementsAre( |
| clock_before + maximum_idle_period_duration(), |
| clock_before + idle_task_runtime + maximum_idle_period_duration(), |
| clock_before + (2 * idle_task_runtime) + |
| maximum_idle_period_duration())); |
| |
| // Check that idle tasks don't run after the idle period ends with a |
| // new BeginMainFrame. |
| max_idle_task_reposts = 5; |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, base::Bind(&RepostingUpdateClockIdleTestTask, |
| idle_task_runner_, &run_count, clock_.get(), |
| idle_task_runtime, &actual_deadlines)); |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, base::Bind(&WillBeginFrameIdleTask, |
| base::Unretained(scheduler_.get()), clock_.get())); |
| RunUntilIdle(); |
| EXPECT_EQ(4, 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_->Advance(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_->DidHandleInputEventOnCompositorThread( |
| FakeInputEvent(blink::WebInputEvent::TouchStart), |
| RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); |
| scheduler_->BeginFrameNotExpectedSoon(); |
| RunUntilIdle(); |
| EXPECT_EQ(0, run_count); |
| |
| // The long idle period should start after the touchstart policy has finished. |
| clock_->Advance(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_->Advance(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_->NowTicks(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL)); |
| EXPECT_FALSE(scheduler_->CanExceedIdleDeadlineIfRequired()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestRendererHiddenIdlePeriod) { |
| int run_count = 0; |
| |
| max_idle_task_reposts = 2; |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&RepostingIdleTestTask, idle_task_runner_, &run_count)); |
| |
| // Renderer should start in visible state. |
| RunUntilIdle(); |
| EXPECT_EQ(0, run_count); |
| |
| // When we hide the renderer it should start a max deadline idle period, which |
| // will run an idle task and then immediately start a new idle period, which |
| // runs the second idle task. |
| scheduler_->SetAllRenderWidgetsHidden(true); |
| RunUntilIdle(); |
| EXPECT_EQ(2, run_count); |
| |
| // Advance time by amount of time by the maximum amount of time we execute |
| // idle tasks when hidden (plus some slack) - idle period should have ended. |
| max_idle_task_reposts = 3; |
| idle_task_runner_->PostIdleTask( |
| FROM_HERE, |
| base::Bind(&RepostingIdleTestTask, idle_task_runner_, &run_count)); |
| clock_->Advance(end_idle_when_hidden_delay() + |
| base::TimeDelta::FromMilliseconds(10)); |
| RunUntilIdle(); |
| EXPECT_EQ(2, run_count); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TimerQueueEnabledByDefault) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "T1 T2"); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("T1"), std::string("T2"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, SuspendAndResumeTimerQueue) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "T1 T2"); |
| |
| scheduler_->SuspendTimerQueue(); |
| RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| scheduler_->ResumeTimerQueue(); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("T1"), std::string("T2"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, MultipleSuspendsNeedMultipleResumes) { |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "T1 T2"); |
| |
| scheduler_->SuspendTimerQueue(); |
| scheduler_->SuspendTimerQueue(); |
| scheduler_->SuspendTimerQueue(); |
| RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| scheduler_->ResumeTimerQueue(); |
| RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| scheduler_->ResumeTimerQueue(); |
| RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| scheduler_->ResumeTimerQueue(); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("T1"), std::string("T2"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, UseCaseToString) { |
| CheckAllUseCaseToString(); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, MismatchedDidHandleInputEventOnMainThread) { |
| // This should not DCHECK because there was no corresponding compositor side |
| // call to DidHandleInputEventOnCompositorThread with |
| // INPUT_EVENT_ACK_STATE_NOT_CONSUMED. There are legitimate reasons for the |
| // compositor to not be there and we don't want to make debugging impossible. |
| scheduler_->DidHandleInputEventOnMainThread( |
| FakeInputEvent(blink::WebInputEvent::GestureFlingStart)); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, BeginMainFrameOnCriticalPath) { |
| ASSERT_FALSE(scheduler_->BeginMainFrameOnCriticalPath()); |
| |
| cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), |
| base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL); |
| scheduler_->WillBeginFrame(begin_frame_args); |
| ASSERT_TRUE(scheduler_->BeginMainFrameOnCriticalPath()); |
| |
| begin_frame_args.on_critical_path = false; |
| scheduler_->WillBeginFrame(begin_frame_args); |
| ASSERT_FALSE(scheduler_->BeginMainFrameOnCriticalPath()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, ShutdownPreventsPostingOfNewTasks) { |
| scheduler_->Shutdown(); |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "D1 C1"); |
| RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, TestRendererBackgroundedTimerSuspension) { |
| scheduler_->SetTimerQueueSuspensionWhenBackgroundedEnabled(true); |
| |
| std::vector<std::string> run_order; |
| PostTestTasks(&run_order, "T1 T2"); |
| |
| // The background signal will not immediately suspend the timer queue. |
| scheduler_->OnRendererBackgrounded(); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("T1"), std::string("T2"))); |
| |
| run_order.clear(); |
| PostTestTasks(&run_order, "T3"); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, testing::ElementsAre(std::string("T3"))); |
| |
| // Advance the time until after the scheduled timer queue suspension. |
| run_order.clear(); |
| clock_->Advance(suspend_timers_when_backgrounded_delay() + |
| base::TimeDelta::FromMilliseconds(10)); |
| RunUntilIdle(); |
| ASSERT_TRUE(run_order.empty()); |
| |
| // Timer tasks should be suspended until the foregrounded signal. |
| PostTestTasks(&run_order, "T4 T5"); |
| RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| scheduler_->OnRendererForegrounded(); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("T4"), std::string("T5"))); |
| |
| // Subsequent timer tasks should fire as usual. |
| run_order.clear(); |
| PostTestTasks(&run_order, "T6"); |
| RunUntilIdle(); |
| EXPECT_THAT(run_order, testing::ElementsAre(std::string("T6"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| ExpensiveLoadingTasksNotBlockedTillFirstBeginMainFrame) { |
| std::vector<std::string> run_order; |
| |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); |
| SimulateExpensiveTasks(loading_task_runner_); |
| ForceTouchStartToBeExpectedSoon(); |
| PostTestTasks(&run_order, "L1 D1"); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase()); |
| EXPECT_FALSE(HaveSeenABeginMainframe()); |
| EXPECT_TRUE(LoadingTasksSeemExpensive()); |
| EXPECT_FALSE(TimerTasksSeemExpensive()); |
| EXPECT_TRUE(TouchStartExpectedSoon()); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("L1"), std::string("D1"))); |
| |
| // Emit a BeginMainFrame, and the loading task should get blocked. |
| DoMainFrame(); |
| run_order.clear(); |
| |
| PostTestTasks(&run_order, "L1 D1"); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(RendererScheduler::UseCase::NONE, CurrentUseCase()); |
| EXPECT_TRUE(HaveSeenABeginMainframe()); |
| EXPECT_TRUE(LoadingTasksSeemExpensive()); |
| EXPECT_FALSE(TimerTasksSeemExpensive()); |
| EXPECT_TRUE(TouchStartExpectedSoon()); |
| EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| ExpensiveLoadingTasksNotBlockedIfNoTouchHandler) { |
| std::vector<std::string> run_order; |
| |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(false); |
| DoMainFrame(); |
| SimulateExpensiveTasks(loading_task_runner_); |
| ForceTouchStartToBeExpectedSoon(); |
| PostTestTasks(&run_order, "L1 D1"); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase()); |
| EXPECT_TRUE(HaveSeenABeginMainframe()); |
| EXPECT_TRUE(LoadingTasksSeemExpensive()); |
| EXPECT_FALSE(TimerTasksSeemExpensive()); |
| EXPECT_FALSE(TouchStartExpectedSoon()); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("L1"), std::string("D1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| ExpensiveLoadingTasksNotBlockedIfNavigationExpected) { |
| std::vector<std::string> run_order; |
| |
| DoMainFrame(); |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); |
| SimulateExpensiveTasks(loading_task_runner_); |
| ForceTouchStartToBeExpectedSoon(); |
| scheduler_->AddPendingNavigation(); |
| |
| PostTestTasks(&run_order, "L1 D1"); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase()); |
| EXPECT_TRUE(HaveSeenABeginMainframe()); |
| EXPECT_TRUE(LoadingTasksSeemExpensive()); |
| EXPECT_FALSE(TimerTasksSeemExpensive()); |
| EXPECT_TRUE(TouchStartExpectedSoon()); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("L1"), std::string("D1"))); |
| |
| // After the nagigation has been cancelled, the expensive loading tasks should |
| // get blocked. |
| scheduler_->RemovePendingNavigation(); |
| run_order.clear(); |
| |
| PostTestTasks(&run_order, "L1 D1"); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(RendererScheduler::UseCase::NONE, CurrentUseCase()); |
| EXPECT_TRUE(HaveSeenABeginMainframe()); |
| EXPECT_TRUE(LoadingTasksSeemExpensive()); |
| EXPECT_FALSE(TimerTasksSeemExpensive()); |
| EXPECT_TRUE(TouchStartExpectedSoon()); |
| EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1"))); |
| } |
| |
| TEST_F( |
| RendererSchedulerImplTest, |
| ExpensiveLoadingTasksNotBlockedIfNavigationExpected_MultipleNavigations) { |
| std::vector<std::string> run_order; |
| |
| DoMainFrame(); |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); |
| SimulateExpensiveTasks(loading_task_runner_); |
| ForceTouchStartToBeExpectedSoon(); |
| scheduler_->AddPendingNavigation(); |
| scheduler_->AddPendingNavigation(); |
| |
| PostTestTasks(&run_order, "L1 D1"); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase()); |
| EXPECT_TRUE(HaveSeenABeginMainframe()); |
| EXPECT_TRUE(LoadingTasksSeemExpensive()); |
| EXPECT_FALSE(TimerTasksSeemExpensive()); |
| EXPECT_TRUE(TouchStartExpectedSoon()); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("L1"), std::string("D1"))); |
| |
| |
| run_order.clear(); |
| scheduler_->RemovePendingNavigation(); |
| // Navigation task expected ref count non-zero so expensive tasks still not |
| // blocked. |
| PostTestTasks(&run_order, "L1 D1"); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase()); |
| EXPECT_TRUE(HaveSeenABeginMainframe()); |
| EXPECT_TRUE(LoadingTasksSeemExpensive()); |
| EXPECT_FALSE(TimerTasksSeemExpensive()); |
| EXPECT_TRUE(TouchStartExpectedSoon()); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("L1"), std::string("D1"))); |
| |
| |
| run_order.clear(); |
| scheduler_->RemovePendingNavigation(); |
| // Navigation task expected ref count is now zero, the expensive loading tasks |
| // should get blocked. |
| PostTestTasks(&run_order, "L1 D1"); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(RendererScheduler::UseCase::NONE, CurrentUseCase()); |
| EXPECT_TRUE(HaveSeenABeginMainframe()); |
| EXPECT_TRUE(LoadingTasksSeemExpensive()); |
| EXPECT_FALSE(TimerTasksSeemExpensive()); |
| EXPECT_TRUE(TouchStartExpectedSoon()); |
| EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, |
| ExpensiveLoadingTasksNotBlockedDuringMainThreadGestures) { |
| std::vector<std::string> run_order; |
| |
| SimulateExpensiveTasks(loading_task_runner_); |
| |
| // Loading tasks should not be disabled during main thread user user |
| // interactions. |
| PostTestTasks(&run_order, "C1 L1"); |
| |
| // Trigger main_thread_gesture UseCase |
| WillBeginMainThreadGestureFrame(); |
| RunUntilIdle(); |
| EXPECT_EQ(RendererScheduler::UseCase::MAIN_THREAD_GESTURE, CurrentUseCase()); |
| |
| EXPECT_TRUE(LoadingTasksSeemExpensive()); |
| EXPECT_FALSE(TimerTasksSeemExpensive()); |
| EXPECT_THAT(run_order, |
| testing::ElementsAre(std::string("C1"), std::string("L1"))); |
| } |
| |
| TEST_F(RendererSchedulerImplTest, ModeratelyExpensiveTimer_NotBlocked) { |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); |
| for (int i = 0; i < 20; i++) { |
| simulate_timer_task_ran_ = false; |
| compositor_task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind( |
| &RendererSchedulerImplTest::SimulateMainThreadGestureCompositorTask, |
| base::Unretained(this), base::TimeDelta::FromMilliseconds(4))); |
| timer_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&RendererSchedulerImplTest::SimulateTimerTask, |
| base::Unretained(this), |
| base::TimeDelta::FromMilliseconds(10))); |
| |
| RunUntilIdle(); |
| EXPECT_TRUE(simulate_timer_task_ran_); |
| EXPECT_EQ(RendererScheduler::UseCase::MAIN_THREAD_GESTURE, |
| CurrentUseCase()); |
| EXPECT_FALSE(LoadingTasksSeemExpensive()); |
| EXPECT_FALSE(TimerTasksSeemExpensive()); |
| |
| base::TimeDelta time_till_next_frame = |
| EstimatedNextFrameBegin() - clock_->NowTicks(); |
| if (time_till_next_frame > base::TimeDelta()) |
| clock_->Advance(time_till_next_frame); |
| } |
| } |
| |
| TEST_F(RendererSchedulerImplTest, ExpensiveTimer_Blocked) { |
| scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); |
| for (int i = 0; i < 20; i++) { |
| simulate_timer_task_ran_ = false; |
| compositor_task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind( |
| &RendererSchedulerImplTest::SimulateMainThreadGestureCompositorTask, |
| base::Unretained(this), base::TimeDelta::FromMilliseconds(8))); |
| timer_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&RendererSchedulerImplTest::SimulateTimerTask, |
| base::Unretained(this), |
| base::TimeDelta::FromMilliseconds(10))); |
| |
| RunUntilIdle(); |
| EXPECT_EQ(RendererScheduler::UseCase::MAIN_THREAD_GESTURE, |
| CurrentUseCase()); |
| EXPECT_FALSE(LoadingTasksSeemExpensive()); |
| if (i == 0) { |
| EXPECT_FALSE(TimerTasksSeemExpensive()); |
| EXPECT_TRUE(simulate_timer_task_ran_); |
| } else { |
| EXPECT_TRUE(TimerTasksSeemExpensive()); |
| EXPECT_FALSE(simulate_timer_task_ran_); |
| } |
| |
| base::TimeDelta time_till_next_frame = |
| EstimatedNextFrameBegin() - clock_->NowTicks(); |
| if (time_till_next_frame > base::TimeDelta()) |
| clock_->Advance(time_till_next_frame); |
| } |
| } |
| |
| } // namespace scheduler |