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