1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// This class defines tests that implementations of TaskRunner should 6// pass in order to be conformant. Here's how you use it to test your 7// implementation. 8// 9// Say your class is called MyTaskRunner. Then you need to define a 10// class called MyTaskRunnerTestDelegate in my_task_runner_unittest.cc 11// like this: 12// 13// class MyTaskRunnerTestDelegate { 14// public: 15// // Tasks posted to the task runner after this and before 16// // StopTaskRunner() is called is called should run successfully. 17// void StartTaskRunner() { 18// ... 19// } 20// 21// // Should return the task runner implementation. Only called 22// // after StartTaskRunner and before StopTaskRunner. 23// scoped_refptr<MyTaskRunner> GetTaskRunner() { 24// ... 25// } 26// 27// // Stop the task runner and make sure all tasks posted before 28// // this is called are run. Caveat: delayed tasks are not run, 29 // they're simply deleted. 30// void StopTaskRunner() { 31// ... 32// } 33// }; 34// 35// The TaskRunnerTest test harness will have a member variable of 36// this delegate type and will call its functions in the various 37// tests. 38// 39// Then you simply #include this file as well as gtest.h and add the 40// following statement to my_task_runner_unittest.cc: 41// 42// INSTANTIATE_TYPED_TEST_CASE_P( 43// MyTaskRunner, TaskRunnerTest, MyTaskRunnerTestDelegate); 44// 45// Easy! 46 47#ifndef BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_ 48#define BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_ 49 50#include <cstddef> 51#include <map> 52 53#include "base/basictypes.h" 54#include "base/bind.h" 55#include "base/callback.h" 56#include "base/memory/ref_counted.h" 57#include "base/synchronization/condition_variable.h" 58#include "base/synchronization/lock.h" 59#include "base/task_runner.h" 60#include "base/threading/thread.h" 61#include "base/tracked_objects.h" 62#include "testing/gtest/include/gtest/gtest.h" 63 64namespace base { 65 66namespace internal { 67 68// Utility class that keeps track of how many times particular tasks 69// are run. 70class TaskTracker : public RefCountedThreadSafe<TaskTracker> { 71 public: 72 TaskTracker(); 73 74 // Returns a closure that runs the given task and increments the run 75 // count of |i| by one. |task| may be null. It is guaranteed that 76 // only one task wrapped by a given tracker will be run at a time. 77 Closure WrapTask(const Closure& task, int i); 78 79 std::map<int, int> GetTaskRunCounts() const; 80 81 // Returns after the tracker observes a total of |count| task completions. 82 void WaitForCompletedTasks(int count); 83 84 private: 85 friend class RefCountedThreadSafe<TaskTracker>; 86 87 ~TaskTracker(); 88 89 void RunTask(const Closure& task, int i); 90 91 mutable Lock lock_; 92 std::map<int, int> task_run_counts_; 93 int task_runs_; 94 ConditionVariable task_runs_cv_; 95 96 DISALLOW_COPY_AND_ASSIGN(TaskTracker); 97}; 98 99} // namespace internal 100 101template <typename TaskRunnerTestDelegate> 102class TaskRunnerTest : public testing::Test { 103 protected: 104 TaskRunnerTest() : task_tracker_(new internal::TaskTracker()) {} 105 106 const scoped_refptr<internal::TaskTracker> task_tracker_; 107 TaskRunnerTestDelegate delegate_; 108}; 109 110TYPED_TEST_CASE_P(TaskRunnerTest); 111 112// We can't really test much, since TaskRunner provides very few 113// guarantees. 114 115// Post a bunch of tasks to the task runner. They should all 116// complete. 117TYPED_TEST_P(TaskRunnerTest, Basic) { 118 std::map<int, int> expected_task_run_counts; 119 120 this->delegate_.StartTaskRunner(); 121 scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner(); 122 // Post each ith task i+1 times. 123 for (int i = 0; i < 20; ++i) { 124 const Closure& ith_task = this->task_tracker_->WrapTask(Closure(), i); 125 for (int j = 0; j < i + 1; ++j) { 126 task_runner->PostTask(FROM_HERE, ith_task); 127 ++expected_task_run_counts[i]; 128 } 129 } 130 this->delegate_.StopTaskRunner(); 131 132 EXPECT_EQ(expected_task_run_counts, 133 this->task_tracker_->GetTaskRunCounts()); 134} 135 136// Post a bunch of delayed tasks to the task runner. They should all 137// complete. 138TYPED_TEST_P(TaskRunnerTest, Delayed) { 139 std::map<int, int> expected_task_run_counts; 140 int expected_total_tasks = 0; 141 142 this->delegate_.StartTaskRunner(); 143 scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner(); 144 // Post each ith task i+1 times with delays from 0-i. 145 for (int i = 0; i < 20; ++i) { 146 const Closure& ith_task = this->task_tracker_->WrapTask(Closure(), i); 147 for (int j = 0; j < i + 1; ++j) { 148 task_runner->PostDelayedTask( 149 FROM_HERE, ith_task, base::TimeDelta::FromMilliseconds(j)); 150 ++expected_task_run_counts[i]; 151 ++expected_total_tasks; 152 } 153 } 154 this->task_tracker_->WaitForCompletedTasks(expected_total_tasks); 155 this->delegate_.StopTaskRunner(); 156 157 EXPECT_EQ(expected_task_run_counts, 158 this->task_tracker_->GetTaskRunCounts()); 159} 160 161namespace internal { 162 163// Calls RunsTasksOnCurrentThread() on |task_runner| and expects it to 164// equal |expected_value|. 165void ExpectRunsTasksOnCurrentThread( 166 bool expected_value, 167 const scoped_refptr<TaskRunner>& task_runner); 168 169} // namespace internal 170 171// Post a bunch of tasks to the task runner as well as to a separate 172// thread, each checking the value of RunsTasksOnCurrentThread(), 173// which should return true for the tasks posted on the task runner 174// and false for the tasks posted on the separate thread. 175TYPED_TEST_P(TaskRunnerTest, RunsTasksOnCurrentThread) { 176 std::map<int, int> expected_task_run_counts; 177 178 Thread thread("Non-task-runner thread"); 179 ASSERT_TRUE(thread.Start()); 180 this->delegate_.StartTaskRunner(); 181 182 scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner(); 183 // Post each ith task i+1 times on the task runner and i+1 times on 184 // the non-task-runner thread. 185 for (int i = 0; i < 20; ++i) { 186 const Closure& ith_task_runner_task = 187 this->task_tracker_->WrapTask( 188 Bind(&internal::ExpectRunsTasksOnCurrentThread, 189 true, task_runner), 190 i); 191 const Closure& ith_non_task_runner_task = 192 this->task_tracker_->WrapTask( 193 Bind(&internal::ExpectRunsTasksOnCurrentThread, 194 false, task_runner), 195 i); 196 for (int j = 0; j < i + 1; ++j) { 197 task_runner->PostTask(FROM_HERE, ith_task_runner_task); 198 thread.message_loop()->PostTask(FROM_HERE, ith_non_task_runner_task); 199 expected_task_run_counts[i] += 2; 200 } 201 } 202 203 this->delegate_.StopTaskRunner(); 204 thread.Stop(); 205 206 EXPECT_EQ(expected_task_run_counts, 207 this->task_tracker_->GetTaskRunCounts()); 208} 209 210REGISTER_TYPED_TEST_CASE_P( 211 TaskRunnerTest, Basic, Delayed, RunsTasksOnCurrentThread); 212 213} // namespace base 214 215#endif //#define BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_ 216