// Copyright 2015 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. #ifndef CC_TEST_TASK_GRAPH_RUNNER_TEST_TEMPLATE_H_ #define CC_TEST_TASK_GRAPH_RUNNER_TEST_TEMPLATE_H_ #include "cc/raster/task_graph_runner.h" #include #include "base/bind.h" #include "base/macros.h" #include "base/synchronization/lock.h" #include "base/threading/simple_thread.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { class TaskGraphRunnerTestBase { public: struct TaskInfo { TaskInfo(int namespace_index, unsigned id, unsigned dependent_id, unsigned dependent_count, unsigned category, unsigned priority) : namespace_index(namespace_index), id(id), dependent_id(dependent_id), dependent_count(dependent_count), category(category), priority(priority) {} int namespace_index; unsigned id; unsigned dependent_id; unsigned dependent_count; unsigned category; unsigned priority; }; TaskGraphRunnerTestBase() {} void SetTaskGraphRunner(TaskGraphRunner* task_graph_runner); void ResetIds(int namespace_index); void RunAllTasks(int namespace_index); void RunTaskOnWorkerThread(int namespace_index, unsigned id); void OnTaskCompleted(int namespace_index, unsigned id); const std::vector& run_task_ids(int namespace_index); const std::vector& on_task_completed_ids(int namespace_index); void ScheduleTasks(int namespace_index, const std::vector& tasks); static const int kNamespaceCount = 3; 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: void RunOnWorkerThread() override; virtual void CompleteOnOriginThread(); protected: ~FakeTaskImpl() override {} 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: void CompleteOnOriginThread() override {} private: ~FakeDependentTaskImpl() override {} DISALLOW_COPY_AND_ASSIGN(FakeDependentTaskImpl); }; TaskGraphRunner* task_graph_runner_; NamespaceToken namespace_token_[kNamespaceCount]; Task::Vector tasks_[kNamespaceCount]; Task::Vector dependents_[kNamespaceCount]; std::vector run_task_ids_[kNamespaceCount]; base::Lock run_task_ids_lock_; std::vector on_task_completed_ids_[kNamespaceCount]; }; template class TaskGraphRunnerTest : public TaskGraphRunnerTestBase, public testing::Test { public: // Overridden from testing::Test: void SetUp() override { delegate_.StartTaskGraphRunner(); SetTaskGraphRunner(delegate_.GetTaskGraphRunner()); for (int i = 0; i < kNamespaceCount; ++i) namespace_token_[i] = task_graph_runner_->GetNamespaceToken(); } void TearDown() override { delegate_.StopTaskGraphRunner(); } private: TaskRunnerTestDelegate delegate_; }; TYPED_TEST_CASE_P(TaskGraphRunnerTest); TYPED_TEST_P(TaskGraphRunnerTest, Basic) { const int kNamespaceCount = TaskGraphRunnerTestBase::kNamespaceCount; using TaskInfo = TaskGraphRunnerTestBase::TaskInfo; for (int i = 0; i < kNamespaceCount; ++i) { EXPECT_EQ(0u, this->run_task_ids(i).size()); EXPECT_EQ(0u, this->on_task_completed_ids(i).size()); this->ScheduleTasks( i, std::vector(1, TaskInfo(i, 0u, 0u, 0u, 0u, 0u))); } for (int i = 0; i < kNamespaceCount; ++i) { this->RunAllTasks(i); EXPECT_EQ(1u, this->run_task_ids(i).size()); EXPECT_EQ(1u, this->on_task_completed_ids(i).size()); } for (int i = 0; i < kNamespaceCount; ++i) this->ScheduleTasks( i, std::vector(1, TaskInfo(i, 0u, 0u, 1u, 0u, 0u))); for (int i = 0; i < kNamespaceCount; ++i) { this->RunAllTasks(i); EXPECT_EQ(3u, this->run_task_ids(i).size()); EXPECT_EQ(2u, this->on_task_completed_ids(i).size()); } for (int i = 0; i < kNamespaceCount; ++i) this->ScheduleTasks( i, std::vector(1, TaskInfo(i, 0u, 0u, 2u, 0u, 0u))); for (int i = 0; i < kNamespaceCount; ++i) { this->RunAllTasks(i); EXPECT_EQ(6u, this->run_task_ids(i).size()); EXPECT_EQ(3u, this->on_task_completed_ids(i).size()); } } TYPED_TEST_P(TaskGraphRunnerTest, Dependencies) { const int kNamespaceCount = TaskGraphRunnerTestBase::kNamespaceCount; using TaskInfo = TaskGraphRunnerTestBase::TaskInfo; for (int i = 0; i < kNamespaceCount; ++i) { this->ScheduleTasks(i, std::vector(1, TaskInfo(i, 0u, 1u, 1u, // 1 dependent 0u, 0u))); } for (int i = 0; i < kNamespaceCount; ++i) { this->RunAllTasks(i); // Check if task ran before dependent. ASSERT_EQ(2u, this->run_task_ids(i).size()); EXPECT_EQ(0u, this->run_task_ids(i)[0]); EXPECT_EQ(1u, this->run_task_ids(i)[1]); ASSERT_EQ(1u, this->on_task_completed_ids(i).size()); EXPECT_EQ(0u, this->on_task_completed_ids(i)[0]); } for (int i = 0; i < kNamespaceCount; ++i) { this->ScheduleTasks(i, std::vector(1, TaskInfo(i, 2u, 3u, 2u, // 2 dependents 0u, 0u))); } for (int i = 0; i < kNamespaceCount; ++i) { this->RunAllTasks(i); // Task should only run once. ASSERT_EQ(5u, this->run_task_ids(i).size()); EXPECT_EQ(2u, this->run_task_ids(i)[2]); EXPECT_EQ(3u, this->run_task_ids(i)[3]); EXPECT_EQ(3u, this->run_task_ids(i)[4]); ASSERT_EQ(2u, this->on_task_completed_ids(i).size()); EXPECT_EQ(2u, this->on_task_completed_ids(i)[1]); } } TYPED_TEST_P(TaskGraphRunnerTest, Categorys) { const int kNamespaceCount = TaskGraphRunnerTestBase::kNamespaceCount; const unsigned kCategoryCount = 3; using TaskInfo = TaskGraphRunnerTestBase::TaskInfo; for (int i = 0; i < kNamespaceCount; ++i) { EXPECT_EQ(0u, this->run_task_ids(i).size()); EXPECT_EQ(0u, this->on_task_completed_ids(i).size()); std::vector tasks; for (unsigned j = 0; j < kCategoryCount; ++j) { tasks.emplace_back(i, 0u, 0u, 0u, j, 0u); } this->ScheduleTasks(i, tasks); } for (int i = 0; i < kNamespaceCount; ++i) { this->RunAllTasks(i); EXPECT_EQ(kCategoryCount, this->run_task_ids(i).size()); EXPECT_EQ(kCategoryCount, this->on_task_completed_ids(i).size()); } for (int i = 0; i < kNamespaceCount; ++i) { std::vector tasks; for (unsigned j = 0; j < kCategoryCount; ++j) { tasks.emplace_back(i, 0u, 0u, 1u, j, 0u); } this->ScheduleTasks(i, tasks); } for (int i = 0; i < kNamespaceCount; ++i) { this->RunAllTasks(i); EXPECT_EQ(kCategoryCount * 3u, this->run_task_ids(i).size()); EXPECT_EQ(kCategoryCount * 2u, this->on_task_completed_ids(i).size()); } for (int i = 0; i < kNamespaceCount; ++i) { std::vector tasks; for (unsigned j = 0; j < kCategoryCount; ++j) { tasks.emplace_back(i, 0u, 0u, 2u, j, 0u); } this->ScheduleTasks(i, tasks); } for (int i = 0; i < kNamespaceCount; ++i) { this->RunAllTasks(i); EXPECT_EQ(kCategoryCount * 6u, this->run_task_ids(i).size()); EXPECT_EQ(kCategoryCount * 3u, this->on_task_completed_ids(i).size()); } } REGISTER_TYPED_TEST_CASE_P(TaskGraphRunnerTest, Basic, Dependencies, Categorys); template using SingleThreadTaskGraphRunnerTest = TaskGraphRunnerTest; TYPED_TEST_CASE_P(SingleThreadTaskGraphRunnerTest); TYPED_TEST_P(SingleThreadTaskGraphRunnerTest, Priority) { const int kNamespaceCount = TaskGraphRunnerTestBase::kNamespaceCount; using TaskInfo = TaskGraphRunnerTestBase::TaskInfo; for (int i = 0; i < kNamespaceCount; ++i) { TaskInfo tasks[] = { TaskInfo(i, 0u, 2u, 1u, 0u, 1u), // Priority 1 TaskInfo(i, 1u, 3u, 1u, 0u, 0u) // Priority 0 }; this->ScheduleTasks(i, std::vector(tasks, tasks + arraysize(tasks))); } for (int i = 0; i < kNamespaceCount; ++i) { this->RunAllTasks(i); // Check if tasks ran in order of priority. ASSERT_EQ(4u, this->run_task_ids(i).size()); EXPECT_EQ(1u, this->run_task_ids(i)[0]); EXPECT_EQ(3u, this->run_task_ids(i)[1]); EXPECT_EQ(0u, this->run_task_ids(i)[2]); EXPECT_EQ(2u, this->run_task_ids(i)[3]); ASSERT_EQ(2u, this->on_task_completed_ids(i).size()); EXPECT_EQ(1u, this->on_task_completed_ids(i)[0]); EXPECT_EQ(0u, this->on_task_completed_ids(i)[1]); } } REGISTER_TYPED_TEST_CASE_P(SingleThreadTaskGraphRunnerTest, Priority); } // namespace cc #endif // CC_TEST_TASK_GRAPH_RUNNER_TEST_TEMPLATE_H_