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