| // Copyright 2013 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 "cc/resources/worker_pool.h" |
| |
| #include <vector> |
| |
| #include "cc/base/completion_event.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace cc { |
| |
| namespace { |
| |
| class FakeWorkerPoolTaskImpl : public internal::WorkerPoolTask { |
| public: |
| FakeWorkerPoolTaskImpl(const base::Closure& callback, |
| const base::Closure& reply) |
| : callback_(callback), |
| reply_(reply) { |
| } |
| |
| // Overridden from internal::WorkerPoolTask: |
| virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE { |
| if (!callback_.is_null()) |
| callback_.Run(); |
| } |
| virtual void CompleteOnOriginThread() OVERRIDE { |
| if (!reply_.is_null()) |
| reply_.Run(); |
| } |
| |
| private: |
| virtual ~FakeWorkerPoolTaskImpl() {} |
| |
| const base::Closure callback_; |
| const base::Closure reply_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeWorkerPoolTaskImpl); |
| }; |
| |
| class FakeWorkerPool : public WorkerPool { |
| public: |
| struct Task { |
| Task(const base::Closure& callback, |
| const base::Closure& reply, |
| const base::Closure& dependent, |
| unsigned dependent_count, |
| unsigned priority) : callback(callback), |
| reply(reply), |
| dependent(dependent), |
| dependent_count(dependent_count), |
| priority(priority) { |
| } |
| |
| base::Closure callback; |
| base::Closure reply; |
| base::Closure dependent; |
| unsigned dependent_count; |
| unsigned priority; |
| }; |
| FakeWorkerPool() : WorkerPool(1, "test") {} |
| virtual ~FakeWorkerPool() {} |
| |
| static scoped_ptr<FakeWorkerPool> Create() { |
| return make_scoped_ptr(new FakeWorkerPool); |
| } |
| |
| void ScheduleTasks(const std::vector<Task>& tasks) { |
| TaskVector new_tasks; |
| TaskVector new_dependents; |
| TaskGraph new_graph; |
| |
| scoped_refptr<FakeWorkerPoolTaskImpl> new_completion_task( |
| new FakeWorkerPoolTaskImpl( |
| base::Bind(&FakeWorkerPool::OnTasksCompleted, |
| base::Unretained(this)), |
| base::Closure())); |
| scoped_ptr<internal::GraphNode> completion_node( |
| new internal::GraphNode(new_completion_task.get(), 0u)); |
| |
| for (std::vector<Task>::const_iterator it = tasks.begin(); |
| it != tasks.end(); ++it) { |
| scoped_refptr<FakeWorkerPoolTaskImpl> new_task( |
| new FakeWorkerPoolTaskImpl(it->callback, it->reply)); |
| scoped_ptr<internal::GraphNode> node( |
| new internal::GraphNode(new_task.get(), it->priority)); |
| |
| DCHECK(it->dependent_count); |
| for (unsigned i = 0; i < it->dependent_count; ++i) { |
| scoped_refptr<FakeWorkerPoolTaskImpl> new_dependent_task( |
| new FakeWorkerPoolTaskImpl(it->dependent, base::Closure())); |
| scoped_ptr<internal::GraphNode> dependent_node( |
| new internal::GraphNode(new_dependent_task.get(), it->priority)); |
| dependent_node->add_dependent(completion_node.get()); |
| completion_node->add_dependency(); |
| node->add_dependent(dependent_node.get()); |
| dependent_node->add_dependency(); |
| new_graph.set(new_dependent_task.get(), dependent_node.Pass()); |
| new_dependents.push_back(new_dependent_task.get()); |
| } |
| |
| new_graph.set(new_task.get(), node.Pass()); |
| new_tasks.push_back(new_task.get()); |
| } |
| |
| new_graph.set(new_completion_task.get(), completion_node.Pass()); |
| |
| scheduled_tasks_completion_.reset(new CompletionEvent); |
| |
| SetTaskGraph(&new_graph); |
| |
| dependents_.swap(new_dependents); |
| completion_task_.swap(new_completion_task); |
| tasks_.swap(new_tasks); |
| } |
| |
| void WaitForTasksToComplete() { |
| DCHECK(scheduled_tasks_completion_); |
| scheduled_tasks_completion_->Wait(); |
| } |
| |
| private: |
| typedef std::vector<scoped_refptr<internal::WorkerPoolTask> > TaskVector; |
| |
| void OnTasksCompleted() { |
| DCHECK(scheduled_tasks_completion_); |
| scheduled_tasks_completion_->Signal(); |
| } |
| |
| TaskVector tasks_; |
| TaskVector dependents_; |
| scoped_refptr<FakeWorkerPoolTaskImpl> completion_task_; |
| scoped_ptr<CompletionEvent> scheduled_tasks_completion_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeWorkerPool); |
| }; |
| |
| class WorkerPoolTest : public testing::Test { |
| public: |
| WorkerPoolTest() {} |
| virtual ~WorkerPoolTest() {} |
| |
| // Overridden from testing::Test: |
| virtual void SetUp() OVERRIDE { |
| worker_pool_ = FakeWorkerPool::Create(); |
| } |
| virtual void TearDown() OVERRIDE { |
| worker_pool_->Shutdown(); |
| worker_pool_->CheckForCompletedTasks(); |
| } |
| |
| void ResetIds() { |
| run_task_ids_.clear(); |
| on_task_completed_ids_.clear(); |
| } |
| |
| void RunAllTasks() { |
| worker_pool_->WaitForTasksToComplete(); |
| worker_pool_->CheckForCompletedTasks(); |
| } |
| |
| FakeWorkerPool* worker_pool() { |
| return worker_pool_.get(); |
| } |
| |
| void RunTask(unsigned id) { |
| run_task_ids_.push_back(id); |
| } |
| |
| void OnTaskCompleted(unsigned id) { |
| on_task_completed_ids_.push_back(id); |
| } |
| |
| const std::vector<unsigned>& run_task_ids() { |
| return run_task_ids_; |
| } |
| |
| const std::vector<unsigned>& on_task_completed_ids() { |
| return on_task_completed_ids_; |
| } |
| |
| private: |
| scoped_ptr<FakeWorkerPool> worker_pool_; |
| std::vector<unsigned> run_task_ids_; |
| std::vector<unsigned> on_task_completed_ids_; |
| }; |
| |
| TEST_F(WorkerPoolTest, Basic) { |
| EXPECT_EQ(0u, run_task_ids().size()); |
| EXPECT_EQ(0u, on_task_completed_ids().size()); |
| |
| worker_pool()->ScheduleTasks( |
| std::vector<FakeWorkerPool::Task>( |
| 1, |
| FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 0u), |
| base::Bind(&WorkerPoolTest::OnTaskCompleted, |
| base::Unretained(this), |
| 0u), |
| base::Closure(), |
| 1u, |
| 0u))); |
| RunAllTasks(); |
| |
| EXPECT_EQ(1u, run_task_ids().size()); |
| EXPECT_EQ(1u, on_task_completed_ids().size()); |
| |
| worker_pool()->ScheduleTasks( |
| std::vector<FakeWorkerPool::Task>( |
| 1, |
| FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 0u), |
| base::Bind(&WorkerPoolTest::OnTaskCompleted, |
| base::Unretained(this), |
| 0u), |
| base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 0u), |
| 1u, |
| 0u))); |
| RunAllTasks(); |
| |
| EXPECT_EQ(3u, run_task_ids().size()); |
| EXPECT_EQ(2u, on_task_completed_ids().size()); |
| |
| worker_pool()->ScheduleTasks( |
| std::vector<FakeWorkerPool::Task>( |
| 1, FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 0u), |
| base::Bind(&WorkerPoolTest::OnTaskCompleted, |
| base::Unretained(this), |
| 0u), |
| base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 0u), |
| 2u, |
| 0u))); |
| RunAllTasks(); |
| |
| EXPECT_EQ(6u, run_task_ids().size()); |
| EXPECT_EQ(3u, on_task_completed_ids().size()); |
| } |
| |
| TEST_F(WorkerPoolTest, Dependencies) { |
| worker_pool()->ScheduleTasks( |
| std::vector<FakeWorkerPool::Task>( |
| 1, FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 0u), |
| base::Bind(&WorkerPoolTest::OnTaskCompleted, |
| base::Unretained(this), |
| 0u), |
| base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 1u), |
| 1u, |
| 0u))); |
| RunAllTasks(); |
| |
| // Check if task ran before dependent. |
| ASSERT_EQ(2u, run_task_ids().size()); |
| EXPECT_EQ(0u, run_task_ids()[0]); |
| EXPECT_EQ(1u, run_task_ids()[1]); |
| ASSERT_EQ(1u, on_task_completed_ids().size()); |
| EXPECT_EQ(0u, on_task_completed_ids()[0]); |
| |
| worker_pool()->ScheduleTasks( |
| std::vector<FakeWorkerPool::Task>( |
| 1, FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 2u), |
| base::Bind(&WorkerPoolTest::OnTaskCompleted, |
| base::Unretained(this), |
| 2u), |
| base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 3u), |
| 2u, |
| 0u))); |
| RunAllTasks(); |
| |
| // Task should only run once. |
| ASSERT_EQ(5u, run_task_ids().size()); |
| EXPECT_EQ(2u, run_task_ids()[2]); |
| EXPECT_EQ(3u, run_task_ids()[3]); |
| EXPECT_EQ(3u, run_task_ids()[4]); |
| ASSERT_EQ(2u, on_task_completed_ids().size()); |
| EXPECT_EQ(2u, on_task_completed_ids()[1]); |
| } |
| |
| TEST_F(WorkerPoolTest, Priority) { |
| { |
| FakeWorkerPool::Task tasks[] = { |
| FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 0u), |
| base::Bind(&WorkerPoolTest::OnTaskCompleted, |
| base::Unretained(this), |
| 0u), |
| base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 2u), |
| 1u, |
| 1u), // Priority 1 |
| FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 1u), |
| base::Bind(&WorkerPoolTest::OnTaskCompleted, |
| base::Unretained(this), |
| 1u), |
| base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 3u), |
| 1u, |
| 0u) // Priority 0 |
| }; |
| worker_pool()->ScheduleTasks( |
| std::vector<FakeWorkerPool::Task>(tasks, tasks + arraysize(tasks))); |
| } |
| RunAllTasks(); |
| |
| // Check if tasks ran in order of priority. |
| ASSERT_EQ(4u, run_task_ids().size()); |
| EXPECT_EQ(1u, run_task_ids()[0]); |
| EXPECT_EQ(3u, run_task_ids()[1]); |
| EXPECT_EQ(0u, run_task_ids()[2]); |
| EXPECT_EQ(2u, run_task_ids()[3]); |
| ASSERT_EQ(2u, on_task_completed_ids().size()); |
| EXPECT_EQ(1u, on_task_completed_ids()[0]); |
| EXPECT_EQ(0u, on_task_completed_ids()[1]); |
| |
| ResetIds(); |
| { |
| std::vector<FakeWorkerPool::Task> tasks; |
| tasks.push_back( |
| FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 0u), |
| base::Bind(&WorkerPoolTest::OnTaskCompleted, |
| base::Unretained(this), |
| 0u), |
| base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 3u), |
| 1u, // 1 dependent |
| 1u)); // Priority 1 |
| tasks.push_back( |
| FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 1u), |
| base::Bind(&WorkerPoolTest::OnTaskCompleted, |
| base::Unretained(this), |
| 1u), |
| base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 4u), |
| 2u, // 2 dependents |
| 1u)); // Priority 1 |
| tasks.push_back( |
| FakeWorkerPool::Task(base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 2u), |
| base::Bind(&WorkerPoolTest::OnTaskCompleted, |
| base::Unretained(this), |
| 2u), |
| base::Bind(&WorkerPoolTest::RunTask, |
| base::Unretained(this), |
| 5u), |
| 1u, // 1 dependent |
| 0u)); // Priority 0 |
| worker_pool()->ScheduleTasks(tasks); |
| } |
| RunAllTasks(); |
| |
| // Check if tasks ran in order of priority and that task with more |
| // dependents ran first when priority is the same. |
| ASSERT_LE(3u, run_task_ids().size()); |
| EXPECT_EQ(2u, run_task_ids()[0]); |
| EXPECT_EQ(5u, run_task_ids()[1]); |
| EXPECT_EQ(1u, run_task_ids()[2]); |
| ASSERT_EQ(3u, on_task_completed_ids().size()); |
| EXPECT_EQ(2u, on_task_completed_ids()[0]); |
| EXPECT_EQ(1u, on_task_completed_ids()[1]); |
| EXPECT_EQ(0u, on_task_completed_ids()[2]); |
| } |
| |
| } // namespace |
| |
| } // namespace cc |