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