xref: /aosp_15_r20/external/cronet/base/test/sequenced_task_runner_test_template.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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