summaryrefslogtreecommitdiffstats
path: root/chromium/cc/resources/task_graph_runner_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/cc/resources/task_graph_runner_unittest.cc')
-rw-r--r--chromium/cc/resources/task_graph_runner_unittest.cc331
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