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 // SequencedTaskRunnerTest defines tests that implementations of
6 // SequencedTaskRunner should pass in order to be conformant.
7 // See task_runner_test_template.h for a description of how to use the
8 // constructs in this file; these work the same.
9
10 #ifndef BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
11 #define BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
12
13 #include <cstddef>
14 #include <iosfwd>
15 #include <vector>
16
17 #include "base/bind.h"
18 #include "base/callback.h"
19 #include "base/macros.h"
20 #include "base/memory/ref_counted.h"
21 #include "base/sequenced_task_runner.h"
22 #include "base/synchronization/condition_variable.h"
23 #include "base/synchronization/lock.h"
24 #include "base/time/time.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26
27 namespace base {
28
29 namespace internal {
30
31 struct TaskEvent {
32 enum Type { POST, START, END };
33 TaskEvent(int i, Type type);
34 int i;
35 Type type;
36 };
37
38 // Utility class used in the tests below.
39 class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> {
40 public:
41 SequencedTaskTracker();
42
43 // Posts the non-nestable task |task|, and records its post event.
44 void PostWrappedNonNestableTask(SequencedTaskRunner* task_runner,
45 const Closure& task);
46
47 // Posts the nestable task |task|, and records its post event.
48 void PostWrappedNestableTask(SequencedTaskRunner* task_runner,
49 const Closure& task);
50
51 // Posts the delayed non-nestable task |task|, and records its post event.
52 void PostWrappedDelayedNonNestableTask(SequencedTaskRunner* task_runner,
53 const Closure& task,
54 TimeDelta delay);
55
56 // Posts |task_count| non-nestable tasks.
57 void PostNonNestableTasks(SequencedTaskRunner* task_runner, int task_count);
58
59 const std::vector<TaskEvent>& GetTaskEvents() const;
60
61 // Returns after the tracker observes a total of |count| task completions.
62 void WaitForCompletedTasks(int count);
63
64 private:
65 friend class RefCountedThreadSafe<SequencedTaskTracker>;
66
67 ~SequencedTaskTracker();
68
69 // A task which runs |task|, recording the start and end events.
70 void RunTask(const Closure& task, int task_i);
71
72 // Records a post event for task |i|. The owner is expected to be holding
73 // |lock_| (unlike |TaskStarted| and |TaskEnded|).
74 void TaskPosted(int i);
75
76 // Records a start event for task |i|.
77 void TaskStarted(int i);
78
79 // Records a end event for task |i|.
80 void TaskEnded(int i);
81
82 // Protects events_, next_post_i_, task_end_count_ and task_end_cv_.
83 Lock lock_;
84
85 // The events as they occurred for each task (protected by lock_).
86 std::vector<TaskEvent> events_;
87
88 // The ordinal to be used for the next task-posting task (protected by
89 // lock_).
90 int next_post_i_;
91
92 // The number of task end events we've received.
93 int task_end_count_;
94 ConditionVariable task_end_cv_;
95
96 DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker);
97 };
98
99 void PrintTo(const TaskEvent& event, std::ostream* os);
100
101 // Checks the non-nestable task invariants for all tasks in |events|.
102 //
103 // The invariants are:
104 // 1) Events started and ended in the same order that they were posted.
105 // 2) Events for an individual tasks occur in the order {POST, START, END},
106 // and there is only one instance of each event type for a task.
107 // 3) The only events between a task's START and END events are the POSTs of
108 // other tasks. I.e. tasks were run sequentially, not interleaved.
109 ::testing::AssertionResult CheckNonNestableInvariants(
110 const std::vector<TaskEvent>& events,
111 int task_count);
112
113 } // namespace internal
114
115 template <typename TaskRunnerTestDelegate>
116 class SequencedTaskRunnerTest : public testing::Test {
117 protected:
SequencedTaskRunnerTest()118 SequencedTaskRunnerTest()
119 : task_tracker_(new internal::SequencedTaskTracker()) {}
120
121 const scoped_refptr<internal::SequencedTaskTracker> task_tracker_;
122 TaskRunnerTestDelegate delegate_;
123 };
124
125 TYPED_TEST_CASE_P(SequencedTaskRunnerTest);
126
127 // This test posts N non-nestable tasks in sequence, and expects them to run
128 // in FIFO order, with no part of any two tasks' execution
129 // overlapping. I.e. that each task starts only after the previously-posted
130 // one has finished.
TYPED_TEST_P(SequencedTaskRunnerTest,SequentialNonNestable)131 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) {
132 const int kTaskCount = 1000;
133
134 this->delegate_.StartTaskRunner();
135 const scoped_refptr<SequencedTaskRunner> task_runner =
136 this->delegate_.GetTaskRunner();
137
138 this->task_tracker_->PostWrappedNonNestableTask(
139 task_runner.get(),
140 Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1)));
141 for (int i = 1; i < kTaskCount; ++i) {
142 this->task_tracker_->PostWrappedNonNestableTask(task_runner.get(),
143 Closure());
144 }
145
146 this->delegate_.StopTaskRunner();
147
148 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
149 kTaskCount));
150 }
151
152 // This test posts N nestable tasks in sequence. It has the same expectations
153 // as SequentialNonNestable because even though the tasks are nestable, they
154 // will not be run nestedly in this case.
TYPED_TEST_P(SequencedTaskRunnerTest,SequentialNestable)155 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNestable) {
156 const int kTaskCount = 1000;
157
158 this->delegate_.StartTaskRunner();
159 const scoped_refptr<SequencedTaskRunner> task_runner =
160 this->delegate_.GetTaskRunner();
161
162 this->task_tracker_->PostWrappedNestableTask(
163 task_runner.get(),
164 Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1)));
165 for (int i = 1; i < kTaskCount; ++i) {
166 this->task_tracker_->PostWrappedNestableTask(task_runner.get(), Closure());
167 }
168
169 this->delegate_.StopTaskRunner();
170
171 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
172 kTaskCount));
173 }
174
175 // This test posts non-nestable tasks in order of increasing delay, and checks
176 // that that the tasks are run in FIFO order and that there is no execution
177 // overlap whatsoever between any two tasks.
TYPED_TEST_P(SequencedTaskRunnerTest,SequentialDelayedNonNestable)178 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) {
179 const int kTaskCount = 20;
180 const int kDelayIncrementMs = 50;
181
182 this->delegate_.StartTaskRunner();
183 const scoped_refptr<SequencedTaskRunner> task_runner =
184 this->delegate_.GetTaskRunner();
185
186 for (int i = 0; i < kTaskCount; ++i) {
187 this->task_tracker_->PostWrappedDelayedNonNestableTask(
188 task_runner.get(), Closure(),
189 TimeDelta::FromMilliseconds(kDelayIncrementMs * i));
190 }
191
192 this->task_tracker_->WaitForCompletedTasks(kTaskCount);
193 this->delegate_.StopTaskRunner();
194
195 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
196 kTaskCount));
197 }
198
199 // This test posts a fast, non-nestable task from within each of a number of
200 // slow, non-nestable tasks and checks that they all run in the sequence they
201 // were posted in and that there is no execution overlap whatsoever.
TYPED_TEST_P(SequencedTaskRunnerTest,NonNestablePostFromNonNestableTask)202 TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) {
203 const int kParentCount = 10;
204 const int kChildrenPerParent = 10;
205
206 this->delegate_.StartTaskRunner();
207 const scoped_refptr<SequencedTaskRunner> task_runner =
208 this->delegate_.GetTaskRunner();
209
210 for (int i = 0; i < kParentCount; ++i) {
211 Closure task = Bind(
212 &internal::SequencedTaskTracker::PostNonNestableTasks,
213 this->task_tracker_,
214 RetainedRef(task_runner),
215 kChildrenPerParent);
216 this->task_tracker_->PostWrappedNonNestableTask(task_runner.get(), task);
217 }
218
219 this->delegate_.StopTaskRunner();
220
221 EXPECT_TRUE(CheckNonNestableInvariants(
222 this->task_tracker_->GetTaskEvents(),
223 kParentCount * (kChildrenPerParent + 1)));
224 }
225
226 // This test posts two tasks with the same delay, and checks that the tasks are
227 // run in the order in which they were posted.
228 //
229 // NOTE: This is actually an approximate test since the API only takes a
230 // "delay" parameter, so we are not exactly simulating two tasks that get
231 // posted at the exact same time. It would be nice if the API allowed us to
232 // specify the desired run time.
TYPED_TEST_P(SequencedTaskRunnerTest,DelayedTasksSameDelay)233 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTasksSameDelay) {
234 const int kTaskCount = 2;
235 const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
236
237 this->delegate_.StartTaskRunner();
238 const scoped_refptr<SequencedTaskRunner> task_runner =
239 this->delegate_.GetTaskRunner();
240
241 this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(),
242 Closure(), kDelay);
243 this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(),
244 Closure(), kDelay);
245 this->task_tracker_->WaitForCompletedTasks(kTaskCount);
246 this->delegate_.StopTaskRunner();
247
248 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
249 kTaskCount));
250 }
251
252 // This test posts a normal task and a delayed task, and checks that the
253 // delayed task runs after the normal task even if the normal task takes
254 // a long time to run.
TYPED_TEST_P(SequencedTaskRunnerTest,DelayedTaskAfterLongTask)255 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterLongTask) {
256 const int kTaskCount = 2;
257
258 this->delegate_.StartTaskRunner();
259 const scoped_refptr<SequencedTaskRunner> task_runner =
260 this->delegate_.GetTaskRunner();
261
262 this->task_tracker_->PostWrappedNonNestableTask(
263 task_runner.get(),
264 base::Bind(&PlatformThread::Sleep, TimeDelta::FromMilliseconds(50)));
265 this->task_tracker_->PostWrappedDelayedNonNestableTask(
266 task_runner.get(), Closure(), TimeDelta::FromMilliseconds(10));
267 this->task_tracker_->WaitForCompletedTasks(kTaskCount);
268 this->delegate_.StopTaskRunner();
269
270 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
271 kTaskCount));
272 }
273
274 // Test that a pile of normal tasks and a delayed task run in the
275 // time-to-run order.
TYPED_TEST_P(SequencedTaskRunnerTest,DelayedTaskAfterManyLongTasks)276 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterManyLongTasks) {
277 const int kTaskCount = 11;
278
279 this->delegate_.StartTaskRunner();
280 const scoped_refptr<SequencedTaskRunner> task_runner =
281 this->delegate_.GetTaskRunner();
282
283 for (int i = 0; i < kTaskCount - 1; i++) {
284 this->task_tracker_->PostWrappedNonNestableTask(
285 task_runner.get(),
286 base::Bind(&PlatformThread::Sleep, TimeDelta::FromMilliseconds(50)));
287 }
288 this->task_tracker_->PostWrappedDelayedNonNestableTask(
289 task_runner.get(), Closure(), TimeDelta::FromMilliseconds(10));
290 this->task_tracker_->WaitForCompletedTasks(kTaskCount);
291 this->delegate_.StopTaskRunner();
292
293 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
294 kTaskCount));
295 }
296
297
298 // TODO(francoisk777@gmail.com) Add a test, similiar to the above, which runs
299 // some tasked nestedly (which should be implemented in the test
300 // delegate). Also add, to the the test delegate, a predicate which checks
301 // whether the implementation supports nested tasks.
302 //
303
304 // The SequencedTaskRunnerTest test case verifies behaviour that is expected
305 // from a sequenced task runner in order to be conformant.
306 REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest,
307 SequentialNonNestable,
308 SequentialNestable,
309 SequentialDelayedNonNestable,
310 NonNestablePostFromNonNestableTask,
311 DelayedTasksSameDelay,
312 DelayedTaskAfterLongTask,
313 DelayedTaskAfterManyLongTasks);
314
315 template <typename TaskRunnerTestDelegate>
316 class SequencedTaskRunnerDelayedTest
317 : public SequencedTaskRunnerTest<TaskRunnerTestDelegate> {};
318
319 TYPED_TEST_CASE_P(SequencedTaskRunnerDelayedTest);
320
321 // This test posts a delayed task, and checks that the task is run later than
322 // the specified time.
TYPED_TEST_P(SequencedTaskRunnerDelayedTest,DelayedTaskBasic)323 TYPED_TEST_P(SequencedTaskRunnerDelayedTest, DelayedTaskBasic) {
324 const int kTaskCount = 1;
325 const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
326
327 this->delegate_.StartTaskRunner();
328 const scoped_refptr<SequencedTaskRunner> task_runner =
329 this->delegate_.GetTaskRunner();
330
331 Time time_before_run = Time::Now();
332 this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(),
333 Closure(), kDelay);
334 this->task_tracker_->WaitForCompletedTasks(kTaskCount);
335 this->delegate_.StopTaskRunner();
336 Time time_after_run = Time::Now();
337
338 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
339 kTaskCount));
340 EXPECT_LE(kDelay, time_after_run - time_before_run);
341 }
342
343 // SequencedTaskRunnerDelayedTest tests that the |delay| parameter of
344 // is used to actually wait for |delay| ms before executing the task.
345 // This is not mandatory for a SequencedTaskRunner to be compliant.
346 REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerDelayedTest, DelayedTaskBasic);
347
348 } // namespace base
349
350 #endif // BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
351