1 // Copyright 2012 The Chromium Authors
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/functional/bind.h"
18 #include "base/functional/callback.h"
19 #include "base/memory/ref_counted.h"
20 #include "base/synchronization/condition_variable.h"
21 #include "base/synchronization/lock.h"
22 #include "base/task/sequenced_task_runner.h"
23 #include "base/threading/platform_thread.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 SequencedTaskTracker(const SequencedTaskTracker&) = delete;
44 SequencedTaskTracker& operator=(const SequencedTaskTracker&) = delete;
45
46 // Posts the non-nestable task |task|, and records its post event.
47 void PostWrappedNonNestableTask(SequencedTaskRunner* task_runner,
48 OnceClosure task);
49
50 // Posts the nestable task |task|, and records its post event.
51 void PostWrappedNestableTask(SequencedTaskRunner* task_runner,
52 OnceClosure task);
53
54 // Posts the delayed non-nestable task |task|, and records its post event.
55 void PostWrappedDelayedNonNestableTask(SequencedTaskRunner* task_runner,
56 OnceClosure task,
57 TimeDelta delay);
58
59 // Posts |task_count| non-nestable tasks.
60 void PostNonNestableTasks(SequencedTaskRunner* task_runner, int task_count);
61
62 const std::vector<TaskEvent>& GetTaskEvents() const;
63
64 // Returns after the tracker observes a total of |count| task completions.
65 void WaitForCompletedTasks(int count);
66
67 private:
68 friend class RefCountedThreadSafe<SequencedTaskTracker>;
69
70 ~SequencedTaskTracker();
71
72 // A task which runs |task|, recording the start and end events.
73 void RunTask(OnceClosure task, int task_i);
74
75 // Records a post event for task |i|. The owner is expected to be holding
76 // |lock_| (unlike |TaskStarted| and |TaskEnded|).
77 void TaskPosted(int i);
78
79 // Records a start event for task |i|.
80 void TaskStarted(int i);
81
82 // Records a end event for task |i|.
83 void TaskEnded(int i);
84
85 // Protects events_, next_post_i_, task_end_count_ and task_end_cv_.
86 Lock lock_;
87
88 // The events as they occurred for each task (protected by lock_).
89 std::vector<TaskEvent> events_;
90
91 // The ordinal to be used for the next task-posting task (protected by
92 // lock_).
93 int next_post_i_;
94
95 // The number of task end events we've received.
96 int task_end_count_;
97 ConditionVariable task_end_cv_;
98 };
99
100 void PrintTo(const TaskEvent& event, std::ostream* os);
101
102 // Checks the non-nestable task invariants for all tasks in |events|.
103 //
104 // The invariants are:
105 // 1) Events started and ended in the same order that they were posted.
106 // 2) Events for an individual tasks occur in the order {POST, START, END},
107 // and there is only one instance of each event type for a task.
108 // 3) The only events between a task's START and END events are the POSTs of
109 // other tasks. I.e. tasks were run sequentially, not interleaved.
110 ::testing::AssertionResult CheckNonNestableInvariants(
111 const std::vector<TaskEvent>& events,
112 int task_count);
113
114 } // namespace internal
115
116 template <typename TaskRunnerTestDelegate>
117 class SequencedTaskRunnerTest : public testing::Test {
118 protected:
SequencedTaskRunnerTest()119 SequencedTaskRunnerTest()
120 : task_tracker_(new internal::SequencedTaskTracker()) {}
121
122 const scoped_refptr<internal::SequencedTaskTracker> task_tracker_;
123 TaskRunnerTestDelegate delegate_;
124 };
125
126 TYPED_TEST_SUITE_P(SequencedTaskRunnerTest);
127
128 // This test posts N non-nestable tasks in sequence, and expects them to run
129 // in FIFO order, with no part of any two tasks' execution
130 // overlapping. I.e. that each task starts only after the previously-posted
131 // one has finished.
TYPED_TEST_P(SequencedTaskRunnerTest,SequentialNonNestable)132 TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) {
133 const int kTaskCount = 1000;
134
135 this->delegate_.StartTaskRunner();
136 const scoped_refptr<SequencedTaskRunner> task_runner =
137 this->delegate_.GetTaskRunner();
138
139 this->task_tracker_->PostWrappedNonNestableTask(
140 task_runner.get(), BindOnce(&PlatformThread::Sleep, Seconds(1)));
141 for (int i = 1; i < kTaskCount; ++i) {
142 this->task_tracker_->PostWrappedNonNestableTask(task_runner.get(),
143 OnceClosure());
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(), BindOnce(&PlatformThread::Sleep, Seconds(1)));
164 for (int i = 1; i < kTaskCount; ++i) {
165 this->task_tracker_->PostWrappedNestableTask(task_runner.get(),
166 OnceClosure());
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(), OnceClosure(), Milliseconds(kDelayIncrementMs * i));
189 }
190
191 this->task_tracker_->WaitForCompletedTasks(kTaskCount);
192 this->delegate_.StopTaskRunner();
193
194 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
195 kTaskCount));
196 }
197
198 // This test posts a fast, non-nestable task from within each of a number of
199 // slow, non-nestable tasks and checks that they all run in the sequence they
200 // were posted in and that there is no execution overlap whatsoever.
TYPED_TEST_P(SequencedTaskRunnerTest,NonNestablePostFromNonNestableTask)201 TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) {
202 const int kParentCount = 10;
203 const int kChildrenPerParent = 10;
204
205 this->delegate_.StartTaskRunner();
206 const scoped_refptr<SequencedTaskRunner> task_runner =
207 this->delegate_.GetTaskRunner();
208
209 for (int i = 0; i < kParentCount; ++i) {
210 auto task = BindOnce(&internal::SequencedTaskTracker::PostNonNestableTasks,
211 this->task_tracker_, RetainedRef(task_runner),
212 kChildrenPerParent);
213 this->task_tracker_->PostWrappedNonNestableTask(task_runner.get(),
214 std::move(task));
215 }
216
217 this->delegate_.StopTaskRunner();
218
219 EXPECT_TRUE(CheckNonNestableInvariants(
220 this->task_tracker_->GetTaskEvents(),
221 kParentCount * (kChildrenPerParent + 1)));
222 }
223
224 // This test posts two tasks with the same delay, and checks that the tasks are
225 // run in the order in which they were posted.
226 //
227 // NOTE: This is actually an approximate test since the API only takes a
228 // "delay" parameter, so we are not exactly simulating two tasks that get
229 // posted at the exact same time. It would be nice if the API allowed us to
230 // specify the desired run time.
TYPED_TEST_P(SequencedTaskRunnerTest,DelayedTasksSameDelay)231 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTasksSameDelay) {
232 const int kTaskCount = 2;
233 const TimeDelta kDelay = Milliseconds(100);
234
235 this->delegate_.StartTaskRunner();
236 const scoped_refptr<SequencedTaskRunner> task_runner =
237 this->delegate_.GetTaskRunner();
238
239 this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(),
240 OnceClosure(), kDelay);
241 this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(),
242 OnceClosure(), kDelay);
243 this->task_tracker_->WaitForCompletedTasks(kTaskCount);
244 this->delegate_.StopTaskRunner();
245
246 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
247 kTaskCount));
248 }
249
250 // This test posts a normal task and a delayed task, and checks that the
251 // delayed task runs after the normal task even if the normal task takes
252 // a long time to run.
TYPED_TEST_P(SequencedTaskRunnerTest,DelayedTaskAfterLongTask)253 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterLongTask) {
254 const int kTaskCount = 2;
255
256 this->delegate_.StartTaskRunner();
257 const scoped_refptr<SequencedTaskRunner> task_runner =
258 this->delegate_.GetTaskRunner();
259
260 this->task_tracker_->PostWrappedNonNestableTask(
261 task_runner.get(),
262 base::BindOnce(&PlatformThread::Sleep, Milliseconds(50)));
263 this->task_tracker_->PostWrappedDelayedNonNestableTask(
264 task_runner.get(), OnceClosure(), Milliseconds(10));
265 this->task_tracker_->WaitForCompletedTasks(kTaskCount);
266 this->delegate_.StopTaskRunner();
267
268 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
269 kTaskCount));
270 }
271
272 // Test that a pile of normal tasks and a delayed task run in the
273 // time-to-run order.
TYPED_TEST_P(SequencedTaskRunnerTest,DelayedTaskAfterManyLongTasks)274 TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterManyLongTasks) {
275 const int kTaskCount = 11;
276
277 this->delegate_.StartTaskRunner();
278 const scoped_refptr<SequencedTaskRunner> task_runner =
279 this->delegate_.GetTaskRunner();
280
281 for (int i = 0; i < kTaskCount - 1; i++) {
282 this->task_tracker_->PostWrappedNonNestableTask(
283 task_runner.get(),
284 base::BindOnce(&PlatformThread::Sleep, Milliseconds(50)));
285 }
286 this->task_tracker_->PostWrappedDelayedNonNestableTask(
287 task_runner.get(), OnceClosure(), Milliseconds(10));
288 this->task_tracker_->WaitForCompletedTasks(kTaskCount);
289 this->delegate_.StopTaskRunner();
290
291 EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
292 kTaskCount));
293 }
294
295 // TODO([email protected]) Add a test, similiar to the above, which runs
296 // some tasked nestedly (which should be implemented in the test
297 // delegate). Also add, to the test delegate, a predicate which checks
298 // whether the implementation supports nested tasks.
299 //
300
301 // The SequencedTaskRunnerTest test case verifies behaviour that is expected
302 // from a sequenced task runner in order to be conformant.
303 REGISTER_TYPED_TEST_SUITE_P(SequencedTaskRunnerTest,
304 SequentialNonNestable,
305 SequentialNestable,
306 SequentialDelayedNonNestable,
307 NonNestablePostFromNonNestableTask,
308 DelayedTasksSameDelay,
309 DelayedTaskAfterLongTask,
310 DelayedTaskAfterManyLongTasks);
311
312 } // namespace base
313
314 #endif // BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
315