// 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