| Index: content/renderer/scheduler/renderer_scheduler_impl_unittest.cc
|
| diff --git a/content/renderer/scheduler/renderer_scheduler_impl_unittest.cc b/content/renderer/scheduler/renderer_scheduler_impl_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..701a222c71ed724c974382bfff62e9509a68bfe9
|
| --- /dev/null
|
| +++ b/content/renderer/scheduler/renderer_scheduler_impl_unittest.cc
|
| @@ -0,0 +1,411 @@
|
| +// 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)));
|
| + 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();
|
| + }
|
| + *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)));
|
| + 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)));
|
| + 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();
|
| + 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,
|
| + 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();
|
| + 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();
|
| + 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
|
|
|