blob: 0ee313bdbbd73785781337d97ac016771caa882d [file] [log] [blame]
// 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 <utility>
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.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 "components/scheduler/renderer/auto_advancing_virtual_time_domain.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::TouchMove),
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;
using RendererSchedulerImpl::EstimateLongestJankFreeTaskDuration;
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 = RendererSchedulerImpl::UseCaseToString(
MainThreadOnly().current_use_case);
if (MainThreadOnly().touchstart_expected_soon) {
use_cases_.push_back(use_case + " touchstart 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 RendererSchedulerImpl::UseCase& use_case) {
return os << RendererSchedulerImpl::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(),
base::WrapUnique(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_,
base::WrapUnique(new TestTimeSource(clock_.get())));
}
Initialize(
base::WrapUnique(new RendererSchedulerImplForTest(main_task_runner_)));
}
void Initialize(std::unique_ptr<RendererSchedulerImplForTest> scheduler) {
scheduler_ = std::move(scheduler);
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 {
base::RunLoop().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
base::RunLoop().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 DoMainFrameOnCriticalPath() {
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::GestureScrollUpdate),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
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);
}
enum class TouchEventPolicy {
SEND_TOUCH_START,
DONT_SEND_TOUCH_START,
};
void SimulateCompositorGestureStart(TouchEventPolicy touch_event_policy) {
if (touch_event_policy == TouchEventPolicy::SEND_TOUCH_START) {
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchStart),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchMove),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchMove),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
}
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollBegin),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollUpdate),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
}
// Simulate a gesture where there is an active compositor scroll, but no
// scroll updates are generated. Instead, the main thread handles
// non-canceleable touch events, making this an effectively main thread
// driven gesture.
void SimulateMainThreadGestureWithoutScrollUpdates() {
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchStart),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchMove),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollBegin),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchMove),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
}
// Simulate a gesture where the main thread handles touch events but does not
// preventDefault(), allowing the gesture to turn into a compositor driven
// gesture. This function also verifies the necessary policy updates are
// scheduled.
void SimulateMainThreadGestureWithoutPreventDefault() {
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchStart),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
// Touchstart policy update.
EXPECT_TRUE(scheduler_->PolicyNeedsUpdateForTesting());
EXPECT_EQ(UseCase::TOUCHSTART, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_FALSE(scheduler_->PolicyNeedsUpdateForTesting());
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchMove),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureTapCancel),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollBegin),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
// Main thread gesture policy update.
EXPECT_TRUE(scheduler_->PolicyNeedsUpdateForTesting());
EXPECT_EQ(UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_FALSE(scheduler_->PolicyNeedsUpdateForTesting());
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollUpdate),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchScrollStarted),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchMove),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
// Compositor thread gesture policy update.
EXPECT_TRUE(scheduler_->PolicyNeedsUpdateForTesting());
EXPECT_EQ(UseCase::COMPOSITOR_GESTURE,
ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_FALSE(scheduler_->PolicyNeedsUpdateForTesting());
}
void SimulateMainThreadGestureStart(TouchEventPolicy touch_event_policy,
blink::WebInputEvent::Type gesture_type) {
if (touch_event_policy == TouchEventPolicy::SEND_TOUCH_START) {
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchStart),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnMainThread(
FakeInputEvent(blink::WebInputEvent::TouchStart));
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchMove),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnMainThread(
FakeInputEvent(blink::WebInputEvent::TouchMove));
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchMove),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnMainThread(
FakeInputEvent(blink::WebInputEvent::TouchMove));
}
if (gesture_type != blink::WebInputEvent::Undefined) {
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(gesture_type),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnMainThread(FakeInputEvent(gesture_type));
}
}
void SimulateMainThreadInputHandlingCompositorTask(
base::TimeDelta begin_main_frame_duration) {
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchMove),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
clock_->Advance(begin_main_frame_duration);
scheduler_->DidHandleInputEventOnMainThread(
FakeInputEvent(blink::WebInputEvent::TouchMove));
scheduler_->DidCommitFrameToCompositor();
}
void SimulateMainThreadCompositorTask(
base::TimeDelta begin_main_frame_duration) {
clock_->Advance(begin_main_frame_duration);
scheduler_->DidCommitFrameToCompositor();
simulate_compositor_task_ran_ = true;
}
bool SimulatedCompositorTaskPending() const {
return !simulate_compositor_task_ran_;
}
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;
}
v8::RAILMode RAILMode() {
return scheduler_->MainThreadOnly().current_policy.rail_mode;
}
bool BeginFrameNotExpectedSoon() {
return scheduler_->MainThreadOnly().begin_frame_not_expected_soon;
}
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;
}
int NavigationTaskExpectedCount() {
return scheduler_->MainThreadOnly().navigation_task_expected_count;
}
// 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);
}
static base::TimeDelta rails_response_time() {
return base::TimeDelta::FromMilliseconds(
RendererSchedulerImpl::kRailsResponseTimeMillis);
}
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);
}
std::unique_ptr<base::SimpleTestTickClock> clock_;
// Only one of mock_task_runner_ or message_loop_ will be set.
scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
std::unique_ptr<base::MessageLoop> message_loop_;
scoped_refptr<SchedulerTqmDelegate> main_task_runner_;
std::unique_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_;
bool simulate_compositor_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,
base::RetainedRef(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,
base::RetainedRef(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(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
}
TEST_F(RendererSchedulerImplTest,
TestCompositorPolicy_CompositorHandlesInput_WithTouchHandler) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
EnableIdleTasks();
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("L1"), std::string("D1"),
std::string("D2"), std::string("I1"),
std::string("C1"), std::string("C2")));
EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
CurrentUseCase());
}
TEST_F(RendererSchedulerImplTest,
TestCompositorPolicy_MainThreadHandlesInput_WithoutScrollUpdates) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
EnableIdleTasks();
SimulateMainThreadGestureWithoutScrollUpdates();
RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("C1"), std::string("C2"),
std::string("L1"), std::string("D1"),
std::string("D2"), std::string("I1")));
EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
CurrentUseCase());
}
TEST_F(RendererSchedulerImplTest,
TestCompositorPolicy_MainThreadHandlesInput_WithoutPreventDefault) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
EnableIdleTasks();
SimulateMainThreadGestureWithoutPreventDefault();
RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("L1"), std::string("D1"),
std::string("D2"), std::string("I1"),
std::string("C1"), std::string("C2")));
EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
CurrentUseCase());
}
TEST_F(RendererSchedulerImplTest,
TestCompositorPolicy_CompositorHandlesInput_LongGestureDuration) {
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
EnableIdleTasks();
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
base::TimeTicks loop_end_time =
clock_->NowTicks() + base::TimeDelta::FromMilliseconds(
UserModel::kMedianGestureDurationMillis * 2);
// The UseCase::COMPOSITOR_GESTURE usecase initially deprioritizes compositor
// tasks (see TestCompositorPolicy_CompositorHandlesInput_WithTouchHandler)
// but if the gesture is long enough, compositor tasks get prioritized again.
while (clock_->NowTicks() < loop_end_time) {
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchMove),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
clock_->Advance(base::TimeDelta::FromMilliseconds(16));
RunUntilIdle();
}
std::vector<std::string> run_order;
PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("C1"), std::string("C2"),
std::string("L1"), std::string("D1"),
std::string("D2")));
EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
CurrentUseCase());
}
TEST_F(RendererSchedulerImplTest,
TestCompositorPolicy_CompositorHandlesInput_WithoutTouchHandler) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
EnableIdleTasks();
SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START);
RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("L1"), std::string("D1"),
std::string("D2"), std::string("I1"),
std::string("C1"), std::string("C2")));
EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
CurrentUseCase());
}
TEST_F(RendererSchedulerImplTest,
TestCompositorPolicy_MainThreadHandlesInput_WithTouchHandler) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
EnableIdleTasks();
SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollBegin);
RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("C1"), std::string("C2"),
std::string("L1"), std::string("D1"),
std::string("D2"), std::string("I1")));
EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
CurrentUseCase());
scheduler_->DidHandleInputEventOnMainThread(
FakeInputEvent(blink::WebInputEvent::GestureFlingStart));
}
TEST_F(RendererSchedulerImplTest,
TestCompositorPolicy_MainThreadHandlesInput_WithoutTouchHandler) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
EnableIdleTasks();
SimulateMainThreadGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollBegin);
RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("C1"), std::string("C2"),
std::string("L1"), std::string("D1"),
std::string("D2"), std::string("I1")));
EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
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();
// Note DidAnimateForInputOnCompositorThread does not by itself trigger a
// policy update.
EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
ForceUpdatePolicyAndGetCurrentUseCase());
EnableIdleTasks();
RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("D1"), std::string("D2"),
std::string("I1"), std::string("C1"),
std::string("C2")));
EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
CurrentUseCase());
}
TEST_F(RendererSchedulerImplTest, Navigation_ResetsTaskCostEstimations) {
std::vector<std::string> run_order;
SimulateExpensiveTasks(timer_task_runner_);
scheduler_->OnNavigationStarted();
PostTestTasks(&run_order, "C1 T1");
SimulateMainThreadGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollBegin);
scheduler_->DidCommitFrameToCompositor(); // Starts Idle Period
RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("C1"), std::string("T1")));
}
TEST_F(RendererSchedulerImplTest,
ExpensiveTimersDontRunWhenMainThreadScrolling) {
std::vector<std::string> run_order;
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
SimulateExpensiveTasks(timer_task_runner_);
DoMainFrame();
SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollUpdate);
PostTestTasks(&run_order, "C1 T1");
RunUntilIdle();
EXPECT_FALSE(TouchStartExpectedSoon());
EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_GESTURE,
CurrentUseCase());
EXPECT_THAT(run_order, testing::ElementsAre(std::string("C1")));
}
TEST_F(RendererSchedulerImplTest,
ExpensiveTimersDoRunWhenMainThreadInputHandling) {
std::vector<std::string> run_order;
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
SimulateExpensiveTasks(timer_task_runner_);
DoMainFrame();
SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START,
blink::WebInputEvent::Undefined);
PostTestTasks(&run_order, "C1 T1");
RunUntilIdle();
EXPECT_FALSE(TouchStartExpectedSoon());
EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
CurrentUseCase());
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("C1"), std::string("T1")));
}
TEST_F(RendererSchedulerImplTest,
ExpensiveTimersDoRunWhenMainThreadScrolling_AndOnCriticalPath) {
std::vector<std::string> run_order;
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
SimulateExpensiveTasks(timer_task_runner_);
DoMainFrameOnCriticalPath();
SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollBegin);
PostTestTasks(&run_order, "C1 T1");
RunUntilIdle();
EXPECT_FALSE(TouchStartExpectedSoon());
EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
CurrentUseCase());
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(RendererSchedulerImpl::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(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
}
TEST_F(RendererSchedulerImplTest,
EventConsumedOnCompositorThread_IgnoresMouseMove_WhenMouseUp) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
EnableIdleTasks();
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::MouseMove),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
RunUntilIdle();
// Note compositor tasks are not prioritized.
EXPECT_EQ(RendererSchedulerImpl::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");
EnableIdleTasks();
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::MouseMove),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
RunUntilIdle();
// Note compositor tasks are not prioritized.
EXPECT_EQ(RendererSchedulerImpl::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");
EnableIdleTasks();
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");
EnableIdleTasks();
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");
EnableIdleTasks();
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");
EnableIdleTasks();
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(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
CurrentUseCase());
}
TEST_F(RendererSchedulerImplTest,
EventConsumedOnCompositorThread_IgnoresKeyboardEvents) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
EnableIdleTasks();
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(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
}
TEST_F(RendererSchedulerImplTest,
EventForwardedToMainThread_IgnoresKeyboardEvents) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
EnableIdleTasks();
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(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
// Note compositor tasks are not prioritized.
scheduler_->DidHandleInputEventOnMainThread(
FakeInputEvent(blink::WebInputEvent::KeyDown));
}
TEST_F(RendererSchedulerImplTest,
TestMainthreadScrollingUseCaseDoesNotStarveDefaultTasks) {
SimulateMainThreadGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollBegin);
EnableIdleTasks();
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) {
SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START);
EXPECT_EQ(UseCase::COMPOSITOR_GESTURE,
ForceUpdatePolicyAndGetCurrentUseCase());
clock_->Advance(base::TimeDelta::FromMilliseconds(1000));
EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
}
TEST_F(RendererSchedulerImplTest,
TestCompositorPolicyEnds_MainThreadHandlesInput) {
SimulateMainThreadGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollBegin);
EXPECT_EQ(UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
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;
default_task_runner_->PostTask(
FROM_HERE, base::Bind(&PostingYieldingTestTask, scheduler_.get(),
base::RetainedRef(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(),
base::RetainedRef(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(),
base::RetainedRef(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::MAIN_THREAD_CUSTOM_INPUT_HANDLING, 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 main thread gesture since the input remains queued.
EXPECT_EQ(UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING, 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_, base::WrapUnique(new TestTimeSource(clock_.get())));
mock_scheduler_ = new RendererSchedulerImplForTest(main_task_runner_);
Initialize(base::WrapUnique(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_);
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
// We expect the first call to IsHighPriorityWorkAnticipated to be called
// after receiving 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_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollEnd),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchEnd),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnMainThread(
FakeInputEvent(blink::WebInputEvent::TouchStart));
scheduler_->DidHandleInputEventOnMainThread(
FakeInputEvent(blink::WebInputEvent::TouchMove));
scheduler_->DidHandleInputEventOnMainThread(
FakeInputEvent(blink::WebInputEvent::TouchMove));
scheduler_->DidHandleInputEventOnMainThread(
FakeInputEvent(blink::WebInputEvent::TouchEnd));
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 touchstart expected' to 'not_scrolling'.
RunUntilIdle();
EXPECT_THAT(
mock_scheduler_->use_cases_,
testing::ElementsAre(
std::string("none"), std::string("compositor_gesture"),
std::string("compositor_gesture touchstart expected"),
std::string("none touchstart 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();
base::RunLoop().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,
base::RetainedRef(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,
base::RetainedRef(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,
base::RetainedRef(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,
base::RetainedRef(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, SuspendAndThrottleTimerQueue) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "T1 T2");
scheduler_->SuspendTimerQueue();
RunUntilIdle();
scheduler_->throttling_helper()->IncreaseThrottleRefCount(
static_cast<TaskQueue*>(timer_task_runner_.get()));
RunUntilIdle();
EXPECT_TRUE(run_order.empty());
}
TEST_F(RendererSchedulerImplTest, ThrottleAndSuspendTimerQueue) {
std::vector<std::string> run_order;
PostTestTasks(&run_order, "T1 T2");
scheduler_->throttling_helper()->IncreaseThrottleRefCount(
static_cast<TaskQueue*>(timer_task_runner_.get()));
RunUntilIdle();
scheduler_->SuspendTimerQueue();
RunUntilIdle();
EXPECT_TRUE(run_order.empty());
}
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, SuspendRenderer) {
// Assume that the renderer is backgrounded.
scheduler_->OnRendererBackgrounded();
// Tasks in some queues don't fire when the renderer is suspended.
std::vector<std::string> run_order;
PostTestTasks(&run_order, "D1 C1 L1 I1 T1");
scheduler_->SuspendRenderer();
EnableIdleTasks();
RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("D1"), std::string("C1"),
std::string("I1")));
// The rest queued tasks fire when the tab goes foregrounded.
run_order.clear();
scheduler_->OnRendererForegrounded();
RunUntilIdle();
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("L1"), std::string("T1")));
}
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(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
EXPECT_TRUE(HaveSeenABeginMainframe());
EXPECT_TRUE(LoadingTasksSeemExpensive());
EXPECT_FALSE(TimerTasksSeemExpensive());
EXPECT_TRUE(TouchStartExpectedSoon());
EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
EXPECT_EQ(v8::PERFORMANCE_RESPONSE, RAILMode());
}
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")));
EXPECT_EQ(v8::PERFORMANCE_ANIMATION, RAILMode());
}
TEST_F(RendererSchedulerImplTest,
ExpensiveTimerTaskBlocked_UseCase_NONE_PreviousCompositorGesture) {
std::vector<std::string> run_order;
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
DoMainFrame();
SimulateExpensiveTasks(timer_task_runner_);
ForceTouchStartToBeExpectedSoon();
PostTestTasks(&run_order, "T1 D1");
RunUntilIdle();
EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_TRUE(HaveSeenABeginMainframe());
EXPECT_FALSE(LoadingTasksSeemExpensive());
EXPECT_TRUE(TimerTasksSeemExpensive());
EXPECT_TRUE(TouchStartExpectedSoon());
EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
EXPECT_EQ(v8::PERFORMANCE_RESPONSE, RAILMode());
}
TEST_F(RendererSchedulerImplTest,
ExpensiveTimerTaskNotBlocked_UseCase_NONE_PreviousMainThreadGesture) {
std::vector<std::string> run_order;
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
DoMainFrame();
SimulateExpensiveTasks(timer_task_runner_);
SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollBegin);
EXPECT_EQ(UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
ForceUpdatePolicyAndGetCurrentUseCase());
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchEnd),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
scheduler_->DidHandleInputEventOnMainThread(
FakeInputEvent(blink::WebInputEvent::TouchEnd));
clock_->Advance(priority_escalation_after_input_duration() * 2);
EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
PostTestTasks(&run_order, "T1 D1");
RunUntilIdle();
EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_TRUE(HaveSeenABeginMainframe());
EXPECT_FALSE(LoadingTasksSeemExpensive());
EXPECT_TRUE(TimerTasksSeemExpensive());
EXPECT_TRUE(TouchStartExpectedSoon());
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("T1"), std::string("D1")));
EXPECT_EQ(v8::PERFORMANCE_ANIMATION, RAILMode());
}
TEST_F(RendererSchedulerImplTest,
ExpensiveTimerTaskBlocked_UseCase_COMPOSITOR_GESTURE) {
std::vector<std::string> run_order;
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
DoMainFrame();
SimulateExpensiveTasks(timer_task_runner_);
ForceTouchStartToBeExpectedSoon();
scheduler_->DidAnimateForInputOnCompositorThread();
PostTestTasks(&run_order, "T1 D1");
RunUntilIdle();
EXPECT_EQ(UseCase::COMPOSITOR_GESTURE,
ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_TRUE(HaveSeenABeginMainframe());
EXPECT_FALSE(LoadingTasksSeemExpensive());
EXPECT_TRUE(TimerTasksSeemExpensive());
EXPECT_TRUE(TouchStartExpectedSoon());
EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
EXPECT_EQ(v8::PERFORMANCE_RESPONSE, RAILMode());
}
TEST_F(RendererSchedulerImplTest,
ExpensiveTimerTaskNotBlockedIfDisallowed_UseCase_COMPOSITOR_GESTURE) {
std::vector<std::string> run_order;
scheduler_->SetExpensiveTaskBlockingAllowed(false);
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
DoMainFrame();
SimulateExpensiveTasks(timer_task_runner_);
ForceTouchStartToBeExpectedSoon();
scheduler_->DidAnimateForInputOnCompositorThread();
PostTestTasks(&run_order, "T1 D1");
RunUntilIdle();
EXPECT_EQ(UseCase::COMPOSITOR_GESTURE,
ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_TRUE(HaveSeenABeginMainframe());
EXPECT_FALSE(LoadingTasksSeemExpensive());
EXPECT_TRUE(TimerTasksSeemExpensive());
EXPECT_TRUE(TouchStartExpectedSoon());
EXPECT_THAT(run_order, testing::ElementsAre(std::string("T1"),
std::string("D1")));
EXPECT_EQ(v8::PERFORMANCE_RESPONSE, RAILMode());
}
TEST_F(RendererSchedulerImplTest,
ExpensiveTimerTaskBlocked_EvenIfBeginMainFrameNotExpectedSoon) {
std::vector<std::string> run_order;
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
DoMainFrame();
SimulateExpensiveTasks(timer_task_runner_);
ForceTouchStartToBeExpectedSoon();
scheduler_->BeginFrameNotExpectedSoon();
PostTestTasks(&run_order, "T1 D1");
RunUntilIdle();
EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_TRUE(HaveSeenABeginMainframe());
EXPECT_FALSE(LoadingTasksSeemExpensive());
EXPECT_TRUE(TimerTasksSeemExpensive());
EXPECT_TRUE(TouchStartExpectedSoon());
EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
EXPECT_EQ(v8::PERFORMANCE_RESPONSE, RAILMode());
}
TEST_F(RendererSchedulerImplTest,
ExpensiveLoadingTasksBlockedIfChildFrameNavigationExpected) {
std::vector<std::string> run_order;
DoMainFrame();
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
SimulateExpensiveTasks(loading_task_runner_);
ForceTouchStartToBeExpectedSoon();
scheduler_->AddPendingNavigation(
blink::WebScheduler::NavigatingFrameType::kChildFrame);
PostTestTasks(&run_order, "L1 D1");
RunUntilIdle();
// The expensive loading task gets blocked.
EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
EXPECT_EQ(v8::PERFORMANCE_RESPONSE, RAILMode());
}
TEST_F(RendererSchedulerImplTest,
ExpensiveLoadingTasksNotBlockedIfMainFrameNavigationExpected) {
std::vector<std::string> run_order;
DoMainFrame();
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
SimulateExpensiveTasks(loading_task_runner_);
ForceTouchStartToBeExpectedSoon();
scheduler_->AddPendingNavigation(
blink::WebScheduler::NavigatingFrameType::kMainFrame);
PostTestTasks(&run_order, "L1 D1");
RunUntilIdle();
EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_TRUE(HaveSeenABeginMainframe());
EXPECT_TRUE(LoadingTasksSeemExpensive());
EXPECT_FALSE(TimerTasksSeemExpensive());
EXPECT_TRUE(TouchStartExpectedSoon());
EXPECT_EQ(1, NavigationTaskExpectedCount());
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(
blink::WebScheduler::NavigatingFrameType::kMainFrame);
run_order.clear();
PostTestTasks(&run_order, "L1 D1");
RunUntilIdle();
EXPECT_EQ(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
EXPECT_TRUE(HaveSeenABeginMainframe());
EXPECT_TRUE(LoadingTasksSeemExpensive());
EXPECT_FALSE(TimerTasksSeemExpensive());
EXPECT_TRUE(TouchStartExpectedSoon());
EXPECT_EQ(0, NavigationTaskExpectedCount());
EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
EXPECT_EQ(v8::PERFORMANCE_RESPONSE, RAILMode());
}
TEST_F(
RendererSchedulerImplTest,
ExpensiveLoadingTasksNotBlockedIfMainFrameNavigationExpected_Multiple) {
std::vector<std::string> run_order;
DoMainFrame();
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
SimulateExpensiveTasks(loading_task_runner_);
ForceTouchStartToBeExpectedSoon();
scheduler_->AddPendingNavigation(
blink::WebScheduler::NavigatingFrameType::kMainFrame);
scheduler_->AddPendingNavigation(
blink::WebScheduler::NavigatingFrameType::kMainFrame);
PostTestTasks(&run_order, "L1 D1");
RunUntilIdle();
EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_TRUE(HaveSeenABeginMainframe());
EXPECT_TRUE(LoadingTasksSeemExpensive());
EXPECT_FALSE(TimerTasksSeemExpensive());
EXPECT_TRUE(TouchStartExpectedSoon());
EXPECT_EQ(2, NavigationTaskExpectedCount());
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("L1"), std::string("D1")));
run_order.clear();
scheduler_->RemovePendingNavigation(
blink::WebScheduler::NavigatingFrameType::kMainFrame);
// 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_EQ(1, NavigationTaskExpectedCount());
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("L1"), std::string("D1")));
run_order.clear();
scheduler_->RemovePendingNavigation(
blink::WebScheduler::NavigatingFrameType::kMainFrame);
// Navigation task expected ref count is now zero, the expensive loading tasks
// should get blocked.
PostTestTasks(&run_order, "L1 D1");
RunUntilIdle();
EXPECT_EQ(RendererSchedulerImpl::UseCase::NONE, CurrentUseCase());
EXPECT_TRUE(HaveSeenABeginMainframe());
EXPECT_TRUE(LoadingTasksSeemExpensive());
EXPECT_FALSE(TimerTasksSeemExpensive());
EXPECT_TRUE(TouchStartExpectedSoon());
EXPECT_EQ(0, NavigationTaskExpectedCount());
EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
EXPECT_EQ(v8::PERFORMANCE_RESPONSE, RAILMode());
}
TEST_F(RendererSchedulerImplTest,
ExpensiveLoadingTasksNotBlockedDuringMainThreadGestures) {
std::vector<std::string> run_order;
SimulateExpensiveTasks(loading_task_runner_);
// Loading tasks should not be disabled during main thread user interactions.
PostTestTasks(&run_order, "C1 L1");
// Trigger main_thread_gesture UseCase
SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollBegin);
RunUntilIdle();
EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
CurrentUseCase());
EXPECT_TRUE(LoadingTasksSeemExpensive());
EXPECT_FALSE(TimerTasksSeemExpensive());
EXPECT_THAT(run_order,
testing::ElementsAre(std::string("C1"), std::string("L1")));
EXPECT_EQ(v8::PERFORMANCE_ANIMATION, RAILMode());
}
TEST_F(RendererSchedulerImplTest, ModeratelyExpensiveTimer_NotBlocked) {
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START,
blink::WebInputEvent::TouchMove);
RunUntilIdle();
for (int i = 0; i < 20; i++) {
simulate_timer_task_ran_ = false;
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);
compositor_task_runner_->PostTask(
FROM_HERE, base::Bind(&RendererSchedulerImplTest::
SimulateMainThreadInputHandlingCompositorTask,
base::Unretained(this),
base::TimeDelta::FromMilliseconds(8)));
timer_task_runner_->PostTask(
FROM_HERE, base::Bind(&RendererSchedulerImplTest::SimulateTimerTask,
base::Unretained(this),
base::TimeDelta::FromMilliseconds(4)));
RunUntilIdle();
EXPECT_TRUE(simulate_timer_task_ran_) << " i = " << i;
EXPECT_EQ(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
CurrentUseCase())
<< " i = " << i;
EXPECT_FALSE(LoadingTasksSeemExpensive()) << " i = " << i;
EXPECT_FALSE(TimerTasksSeemExpensive()) << " i = " << i;
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,
FourtyMsTimer_NotBlocked_CompositorScrolling) {
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
RunUntilIdle();
for (int i = 0; i < 20; i++) {
simulate_timer_task_ran_ = false;
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_->DidAnimateForInputOnCompositorThread();
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RendererSchedulerImplTest::SimulateMainThreadCompositorTask,
base::Unretained(this),
base::TimeDelta::FromMilliseconds(8)));
timer_task_runner_->PostTask(
FROM_HERE, base::Bind(&RendererSchedulerImplTest::SimulateTimerTask,
base::Unretained(this),
base::TimeDelta::FromMilliseconds(40)));
RunUntilIdle();
EXPECT_TRUE(simulate_timer_task_ran_) << " i = " << i;
EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
CurrentUseCase())
<< " i = " << i;
EXPECT_FALSE(LoadingTasksSeemExpensive()) << " i = " << i;
EXPECT_FALSE(TimerTasksSeemExpensive()) << " i = " << i;
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_NotBlocked_UseCase_MAIN_THREAD_CUSTOM_INPUT_HANDLING) {
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START,
blink::WebInputEvent::TouchMove);
RunUntilIdle();
for (int i = 0; i < 20; i++) {
simulate_timer_task_ran_ = false;
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);
compositor_task_runner_->PostTask(
FROM_HERE, base::Bind(&RendererSchedulerImplTest::
SimulateMainThreadInputHandlingCompositorTask,
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(RendererSchedulerImpl::UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING,
CurrentUseCase())
<< " i = " << i;
EXPECT_FALSE(LoadingTasksSeemExpensive()) << " i = " << i;
if (i == 0) {
EXPECT_FALSE(TimerTasksSeemExpensive()) << " i = " << i;
} else {
EXPECT_TRUE(TimerTasksSeemExpensive()) << " i = " << i;
}
EXPECT_TRUE(simulate_timer_task_ran_) << " i = " << i;
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,
EstimateLongestJankFreeTaskDuration_UseCase_NONE) {
EXPECT_EQ(UseCase::NONE, CurrentUseCase());
EXPECT_EQ(rails_response_time(),
scheduler_->EstimateLongestJankFreeTaskDuration());
}
TEST_F(RendererSchedulerImplTest,
EstimateLongestJankFreeTaskDuration_UseCase_COMPOSITOR_GESTURE) {
SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START);
EXPECT_EQ(UseCase::COMPOSITOR_GESTURE,
ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_EQ(rails_response_time(),
scheduler_->EstimateLongestJankFreeTaskDuration());
}
// TODO(alexclarke): Reenable once we've reinstaed the Loading UseCase.
TEST_F(RendererSchedulerImplTest,
DISABLED_EstimateLongestJankFreeTaskDuration_UseCase_) {
scheduler_->OnNavigationStarted();
EXPECT_EQ(UseCase::LOADING, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_EQ(rails_response_time(),
scheduler_->EstimateLongestJankFreeTaskDuration());
}
TEST_F(RendererSchedulerImplTest,
EstimateLongestJankFreeTaskDuration_UseCase_MAIN_THREAD_GESTURE) {
SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollUpdate);
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);
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RendererSchedulerImplTest::
SimulateMainThreadInputHandlingCompositorTask,
base::Unretained(this), base::TimeDelta::FromMilliseconds(5)));
RunUntilIdle();
EXPECT_EQ(UseCase::MAIN_THREAD_GESTURE, CurrentUseCase());
// 16ms frame - 5ms compositor work = 11ms for other stuff.
EXPECT_EQ(base::TimeDelta::FromMilliseconds(11),
scheduler_->EstimateLongestJankFreeTaskDuration());
}
TEST_F(
RendererSchedulerImplTest,
EstimateLongestJankFreeTaskDuration_UseCase_MAIN_THREAD_CUSTOM_INPUT_HANDLING) {
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);
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RendererSchedulerImplTest::
SimulateMainThreadInputHandlingCompositorTask,
base::Unretained(this), base::TimeDelta::FromMilliseconds(5)));
RunUntilIdle();
EXPECT_EQ(UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING, CurrentUseCase());
// 16ms frame - 5ms compositor work = 11ms for other stuff.
EXPECT_EQ(base::TimeDelta::FromMilliseconds(11),
scheduler_->EstimateLongestJankFreeTaskDuration());
}
TEST_F(RendererSchedulerImplTest,
EstimateLongestJankFreeTaskDuration_UseCase_SYNCHRONIZED_GESTURE) {
SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START);
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);
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RendererSchedulerImplTest::SimulateMainThreadCompositorTask,
base::Unretained(this), base::TimeDelta::FromMilliseconds(5)));
RunUntilIdle();
EXPECT_EQ(UseCase::SYNCHRONIZED_GESTURE, CurrentUseCase());
// 16ms frame - 5ms compositor work = 11ms for other stuff.
EXPECT_EQ(base::TimeDelta::FromMilliseconds(11),
scheduler_->EstimateLongestJankFreeTaskDuration());
}
class WebViewSchedulerImplForTest : public WebViewSchedulerImpl {
public:
WebViewSchedulerImplForTest(RendererSchedulerImpl* scheduler)
: WebViewSchedulerImpl(nullptr, scheduler, false) {}
~WebViewSchedulerImplForTest() override {}
void AddConsoleWarning(const std::string& message) override {
console_warnings_.push_back(message);
}
const std::vector<std::string>& console_warnings() const {
return console_warnings_;
}
private:
std::vector<std::string> console_warnings_;
DISALLOW_COPY_AND_ASSIGN(WebViewSchedulerImplForTest);
};
TEST_F(RendererSchedulerImplTest, BlockedTimerNotification) {
// Make sure we see one (and just one) console warning about an expensive
// timer being deferred.
WebViewSchedulerImplForTest web_view_scheduler(scheduler_.get());
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
scheduler_->SetExpensiveTaskBlockingAllowed(true);
DoMainFrame();
SimulateExpensiveTasks(timer_task_runner_);
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
ForceTouchStartToBeExpectedSoon();
std::vector<std::string> run_order;
PostTestTasks(&run_order, "T1 T2");
RunUntilIdle();
EXPECT_EQ(0u, run_order.size());
EXPECT_EQ(1u, web_view_scheduler.console_warnings().size());
EXPECT_NE(std::string::npos,
web_view_scheduler.console_warnings()[0].find("crbug.com/574343"));
}
TEST_F(RendererSchedulerImplTest,
BlockedTimerNotification_ExpensiveTaskBlockingNotAllowed) {
// Make sure we don't report warnings about blocked tasks when expensive task
// blocking is not allowed.
WebViewSchedulerImplForTest web_view_scheduler(scheduler_.get());
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
scheduler_->SetExpensiveTaskBlockingAllowed(false);
scheduler_->SuspendTimerQueue();
DoMainFrame();
SimulateExpensiveTasks(timer_task_runner_);
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
ForceTouchStartToBeExpectedSoon();
std::vector<std::string> run_order;
PostTestTasks(&run_order, "T1 T2");
RunUntilIdle();
EXPECT_EQ(0u, run_order.size());
EXPECT_EQ(0u, web_view_scheduler.console_warnings().size());
}
TEST_F(RendererSchedulerImplTest, BlockedTimerNotification_TimersSuspended) {
// Make sure we don't report warnings about blocked tasks when timers are
// being blocked for other reasons.
WebViewSchedulerImplForTest web_view_scheduler(scheduler_.get());
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
scheduler_->SetExpensiveTaskBlockingAllowed(true);
scheduler_->SuspendTimerQueue();
DoMainFrame();
SimulateExpensiveTasks(timer_task_runner_);
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
ForceTouchStartToBeExpectedSoon();
std::vector<std::string> run_order;
PostTestTasks(&run_order, "T1 T2");
RunUntilIdle();
EXPECT_EQ(0u, run_order.size());
EXPECT_EQ(0u, web_view_scheduler.console_warnings().size());
}
TEST_F(RendererSchedulerImplTest, BlockedTimerNotification_TOUCHSTART) {
// Make sure we don't report warnings about blocked tasks during TOUCHSTART.
WebViewSchedulerImplForTest web_view_scheduler(scheduler_.get());
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
DoMainFrame();
SimulateExpensiveTasks(timer_task_runner_);
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchStart),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
EXPECT_EQ(UseCase::TOUCHSTART, ForceUpdatePolicyAndGetCurrentUseCase());
std::vector<std::string> run_order;
PostTestTasks(&run_order, "T1 T2");
RunUntilIdle();
EXPECT_EQ(0u, run_order.size());
EXPECT_EQ(0u, web_view_scheduler.console_warnings().size());
}
TEST_F(RendererSchedulerImplTest,
BlockedTimerNotification_SYNCHRONIZED_GESTURE) {
// Make sure we only report warnings during a high blocking threshold.
WebViewSchedulerImplForTest web_view_scheduler(scheduler_.get());
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
DoMainFrame();
SimulateExpensiveTasks(timer_task_runner_);
SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START);
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);
EXPECT_EQ(UseCase::SYNCHRONIZED_GESTURE,
ForceUpdatePolicyAndGetCurrentUseCase());
std::vector<std::string> run_order;
PostTestTasks(&run_order, "T1 T2");
RunUntilIdle();
EXPECT_EQ(0u, run_order.size());
EXPECT_EQ(0u, web_view_scheduler.console_warnings().size());
}
namespace {
void SlowCountingTask(size_t* count,
base::SimpleTestTickClock* clock,
int task_duration,
scoped_refptr<base::SingleThreadTaskRunner> timer_queue) {
clock->Advance(base::TimeDelta::FromMilliseconds(task_duration));
if (++(*count) < 500) {
timer_queue->PostTask(FROM_HERE, base::Bind(SlowCountingTask, count, clock,
task_duration, timer_queue));
}
}
}
TEST_F(RendererSchedulerImplTest,
SYNCHRONIZED_GESTURE_TimerTaskThrottling_task_expensive) {
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
base::TimeTicks first_throttled_run_time =
ThrottlingHelper::ThrottledRunTime(clock_->NowTicks());
size_t count = 0;
// With the compositor task taking 10ms, there is not enough time to run this
// 7ms timer task in the 16ms frame.
scheduler_->TimerTaskRunner()->PostTask(
FROM_HERE, base::Bind(SlowCountingTask, &count, clock_.get(), 7,
scheduler_->TimerTaskRunner()));
for (int i = 0; i < 1000; i++) {
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);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollUpdate),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
simulate_compositor_task_ran_ = false;
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RendererSchedulerImplTest::SimulateMainThreadCompositorTask,
base::Unretained(this),
base::TimeDelta::FromMilliseconds(10)));
mock_task_runner_->RunTasksWhile(
base::Bind(&RendererSchedulerImplTest::SimulatedCompositorTaskPending,
base::Unretained(this)));
EXPECT_EQ(UseCase::SYNCHRONIZED_GESTURE, CurrentUseCase()) << "i = " << i;
// Before the policy is updated the queue will be enabled. Subsequently it
// will be disabled until the throttled queue is pumped.
bool expect_queue_enabled =
(i == 0) || (clock_->NowTicks() > first_throttled_run_time);
EXPECT_EQ(expect_queue_enabled,
scheduler_->TimerTaskRunner()->IsQueueEnabled())
<< "i = " << i;
}
// Task is throttled but not completely blocked.
EXPECT_EQ(12u, count);
}
TEST_F(RendererSchedulerImplTest,
SYNCHRONIZED_GESTURE_TimerTaskThrottling_TimersSuspended) {
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
base::TimeTicks first_throttled_run_time =
ThrottlingHelper::ThrottledRunTime(clock_->NowTicks());
size_t count = 0;
// With the compositor task taking 10ms, there is not enough time to run this
// 7ms timer task in the 16ms frame.
scheduler_->TimerTaskRunner()->PostTask(
FROM_HERE, base::Bind(SlowCountingTask, &count, clock_.get(), 7,
scheduler_->TimerTaskRunner()));
bool suspended = false;
for (int i = 0; i < 1000; i++) {
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);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollUpdate),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
simulate_compositor_task_ran_ = false;
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RendererSchedulerImplTest::SimulateMainThreadCompositorTask,
base::Unretained(this),
base::TimeDelta::FromMilliseconds(10)));
mock_task_runner_->RunTasksWhile(
base::Bind(&RendererSchedulerImplTest::SimulatedCompositorTaskPending,
base::Unretained(this)));
EXPECT_EQ(UseCase::SYNCHRONIZED_GESTURE, CurrentUseCase()) << "i = " << i;
// Before the policy is updated the queue will be enabled. Subsequently it
// will be disabled until the throttled queue is pumped.
bool expect_queue_enabled =
(i == 0) || (clock_->NowTicks() > first_throttled_run_time);
if (suspended)
expect_queue_enabled = false;
EXPECT_EQ(expect_queue_enabled,
scheduler_->TimerTaskRunner()->IsQueueEnabled())
<< "i = " << i;
// After we've run any expensive tasks suspend the queue. The throttling
// helper should /not/ re-enable this queue under any circumstances while
// timers are suspended.
if (count > 0 && !suspended) {
EXPECT_EQ(2u, count);
scheduler_->SuspendTimerQueue();
suspended = true;
}
}
// Make sure the timer queue stayed suspended!
EXPECT_EQ(2u, count);
}
TEST_F(RendererSchedulerImplTest,
SYNCHRONIZED_GESTURE_TimerTaskThrottling_task_not_expensive) {
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
size_t count = 0;
// With the compositor task taking 10ms, there is enough time to run this 6ms
// timer task in the 16ms frame.
scheduler_->TimerTaskRunner()->PostTask(
FROM_HERE, base::Bind(SlowCountingTask, &count, clock_.get(), 6,
scheduler_->TimerTaskRunner()));
for (int i = 0; i < 1000; i++) {
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);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollUpdate),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
simulate_compositor_task_ran_ = false;
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RendererSchedulerImplTest::SimulateMainThreadCompositorTask,
base::Unretained(this),
base::TimeDelta::FromMilliseconds(10)));
mock_task_runner_->RunTasksWhile(
base::Bind(&RendererSchedulerImplTest::SimulatedCompositorTaskPending,
base::Unretained(this)));
EXPECT_EQ(UseCase::SYNCHRONIZED_GESTURE, CurrentUseCase()) << "i = " << i;
EXPECT_TRUE(scheduler_->TimerTaskRunner()->IsQueueEnabled()) << "i = " << i;
}
// Task is not throttled.
EXPECT_EQ(500u, count);
}
TEST_F(RendererSchedulerImplTest,
ExpensiveTimerTaskBlocked_SYNCHRONIZED_GESTURE_TouchStartExpected) {
SimulateExpensiveTasks(timer_task_runner_);
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
ForceTouchStartToBeExpectedSoon();
// Bump us into SYNCHRONIZED_GESTURE.
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollUpdate),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
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);
EXPECT_EQ(UseCase::SYNCHRONIZED_GESTURE,
ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_TRUE(TimerTasksSeemExpensive());
EXPECT_TRUE(TouchStartExpectedSoon());
EXPECT_FALSE(scheduler_->TimerTaskRunner()->IsQueueEnabled());
}
TEST_F(RendererSchedulerImplTest, DenyLongIdleDuringTouchStart) {
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchStart),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
EXPECT_EQ(UseCase::TOUCHSTART, ForceUpdatePolicyAndGetCurrentUseCase());
// First check that long idle is denied during the TOUCHSTART use case.
IdleHelper::Delegate* idle_delegate = scheduler_.get();
base::TimeTicks now;
base::TimeDelta next_time_to_check;
EXPECT_FALSE(idle_delegate->CanEnterLongIdlePeriod(now, &next_time_to_check));
EXPECT_GE(next_time_to_check, base::TimeDelta());
// Check again at a time past the TOUCHSTART expiration. We should still get a
// non-negative delay to when to check again.
now += base::TimeDelta::FromMilliseconds(500);
EXPECT_FALSE(idle_delegate->CanEnterLongIdlePeriod(now, &next_time_to_check));
EXPECT_GE(next_time_to_check, base::TimeDelta());
}
TEST_F(RendererSchedulerImplTest, TestCompositorPolicy_TouchStartDuringFling) {
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
scheduler_->DidAnimateForInputOnCompositorThread();
// Note DidAnimateForInputOnCompositorThread does not by itself trigger a
// policy update.
EXPECT_EQ(RendererSchedulerImpl::UseCase::COMPOSITOR_GESTURE,
ForceUpdatePolicyAndGetCurrentUseCase());
// Make sure TouchStart causes a policy change.
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchStart),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
EXPECT_EQ(RendererSchedulerImpl::UseCase::TOUCHSTART,
ForceUpdatePolicyAndGetCurrentUseCase());
}
TEST_F(RendererSchedulerImplTest, SYNCHRONIZED_GESTURE_CompositingExpensive) {
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
// With the compositor task taking 20ms, there is not enough time to run
// other tasks in the same 16ms frame. To avoid starvation, compositing tasks
// should therefore not get prioritized.
std::vector<std::string> run_order;
for (int i = 0; i < 1000; i++)
PostTestTasks(&run_order, "T1");
for (int i = 0; i < 100; i++) {
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);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollUpdate),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
simulate_compositor_task_ran_ = false;
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RendererSchedulerImplTest::SimulateMainThreadCompositorTask,
base::Unretained(this),
base::TimeDelta::FromMilliseconds(20)));
mock_task_runner_->RunTasksWhile(
base::Bind(&RendererSchedulerImplTest::SimulatedCompositorTaskPending,
base::Unretained(this)));
EXPECT_EQ(UseCase::SYNCHRONIZED_GESTURE, CurrentUseCase()) << "i = " << i;
}
// Timer tasks should not have been starved by the expensive compositor
// tasks.
EXPECT_EQ(TaskQueue::NORMAL_PRIORITY,
scheduler_->CompositorTaskRunner()->GetQueuePriority());
EXPECT_EQ(1000u, run_order.size());
}
TEST_F(RendererSchedulerImplTest, MAIN_THREAD_CUSTOM_INPUT_HANDLING) {
SimulateMainThreadGestureStart(TouchEventPolicy::SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollBegin);
// With the compositor task taking 20ms, there is not enough time to run
// other tasks in the same 16ms frame. To avoid starvation, compositing tasks
// should therefore not get prioritized.
std::vector<std::string> run_order;
for (int i = 0; i < 1000; i++)
PostTestTasks(&run_order, "T1");
for (int i = 0; i < 100; i++) {
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);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchMove),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
simulate_compositor_task_ran_ = false;
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RendererSchedulerImplTest::SimulateMainThreadCompositorTask,
base::Unretained(this),
base::TimeDelta::FromMilliseconds(20)));
mock_task_runner_->RunTasksWhile(
base::Bind(&RendererSchedulerImplTest::SimulatedCompositorTaskPending,
base::Unretained(this)));
EXPECT_EQ(UseCase::MAIN_THREAD_CUSTOM_INPUT_HANDLING, CurrentUseCase())
<< "i = " << i;
}
// Timer tasks should not have been starved by the expensive compositor
// tasks.
EXPECT_EQ(TaskQueue::NORMAL_PRIORITY,
scheduler_->CompositorTaskRunner()->GetQueuePriority());
EXPECT_EQ(1000u, run_order.size());
}
TEST_F(RendererSchedulerImplTest, MAIN_THREAD_GESTURE) {
SimulateMainThreadGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START,
blink::WebInputEvent::GestureScrollBegin);
// With the compositor task taking 20ms, there is not enough time to run
// other tasks in the same 16ms frame. However because this is a main thread
// gesture instead of custom main thread input handling, we allow the timer
// tasks to be starved.
std::vector<std::string> run_order;
for (int i = 0; i < 1000; i++)
PostTestTasks(&run_order, "T1");
for (int i = 0; i < 100; i++) {
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);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollUpdate),
RendererScheduler::InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
simulate_compositor_task_ran_ = false;
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RendererSchedulerImplTest::SimulateMainThreadCompositorTask,
base::Unretained(this),
base::TimeDelta::FromMilliseconds(20)));
mock_task_runner_->RunTasksWhile(
base::Bind(&RendererSchedulerImplTest::SimulatedCompositorTaskPending,
base::Unretained(this)));
EXPECT_EQ(UseCase::MAIN_THREAD_GESTURE, CurrentUseCase()) << "i = " << i;
}
EXPECT_EQ(TaskQueue::HIGH_PRIORITY,
scheduler_->CompositorTaskRunner()->GetQueuePriority());
EXPECT_EQ(279u, run_order.size());
}
class MockRAILModeObserver : public RendererScheduler::RAILModeObserver {
public:
MOCK_METHOD1(OnRAILModeChanged, void(v8::RAILMode rail_mode));
};
TEST_F(RendererSchedulerImplTest, TestResponseRAILMode) {
MockRAILModeObserver observer;
scheduler_->SetRAILModeObserver(&observer);
EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_RESPONSE));
scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
ForceTouchStartToBeExpectedSoon();
EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_EQ(v8::PERFORMANCE_RESPONSE, RAILMode());
scheduler_->SetRAILModeObserver(nullptr);
}
TEST_F(RendererSchedulerImplTest, TestAnimateRAILMode) {
MockRAILModeObserver observer;
scheduler_->SetRAILModeObserver(&observer);
EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_ANIMATION)).Times(0);
EXPECT_FALSE(BeginFrameNotExpectedSoon());
EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_EQ(v8::PERFORMANCE_ANIMATION, RAILMode());
scheduler_->SetRAILModeObserver(nullptr);
}
TEST_F(RendererSchedulerImplTest, TestIdleRAILMode) {
MockRAILModeObserver observer;
scheduler_->SetRAILModeObserver(&observer);
EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_ANIMATION));
EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_IDLE));
scheduler_->SetAllRenderWidgetsHidden(true);
EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_EQ(v8::PERFORMANCE_IDLE, RAILMode());
scheduler_->SetAllRenderWidgetsHidden(false);
EXPECT_EQ(UseCase::NONE, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_EQ(v8::PERFORMANCE_ANIMATION, RAILMode());
scheduler_->SetRAILModeObserver(nullptr);
}
TEST_F(RendererSchedulerImplTest, UnthrottledTaskRunner) {
// Ensure neither suspension nor timer task throttling affects an unthrottled
// task runner.
SimulateCompositorGestureStart(TouchEventPolicy::SEND_TOUCH_START);
scoped_refptr<TaskQueue> unthrottled_task_runner =
scheduler_->NewUnthrottledTaskRunner("unthrottled_tq");
size_t timer_count = 0;
size_t unthrottled_count = 0;
scheduler_->TimerTaskRunner()->PostTask(
FROM_HERE, base::Bind(SlowCountingTask, &timer_count, clock_.get(), 7,
scheduler_->TimerTaskRunner()));
unthrottled_task_runner->PostTask(
FROM_HERE, base::Bind(SlowCountingTask, &unthrottled_count, clock_.get(),
7, unthrottled_task_runner));
scheduler_->SuspendTimerQueue();
for (int i = 0; i < 1000; i++) {
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);
scheduler_->DidHandleInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::GestureScrollUpdate),
RendererScheduler::InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
simulate_compositor_task_ran_ = false;
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RendererSchedulerImplTest::SimulateMainThreadCompositorTask,
base::Unretained(this),
base::TimeDelta::FromMilliseconds(10)));
mock_task_runner_->RunTasksWhile(
base::Bind(&RendererSchedulerImplTest::SimulatedCompositorTaskPending,
base::Unretained(this)));
EXPECT_EQ(UseCase::SYNCHRONIZED_GESTURE, CurrentUseCase()) << "i = " << i;
}
EXPECT_EQ(0u, timer_count);
EXPECT_EQ(500u, unthrottled_count);
}
TEST_F(RendererSchedulerImplTest, EnableVirtualTime) {
scheduler_->EnableVirtualTime();
scoped_refptr<TaskQueue> loading_tq =
scheduler_->NewLoadingTaskRunner("test");
scoped_refptr<TaskQueue> timer_tq = scheduler_->NewTimerTaskRunner("test");
scoped_refptr<TaskQueue> unthrottled_tq =
scheduler_->NewUnthrottledTaskRunner("test");
EXPECT_EQ(scheduler_->DefaultTaskRunner()->GetTimeDomain(),
scheduler_->GetVirtualTimeDomain());
EXPECT_EQ(scheduler_->CompositorTaskRunner()->GetTimeDomain(),
scheduler_->GetVirtualTimeDomain());
EXPECT_EQ(scheduler_->LoadingTaskRunner()->GetTimeDomain(),
scheduler_->GetVirtualTimeDomain());
EXPECT_EQ(scheduler_->TimerTaskRunner()->GetTimeDomain(),
scheduler_->GetVirtualTimeDomain());
EXPECT_EQ(loading_tq->GetTimeDomain(), scheduler_->GetVirtualTimeDomain());
EXPECT_EQ(timer_tq->GetTimeDomain(), scheduler_->GetVirtualTimeDomain());
EXPECT_EQ(unthrottled_tq->GetTimeDomain(),
scheduler_->GetVirtualTimeDomain());
EXPECT_EQ(scheduler_->NewLoadingTaskRunner("test")->GetTimeDomain(),
scheduler_->GetVirtualTimeDomain());
EXPECT_EQ(scheduler_->NewTimerTaskRunner("test")->GetTimeDomain(),
scheduler_->GetVirtualTimeDomain());
EXPECT_EQ(scheduler_->NewUnthrottledTaskRunner("test")->GetTimeDomain(),
scheduler_->GetVirtualTimeDomain());
}
} // namespace scheduler