blob: 092440cf66dee0f72cda234e5f05c5aa26d7744c [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 "content/renderer/scheduler/renderer_scheduler_impl.h"
#include "base/callback.h"
#include "cc/output/begin_frame_args.h"
#include "cc/test/ordered_simple_task_runner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
class RendererSchedulerImplForTest : public RendererSchedulerImpl {
public:
RendererSchedulerImplForTest(
scoped_refptr<cc::OrderedSimpleTaskRunner> task_runner,
scoped_refptr<cc::TestNowSource> clock)
: RendererSchedulerImpl(task_runner), clock_(clock) {}
~RendererSchedulerImplForTest() override {}
protected:
base::TimeTicks Now() const override { return clock_->Now(); }
private:
scoped_refptr<cc::TestNowSource> clock_;
};
class RendererSchedulerImplTest : public testing::Test {
public:
RendererSchedulerImplTest()
: clock_(cc::TestNowSource::Create(5000)),
mock_task_runner_(new cc::OrderedSimpleTaskRunner(clock_, false)),
scheduler_(new RendererSchedulerImplForTest(mock_task_runner_, clock_)),
default_task_runner_(scheduler_->DefaultTaskRunner()),
compositor_task_runner_(scheduler_->CompositorTaskRunner()),
idle_task_runner_(scheduler_->IdleTaskRunner()) {}
~RendererSchedulerImplTest() override {}
void RunUntilIdle() { mock_task_runner_->RunUntilIdle(); }
void EnableIdleTasks() {
scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create(
clock_->Now(), base::TimeTicks(),
base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL));
scheduler_->DidCommitFrameToCompositor();
}
protected:
scoped_refptr<cc::TestNowSource> clock_;
scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
scoped_ptr<RendererSchedulerImpl> scheduler_;
scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_;
DISALLOW_COPY_AND_ASSIGN(RendererSchedulerImplTest);
};
void NullTask() {
}
void OrderedTestTask(int value, int* result) {
*result = (*result << 4) | value;
}
void UnorderedTestTask(int value, int* result) {
*result += value;
}
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 AppendToVectorReentrantTask(
scoped_refptr<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, task_runner, vector,
reentrant_count, max_reentrant_count));
}
}
void IdleTestTask(bool* task_run,
base::TimeTicks* deadline_out,
base::TimeTicks deadline) {
EXPECT_FALSE(*task_run);
*deadline_out = deadline;
*task_run = true;
}
void RepostingIdleTestTask(
scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner,
int* run_count,
base::TimeTicks deadline) {
if (*run_count == 0) {
idle_task_runner->PostIdleTask(
FROM_HERE,
base::Bind(&RepostingIdleTestTask, idle_task_runner, run_count));
}
(*run_count)++;
}
void UpdateClockToDeadlineIdleTestTask(
scoped_refptr<cc::TestNowSource> clock,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
int* run_count,
base::TimeTicks deadline) {
clock->SetNow(deadline);
// Due to the way in which OrderedSimpleTestRunner orders tasks and the fact
// that we updated the time within a task, the delayed pending task to call
// EndIdlePeriod will not happen until after a TaskQueueManager DoWork, so
// post a normal task here to ensure it runs before the next idle task.
task_runner->PostTask(FROM_HERE, base::Bind(NullTask));
(*run_count)++;
}
void PostingYieldingTestTask(
RendererSchedulerImpl* scheduler,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
bool simulate_input,
bool* should_yield_before,
bool* should_yield_after) {
*should_yield_before = scheduler->ShouldYieldForHighPriorityWork();
task_runner->PostTask(FROM_HERE, base::Bind(NullTask));
if (simulate_input) {
scheduler->DidReceiveInputEventOnCompositorThread(
blink::WebInputEvent::GestureFlingStart);
}
*should_yield_after = scheduler->ShouldYieldForHighPriorityWork();
}
TEST_F(RendererSchedulerImplTest, TestPostDefaultTask) {
int result = 0;
default_task_runner_->PostTask(FROM_HERE,
base::Bind(OrderedTestTask, 1, &result));
default_task_runner_->PostTask(FROM_HERE,
base::Bind(OrderedTestTask, 2, &result));
default_task_runner_->PostTask(FROM_HERE,
base::Bind(OrderedTestTask, 3, &result));
default_task_runner_->PostTask(FROM_HERE,
base::Bind(OrderedTestTask, 4, &result));
RunUntilIdle();
EXPECT_EQ(0x1234, result);
}
TEST_F(RendererSchedulerImplTest, TestPostDefaultAndCompositor) {
int result = 0;
default_task_runner_->PostTask(FROM_HERE,
base::Bind(&UnorderedTestTask, 1, &result));
compositor_task_runner_->PostTask(FROM_HERE,
base::Bind(&UnorderedTestTask, 2, &result));
RunUntilIdle();
EXPECT_EQ(3, result);
}
TEST_F(RendererSchedulerImplTest, TestRentrantTask) {
int count = 0;
std::vector<int> order;
default_task_runner_->PostTask(
FROM_HERE, base::Bind(AppendToVectorReentrantTask, default_task_runner_,
&order, &count, 5));
RunUntilIdle();
EXPECT_THAT(order, testing::ElementsAre(0, 1, 2, 3, 4));
}
TEST_F(RendererSchedulerImplTest, TestPostIdleTask) {
bool task_run = false;
base::TimeTicks expected_deadline =
clock_->Now() + base::TimeDelta::FromMilliseconds(2300);
base::TimeTicks deadline_in_task;
clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(100));
idle_task_runner_->PostIdleTask(
FROM_HERE, base::Bind(&IdleTestTask, &task_run, &deadline_in_task));
RunUntilIdle();
EXPECT_FALSE(task_run); // Shouldn't run yet as no WillBeginFrame.
scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create(
clock_->Now(), base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
cc::BeginFrameArgs::NORMAL));
RunUntilIdle();
EXPECT_FALSE(task_run); // Shouldn't run as no DidCommitFrameToCompositor.
clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(1200));
scheduler_->DidCommitFrameToCompositor();
RunUntilIdle();
EXPECT_FALSE(task_run); // We missed the deadline.
scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create(
clock_->Now(), base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
cc::BeginFrameArgs::NORMAL));
clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(800));
scheduler_->DidCommitFrameToCompositor();
RunUntilIdle();
EXPECT_TRUE(task_run);
EXPECT_EQ(expected_deadline, deadline_in_task);
}
TEST_F(RendererSchedulerImplTest, TestRepostingIdleTask) {
int run_count = 0;
idle_task_runner_->PostIdleTask(
FROM_HERE,
base::Bind(&RepostingIdleTestTask, idle_task_runner_, &run_count));
EnableIdleTasks();
RunUntilIdle();
EXPECT_EQ(1, run_count);
// Reposted tasks shouldn't run until next idle period.
RunUntilIdle();
EXPECT_EQ(1, run_count);
EnableIdleTasks();
RunUntilIdle();
EXPECT_EQ(2, run_count);
}
TEST_F(RendererSchedulerImplTest, TestIdleTaskExceedsDeadline) {
mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
int run_count = 0;
// Post two UpdateClockToDeadlineIdleTestTask tasks.
idle_task_runner_->PostIdleTask(
FROM_HERE, base::Bind(&UpdateClockToDeadlineIdleTestTask, clock_,
default_task_runner_, &run_count));
idle_task_runner_->PostIdleTask(
FROM_HERE, base::Bind(&UpdateClockToDeadlineIdleTestTask, clock_,
default_task_runner_, &run_count));
EnableIdleTasks();
RunUntilIdle();
// Only the first idle task should execute since it's used up the deadline.
EXPECT_EQ(1, run_count);
EnableIdleTasks();
RunUntilIdle();
// Second task should be run on the next idle period.
EXPECT_EQ(2, run_count);
}
TEST_F(RendererSchedulerImplTest, TestDefaultPolicy) {
std::vector<std::string> order;
idle_task_runner_->PostIdleTask(
FROM_HERE,
base::Bind(&AppendToVectorIdleTestTask, &order, std::string("I1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D1")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D2")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C2")));
EnableIdleTasks();
RunUntilIdle();
EXPECT_THAT(order, testing::ElementsAre(std::string("D1"), std::string("C1"),
std::string("D2"), std::string("C2"),
std::string("I1")));
}
TEST_F(RendererSchedulerImplTest, TestCompositorPolicy) {
std::vector<std::string> order;
idle_task_runner_->PostIdleTask(
FROM_HERE,
base::Bind(&AppendToVectorIdleTestTask, &order, std::string("I1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D1")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D2")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C2")));
scheduler_->DidReceiveInputEventOnCompositorThread(
blink::WebInputEvent::GestureFlingStart);
EnableIdleTasks();
RunUntilIdle();
EXPECT_THAT(order, testing::ElementsAre(std::string("C1"), std::string("C2"),
std::string("D1"), std::string("D2"),
std::string("I1")));
}
TEST_F(RendererSchedulerImplTest, TestCompositorPolicy_DidAnimateForInput) {
std::vector<std::string> order;
idle_task_runner_->PostIdleTask(
FROM_HERE,
base::Bind(&AppendToVectorIdleTestTask, &order, std::string("I1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D1")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D2")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C2")));
scheduler_->DidAnimateForInputOnCompositorThread();
EnableIdleTasks();
RunUntilIdle();
EXPECT_THAT(order, testing::ElementsAre(std::string("C1"), std::string("C2"),
std::string("D1"), std::string("D2"),
std::string("I1")));
}
TEST_F(RendererSchedulerImplTest,
DidReceiveInputEventOnCompositorThread_IgnoresMouseEvents) {
std::vector<std::string> order;
idle_task_runner_->PostIdleTask(
FROM_HERE,
base::Bind(&AppendToVectorIdleTestTask, &order, std::string("I1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D1")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D2")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C2")));
scheduler_->DidReceiveInputEventOnCompositorThread(
blink::WebInputEvent::MouseMove);
EnableIdleTasks();
RunUntilIdle();
// Note compositor tasks are not prioritized.
EXPECT_THAT(order, testing::ElementsAre(std::string("D1"), std::string("C1"),
std::string("D2"), std::string("C2"),
std::string("I1")));
}
TEST_F(RendererSchedulerImplTest,
DidReceiveInputEventOnCompositorThread_IgnoresKeyboardEvents) {
std::vector<std::string> order;
idle_task_runner_->PostIdleTask(
FROM_HERE,
base::Bind(&AppendToVectorIdleTestTask, &order, std::string("I1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D1")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D2")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C2")));
scheduler_->DidReceiveInputEventOnCompositorThread(
blink::WebInputEvent::KeyDown);
EnableIdleTasks();
RunUntilIdle();
// Note compositor tasks are not prioritized.
EXPECT_THAT(order, testing::ElementsAre(std::string("D1"), std::string("C1"),
std::string("D2"), std::string("C2"),
std::string("I1")));
}
TEST_F(RendererSchedulerImplTest,
TestCompositorPolicyDoesNotStarveDefaultTasks) {
std::vector<std::string> order;
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D1")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C1")));
for (int i = 0; i < 20; i++) {
compositor_task_runner_->PostTask(FROM_HERE, base::Bind(&NullTask));
}
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C2")));
scheduler_->DidReceiveInputEventOnCompositorThread(
blink::WebInputEvent::GestureFlingStart);
RunUntilIdle();
// Ensure that the default D1 task gets to run at some point before the final
// C2 compositor task.
EXPECT_THAT(order, testing::ElementsAre(std::string("C1"), std::string("D1"),
std::string("C2")));
}
TEST_F(RendererSchedulerImplTest, TestCompositorPolicyEnds) {
std::vector<std::string> order;
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D1")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D2")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C2")));
scheduler_->DidReceiveInputEventOnCompositorThread(
blink::WebInputEvent::GestureFlingStart);
RunUntilIdle();
EXPECT_THAT(order,
testing::ElementsAre(std::string("C1"), std::string("C2"),
std::string("D1"), std::string("D2")));
order.clear();
clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(1000));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D1")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C1")));
default_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("D2")));
compositor_task_runner_->PostTask(
FROM_HERE,
base::Bind(&AppendToVectorTestTask, &order, std::string("C2")));
// Compositor policy mode should have ended now that the clock has advanced.
RunUntilIdle();
EXPECT_THAT(order,
testing::ElementsAre(std::string("D1"), std::string("C1"),
std::string("D2"), std::string("C2")));
}
TEST_F(RendererSchedulerImplTest, TestShouldYield) {
bool should_yield_before = false;
bool should_yield_after = false;
default_task_runner_->PostTask(
FROM_HERE, base::Bind(&PostingYieldingTestTask, scheduler_.get(),
default_task_runner_, false, &should_yield_before,
&should_yield_after));
RunUntilIdle();
// Posting to default runner shouldn't cause yielding.
EXPECT_FALSE(should_yield_before);
EXPECT_FALSE(should_yield_after);
default_task_runner_->PostTask(
FROM_HERE, base::Bind(&PostingYieldingTestTask, scheduler_.get(),
compositor_task_runner_, false,
&should_yield_before, &should_yield_after));
RunUntilIdle();
// Posting while not in compositor priority shouldn't cause yielding.
EXPECT_FALSE(should_yield_before);
EXPECT_FALSE(should_yield_after);
default_task_runner_->PostTask(
FROM_HERE, base::Bind(&PostingYieldingTestTask, scheduler_.get(),
compositor_task_runner_, true, &should_yield_before,
&should_yield_after));
RunUntilIdle();
// We should be able to switch to compositor priority mid-task.
EXPECT_FALSE(should_yield_before);
EXPECT_TRUE(should_yield_after);
}
} // namespace content