Chromium Code Reviews| 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..ac2126960c6e5c3504aa2088facc1ade2967a9eb |
| --- /dev/null |
| +++ b/content/renderer/scheduler/renderer_scheduler_impl_unittest.cc |
| @@ -0,0 +1,422 @@ |
| +// 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 "base/test/simple_test_tick_clock.h" |
| +#include "base/test/test_simple_task_runner.h" |
| +#include "cc/output/begin_frame_args.h" |
| +#include "testing/gmock/include/gmock/gmock.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace content { |
| + |
| +class DelaySupportingTestSimpleTaskRunner : public base::TestSimpleTaskRunner { |
| + public: |
| + DelaySupportingTestSimpleTaskRunner(base::SimpleTestTickClock* clock) |
| + : clock_(clock) {} |
| + |
| + virtual bool PostDelayedTask(const tracked_objects::Location& from_here, |
| + const base::Closure& task, |
| + base::TimeDelta delay) override { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + pending_tasks_.push_back( |
| + base::TestPendingTask(from_here, |
| + task, |
| + clock_->NowTicks(), |
| + delay, |
| + base::TestPendingTask::NESTABLE)); |
| + return true; |
| + } |
| + |
| + virtual bool PostNonNestableDelayedTask( |
| + const tracked_objects::Location& from_here, |
| + const base::Closure& task, |
| + base::TimeDelta delay) override { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + pending_tasks_.push_back( |
| + base::TestPendingTask(from_here, |
| + task, |
| + clock_->NowTicks(), |
| + delay, |
| + base::TestPendingTask::NON_NESTABLE)); |
| + return true; |
| + } |
| + |
| + bool HaveRunnablePendingTasks() { |
|
Sami
2014/10/27 12:02:38
nit: const
rmcilroy
2014/10/27 16:25:01
Moved to using cc::OrderedSimpleTaskRunner (thanks
|
| + for (base::TestPendingTask pending_task : pending_tasks_) { |
|
Sami
2014/10/27 12:02:38
nit: const ref (or const auto&).
rmcilroy
2014/10/27 16:25:01
ditto
|
| + if (pending_task.GetTimeToRun() <= clock_->NowTicks()) { |
| + return true; |
| + } |
| + } |
| + return false; |
| + } |
| + |
| + void RunPendingTasksIfNotDelayed() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + // Swap with a local variable to avoid re-entrancy problems. |
| + std::deque<base::TestPendingTask> tasks_to_run; |
| + tasks_to_run.swap(pending_tasks_); |
| + for (base::TestPendingTask pending_task : tasks_to_run) { |
|
Sami
2014/10/27 12:02:38
nit: const ref (or const auto&).
rmcilroy
2014/10/27 16:25:01
ditto
|
| + if (pending_task.GetTimeToRun() <= clock_->NowTicks()) { |
| + pending_task.task.Run(); |
| + } else { |
| + pending_tasks_.push_front(pending_task); |
|
Sami
2014/10/27 12:02:38
I think this is slightly wrong since delayed tasks
rmcilroy
2014/10/27 16:25:01
ditto
|
| + } |
| + } |
| + } |
| + |
| + void RunUntilTasksAreAllDelayed() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + while (!pending_tasks_.empty() && HaveRunnablePendingTasks()) { |
| + RunPendingTasksIfNotDelayed(); |
| + } |
| + } |
| + |
| + protected: |
| + virtual ~DelaySupportingTestSimpleTaskRunner() {} |
| + |
| + private: |
| + base::SimpleTestTickClock* clock_; |
| +}; |
| + |
| +class RendererSchedulerImplForTest : public RendererSchedulerImpl { |
| + public: |
| + RendererSchedulerImplForTest( |
| + scoped_refptr<base::TestSimpleTaskRunner> task_runner, |
| + base::SimpleTestTickClock* clock) |
| + : RendererSchedulerImpl(task_runner), clock_(clock) {} |
| + virtual ~RendererSchedulerImplForTest() {} |
| + |
| + protected: |
| + virtual base::TimeTicks Now() const override { return clock_->NowTicks(); } |
| + |
| + private: |
| + base::SimpleTestTickClock* clock_; |
| +}; |
| + |
| +class RendererSchedulerImplTest : public testing::Test { |
| + public: |
| + RendererSchedulerImplTest() |
| + : clock_(new base::SimpleTestTickClock()), |
| + mock_task_runner_( |
| + new DelaySupportingTestSimpleTaskRunner(clock_.get())), |
| + scheduler_( |
| + new RendererSchedulerImplForTest(mock_task_runner_, clock_.get())), |
| + default_task_runner_(scheduler_->DefaultTaskRunner()), |
| + compositor_task_runner_(scheduler_->CompositorTaskRunner()), |
| + idle_task_runner_(scheduler_->IdleTaskRunner()) {} |
| + virtual ~RendererSchedulerImplTest() {} |
| + |
| + void RunUntilIdle() { mock_task_runner_->RunUntilTasksAreAllDelayed(); } |
| + |
| + void EnableIdleTasks() { |
| + scheduler_->WillBeginFrame( |
| + cc::BeginFrameArgs::Create(clock_->NowTicks(), |
| + base::TimeTicks(), |
| + base::TimeDelta::FromMilliseconds(1000))); |
| + scheduler_->DidCommitFrameToCompositor(); |
| + } |
| + |
| + protected: |
| + virtual void SetUp() override { |
| + // Ensure clock is non-zero. |
| + clock_->Advance(base::TimeDelta::FromMilliseconds(5000)); |
| + } |
| + |
| + scoped_ptr<base::SimpleTestTickClock> clock_; |
| + scoped_refptr<DelaySupportingTestSimpleTaskRunner> 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 expected_deadline, |
| + base::TimeTicks deadline) { |
| + EXPECT_FALSE(*task_run); |
| + EXPECT_EQ(expected_deadline, 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)++; |
| +} |
| + |
| +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_->NowTicks() + base::TimeDelta::FromMilliseconds(2300); |
| + |
| + clock_->Advance(base::TimeDelta::FromMilliseconds(100)); |
| + idle_task_runner_->PostIdleTask( |
| + FROM_HERE, base::Bind(&IdleTestTask, &task_run, expected_deadline)); |
| + |
| + RunUntilIdle(); |
| + EXPECT_FALSE(task_run); // Shouldn't run yet as no WillBeginFrame. |
| + |
| + scheduler_->WillBeginFrame( |
| + cc::BeginFrameArgs::Create(clock_->NowTicks(), |
| + base::TimeTicks(), |
| + base::TimeDelta::FromMilliseconds(1000))); |
| + RunUntilIdle(); |
| + EXPECT_FALSE(task_run); // Shouldn't run as no didCommitFrameToCompositor. |
|
Sami
2014/10/27 12:02:38
s/did/Did/
rmcilroy
2014/10/27 16:25:01
Done.
|
| + |
| + clock_->Advance(base::TimeDelta::FromMilliseconds(1200)); |
| + scheduler_->DidCommitFrameToCompositor(); |
| + RunUntilIdle(); |
| + EXPECT_FALSE(task_run); // We missed the deadline. |
| + |
| + scheduler_->WillBeginFrame( |
| + cc::BeginFrameArgs::Create(clock_->NowTicks(), |
| + base::TimeTicks(), |
| + base::TimeDelta::FromMilliseconds(1000))); |
| + clock_->Advance(base::TimeDelta::FromMilliseconds(800)); |
| + scheduler_->DidCommitFrameToCompositor(); |
| + RunUntilIdle(); |
| + EXPECT_TRUE(task_run); |
|
Sami
2014/10/27 12:02:38
Could you also check that the deadline was given t
rmcilroy
2014/10/27 16:25:01
The deadline was checked in IdleTestTask. Moved t
Sami
2014/10/27 18:56:29
Ah, missed it. Probably good that you moved it her
|
| +} |
| + |
| +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, 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_->DidReceiveInputEvent(); |
| + 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_->DidReceiveInputEvent(); |
| + 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_->DidReceiveInputEvent(); |
| + RunUntilIdle(); |
| + EXPECT_THAT(order, |
| + testing::ElementsAre(std::string("C1"), |
| + std::string("C2"), |
| + std::string("D1"), |
| + std::string("D2"))); |
| + |
| + order.clear(); |
| + clock_->Advance(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"))); |
| +} |
| + |
| +} // namespace content |