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 64 namespace base { 65 66 namespace internal { 67 68 // Utility class that keeps track of how many times particular tasks 69 // are run. 70 class 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 101 template <typename TaskRunnerTestDelegate> 102 class 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 110 TYPED_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. 117 TYPED_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. 138 TYPED_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 161 namespace internal { 162 163 // Calls RunsTasksOnCurrentThread() on |task_runner| and expects it to 164 // equal |expected_value|. 165 void 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. 175 TYPED_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 210 REGISTER_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