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