Index: content/renderer/scheduler/renderer_scheduler_unittest.cc |
diff --git a/content/renderer/scheduler/renderer_scheduler_unittest.cc b/content/renderer/scheduler/renderer_scheduler_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8114ff5956f68e1ca11e5c2d3cf74a36c0d0da17 |
--- /dev/null |
+++ b/content/renderer/scheduler/renderer_scheduler_unittest.cc |
@@ -0,0 +1,290 @@ |
+// 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.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 { |
Sami
2014/10/24 10:46:13
I wonder if we could just reuse cc/test/ordered_si
rmcilroy
2014/10/24 14:58:29
I'll look into whether this is possible.
|
+ 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; |
+ } |
+ |
+ 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 (std::deque<base::TestPendingTask>::iterator it = tasks_to_run.begin(); |
+ it != tasks_to_run.end(); ++it) { |
+ if (it->GetTimeToRun() <= clock_->NowTicks()) { |
+ it->task.Run(); |
+ } else { |
+ pending_tasks_.push_front(*it); |
+ } |
+ } |
+ } |
+ |
+ void RunUntilTasksAreAllDelayed() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ while (!pending_tasks_.empty() && |
+ pending_tasks_.front().GetTimeToRun() <= clock_->NowTicks()) { |
+ RunPendingTasksIfNotDelayed(); |
+ } |
+ } |
+ |
+ protected: |
+ virtual ~DelaySupportingTestSimpleTaskRunner() { } |
+ |
+ private: |
+ base::SimpleTestTickClock* clock_; |
+}; |
+ |
+class RendererSchedulerForTest : public RendererScheduler { |
+ public: |
+ RendererSchedulerForTest( |
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner, |
+ base::SimpleTestTickClock* clock) |
+ : RendererScheduler(task_runner), |
+ clock_(clock) { } |
+ virtual ~RendererSchedulerForTest() { } |
+ |
+ protected: |
+ virtual base::TimeTicks Now() const override { |
+ return clock_->NowTicks(); |
+ } |
+ |
+ private: |
+ base::SimpleTestTickClock* clock_; |
+}; |
+ |
+class RendererSchedulerTest : public testing::Test { |
+ public: |
+ RendererSchedulerTest() |
+ : clock_(new base::SimpleTestTickClock()), |
+ mock_task_runner_(new DelaySupportingTestSimpleTaskRunner(clock_.get())), |
+ scheduler_(new RendererSchedulerForTest(mock_task_runner_, clock_.get())), |
+ default_task_runner_(scheduler_->DefaultTaskRunner()), |
+ compositor_task_runner_(scheduler_->CompositorTaskRunner()), |
+ idle_task_runner_(scheduler_->IdleTaskRunner()) { } |
+ virtual ~RendererSchedulerTest() { } |
+ |
+ void RunUntilIdle() { |
+ mock_task_runner_->RunUntilTasksAreAllDelayed(); |
+ } |
+ |
+ void EnableIdleTasks() { |
+ scheduler_->WillBeginFrame( |
+ cc::BeginFrameArgs::Create(clock_->NowTicks(), |
+ base::TimeTicks(), |
+ base::TimeDelta::FromMilliseconds(1000))); |
+ clock_->Advance(base::TimeDelta::FromMilliseconds(800)); |
+ 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<RendererScheduler> 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(RendererSchedulerTest); |
+}; |
+ |
+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 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(RendererSchedulerTest, 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(RendererSchedulerTest, 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(RendererSchedulerTest, 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. |
+ |
+ 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); |
+} |
+ |
+TEST_F(RendererSchedulerTest, 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(RendererSchedulerTest, 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(RendererSchedulerTest, 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"))); |
+} |
+ |
+} // namespace content |