diff options
Diffstat (limited to 'chromium/cc/resources/task_graph_runner_unittest.cc')
-rw-r--r-- | chromium/cc/resources/task_graph_runner_unittest.cc | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/chromium/cc/resources/task_graph_runner_unittest.cc b/chromium/cc/resources/task_graph_runner_unittest.cc new file mode 100644 index 00000000000..1a6256c5835 --- /dev/null +++ b/chromium/cc/resources/task_graph_runner_unittest.cc @@ -0,0 +1,331 @@ +// 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 "cc/resources/task_graph_runner.h" + +#include <vector> + +#include "base/bind.h" +#include "base/synchronization/lock.h" +#include "base/threading/simple_thread.h" +#include "cc/base/scoped_ptr_deque.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +const int kNamespaceCount = 3; + +class TaskGraphRunnerTestBase { + public: + struct TaskInfo { + TaskInfo(int namespace_index, + unsigned id, + unsigned dependent_id, + unsigned dependent_count, + unsigned priority) + : namespace_index(namespace_index), + id(id), + dependent_id(dependent_id), + dependent_count(dependent_count), + priority(priority) {} + + int namespace_index; + unsigned id; + unsigned dependent_id; + unsigned dependent_count; + unsigned priority; + }; + + TaskGraphRunnerTestBase() : task_graph_runner_(new TaskGraphRunner) {} + + void ResetIds(int namespace_index) { + run_task_ids_[namespace_index].clear(); + on_task_completed_ids_[namespace_index].clear(); + } + + void RunAllTasks(int namespace_index) { + task_graph_runner_->WaitForTasksToFinishRunning( + namespace_token_[namespace_index]); + + Task::Vector completed_tasks; + task_graph_runner_->CollectCompletedTasks(namespace_token_[namespace_index], + &completed_tasks); + for (Task::Vector::const_iterator it = completed_tasks.begin(); + it != completed_tasks.end(); + ++it) { + FakeTaskImpl* task = static_cast<FakeTaskImpl*>(it->get()); + task->CompleteOnOriginThread(); + } + } + + void RunTaskOnWorkerThread(int namespace_index, unsigned id) { + base::AutoLock lock(run_task_ids_lock_); + run_task_ids_[namespace_index].push_back(id); + } + + void OnTaskCompleted(int namespace_index, unsigned id) { + on_task_completed_ids_[namespace_index].push_back(id); + } + + const std::vector<unsigned>& run_task_ids(int namespace_index) { + return run_task_ids_[namespace_index]; + } + + const std::vector<unsigned>& on_task_completed_ids(int namespace_index) { + return on_task_completed_ids_[namespace_index]; + } + + void ScheduleTasks(int namespace_index, const std::vector<TaskInfo>& tasks) { + Task::Vector new_tasks; + Task::Vector new_dependents; + TaskGraph new_graph; + + for (std::vector<TaskInfo>::const_iterator it = tasks.begin(); + it != tasks.end(); + ++it) { + scoped_refptr<FakeTaskImpl> new_task( + new FakeTaskImpl(this, it->namespace_index, it->id)); + new_graph.nodes.push_back( + TaskGraph::Node(new_task.get(), it->priority, 0u)); + for (unsigned i = 0; i < it->dependent_count; ++i) { + scoped_refptr<FakeDependentTaskImpl> new_dependent_task( + new FakeDependentTaskImpl( + this, it->namespace_index, it->dependent_id)); + new_graph.nodes.push_back( + TaskGraph::Node(new_dependent_task.get(), it->priority, 1u)); + new_graph.edges.push_back( + TaskGraph::Edge(new_task.get(), new_dependent_task.get())); + + new_dependents.push_back(new_dependent_task.get()); + } + + new_tasks.push_back(new_task.get()); + } + + task_graph_runner_->ScheduleTasks(namespace_token_[namespace_index], + &new_graph); + + dependents_[namespace_index].swap(new_dependents); + tasks_[namespace_index].swap(new_tasks); + } + + protected: + class FakeTaskImpl : public Task { + public: + FakeTaskImpl(TaskGraphRunnerTestBase* test, int namespace_index, int id) + : test_(test), namespace_index_(namespace_index), id_(id) {} + + // Overridden from Task: + virtual void RunOnWorkerThread() OVERRIDE { + test_->RunTaskOnWorkerThread(namespace_index_, id_); + } + + virtual void CompleteOnOriginThread() { + test_->OnTaskCompleted(namespace_index_, id_); + } + + protected: + virtual ~FakeTaskImpl() {} + + private: + TaskGraphRunnerTestBase* test_; + int namespace_index_; + int id_; + + DISALLOW_COPY_AND_ASSIGN(FakeTaskImpl); + }; + + class FakeDependentTaskImpl : public FakeTaskImpl { + public: + FakeDependentTaskImpl(TaskGraphRunnerTestBase* test, + int namespace_index, + int id) + : FakeTaskImpl(test, namespace_index, id) {} + + // Overridden from FakeTaskImpl: + virtual void CompleteOnOriginThread() OVERRIDE {} + + private: + virtual ~FakeDependentTaskImpl() {} + + DISALLOW_COPY_AND_ASSIGN(FakeDependentTaskImpl); + }; + + scoped_ptr<TaskGraphRunner> task_graph_runner_; + NamespaceToken namespace_token_[kNamespaceCount]; + Task::Vector tasks_[kNamespaceCount]; + Task::Vector dependents_[kNamespaceCount]; + std::vector<unsigned> run_task_ids_[kNamespaceCount]; + base::Lock run_task_ids_lock_; + std::vector<unsigned> on_task_completed_ids_[kNamespaceCount]; +}; + +class TaskGraphRunnerTest : public TaskGraphRunnerTestBase, + public testing::TestWithParam<int>, + public base::DelegateSimpleThread::Delegate { + public: + // Overridden from testing::Test: + virtual void SetUp() OVERRIDE { + const size_t num_threads = GetParam(); + while (workers_.size() < num_threads) { + scoped_ptr<base::DelegateSimpleThread> worker = + make_scoped_ptr(new base::DelegateSimpleThread(this, "TestWorker")); + worker->Start(); + workers_.push_back(worker.Pass()); + } + + for (int i = 0; i < kNamespaceCount; ++i) + namespace_token_[i] = task_graph_runner_->GetNamespaceToken(); + } + virtual void TearDown() OVERRIDE { + task_graph_runner_->Shutdown(); + while (workers_.size()) { + scoped_ptr<base::DelegateSimpleThread> worker = workers_.take_front(); + worker->Join(); + } + } + + private: + // Overridden from base::DelegateSimpleThread::Delegate: + virtual void Run() OVERRIDE { task_graph_runner_->Run(); } + + ScopedPtrDeque<base::DelegateSimpleThread> workers_; +}; + +TEST_P(TaskGraphRunnerTest, Basic) { + for (int i = 0; i < kNamespaceCount; ++i) { + EXPECT_EQ(0u, run_task_ids(i).size()); + EXPECT_EQ(0u, on_task_completed_ids(i).size()); + + ScheduleTasks(i, std::vector<TaskInfo>(1, TaskInfo(i, 0u, 0u, 0u, 0u))); + } + + for (int i = 0; i < kNamespaceCount; ++i) { + RunAllTasks(i); + + EXPECT_EQ(1u, run_task_ids(i).size()); + EXPECT_EQ(1u, on_task_completed_ids(i).size()); + } + + for (int i = 0; i < kNamespaceCount; ++i) + ScheduleTasks(i, std::vector<TaskInfo>(1, TaskInfo(i, 0u, 0u, 1u, 0u))); + + for (int i = 0; i < kNamespaceCount; ++i) { + RunAllTasks(i); + + EXPECT_EQ(3u, run_task_ids(i).size()); + EXPECT_EQ(2u, on_task_completed_ids(i).size()); + } + + for (int i = 0; i < kNamespaceCount; ++i) + ScheduleTasks(i, std::vector<TaskInfo>(1, TaskInfo(i, 0u, 0u, 2u, 0u))); + + for (int i = 0; i < kNamespaceCount; ++i) { + RunAllTasks(i); + + EXPECT_EQ(6u, run_task_ids(i).size()); + EXPECT_EQ(3u, on_task_completed_ids(i).size()); + } +} + +TEST_P(TaskGraphRunnerTest, Dependencies) { + for (int i = 0; i < kNamespaceCount; ++i) { + ScheduleTasks(i, + std::vector<TaskInfo>(1, + TaskInfo(i, + 0u, + 1u, + 1u, // 1 dependent + 0u))); + } + + for (int i = 0; i < kNamespaceCount; ++i) { + RunAllTasks(i); + + // Check if task ran before dependent. + ASSERT_EQ(2u, run_task_ids(i).size()); + EXPECT_EQ(0u, run_task_ids(i)[0]); + EXPECT_EQ(1u, run_task_ids(i)[1]); + ASSERT_EQ(1u, on_task_completed_ids(i).size()); + EXPECT_EQ(0u, on_task_completed_ids(i)[0]); + } + + for (int i = 0; i < kNamespaceCount; ++i) { + ScheduleTasks(i, + std::vector<TaskInfo>(1, + TaskInfo(i, + 2u, + 3u, + 2u, // 2 dependents + 0u))); + } + + for (int i = 0; i < kNamespaceCount; ++i) { + RunAllTasks(i); + + // Task should only run once. + ASSERT_EQ(5u, run_task_ids(i).size()); + EXPECT_EQ(2u, run_task_ids(i)[2]); + EXPECT_EQ(3u, run_task_ids(i)[3]); + EXPECT_EQ(3u, run_task_ids(i)[4]); + ASSERT_EQ(2u, on_task_completed_ids(i).size()); + EXPECT_EQ(2u, on_task_completed_ids(i)[1]); + } +} + +INSTANTIATE_TEST_CASE_P(TaskGraphRunnerTests, + TaskGraphRunnerTest, + ::testing::Range(1, 5)); + +class TaskGraphRunnerSingleThreadTest + : public TaskGraphRunnerTestBase, + public testing::Test, + public base::DelegateSimpleThread::Delegate { + public: + // Overridden from testing::Test: + virtual void SetUp() OVERRIDE { + worker_.reset(new base::DelegateSimpleThread(this, "TestWorker")); + worker_->Start(); + + for (int i = 0; i < kNamespaceCount; ++i) + namespace_token_[i] = task_graph_runner_->GetNamespaceToken(); + } + virtual void TearDown() OVERRIDE { + task_graph_runner_->Shutdown(); + worker_->Join(); + } + + private: + // Overridden from base::DelegateSimpleThread::Delegate: + virtual void Run() OVERRIDE { task_graph_runner_->Run(); } + + scoped_ptr<base::DelegateSimpleThread> worker_; +}; + +TEST_F(TaskGraphRunnerSingleThreadTest, Priority) { + for (int i = 0; i < kNamespaceCount; ++i) { + TaskInfo tasks[] = {TaskInfo(i, 0u, 2u, 1u, 1u), // Priority 1 + TaskInfo(i, 1u, 3u, 1u, 0u) // Priority 0 + }; + ScheduleTasks(i, std::vector<TaskInfo>(tasks, tasks + arraysize(tasks))); + } + + for (int i = 0; i < kNamespaceCount; ++i) { + RunAllTasks(i); + + // Check if tasks ran in order of priority. + ASSERT_EQ(4u, run_task_ids(i).size()); + EXPECT_EQ(1u, run_task_ids(i)[0]); + EXPECT_EQ(3u, run_task_ids(i)[1]); + EXPECT_EQ(0u, run_task_ids(i)[2]); + EXPECT_EQ(2u, run_task_ids(i)[3]); + ASSERT_EQ(2u, on_task_completed_ids(i).size()); + EXPECT_EQ(1u, on_task_completed_ids(i)[0]); + EXPECT_EQ(0u, on_task_completed_ids(i)[1]); + } +} + +} // namespace +} // namespace cc |