xref: /aosp_15_r20/external/pigweed/pw_async/fake_dispatcher_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2022 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #include "pw_async/fake_dispatcher.h"
15 
16 #include "pw_chrono/system_clock.h"
17 #include "pw_containers/vector.h"
18 #include "pw_string/to_string.h"
19 #include "pw_unit_test/framework.h"
20 
21 #define ASSERT_CANCELLED(status) ASSERT_EQ(Status::Cancelled(), status)
22 
23 using namespace std::chrono_literals;
24 
25 struct CallCounts {
26   int ok = 0;
27   int cancelled = 0;
operator ==CallCounts28   bool operator==(const CallCounts& other) const {
29     return ok == other.ok && cancelled == other.cancelled;
30   }
31 };
32 
33 namespace pw {
34 template <>
ToString(const CallCounts & value,span<char> buffer)35 StatusWithSize ToString<CallCounts>(const CallCounts& value,
36                                     span<char> buffer) {
37   return string::Format(buffer,
38                         "CallCounts {.ok = %d, .cancelled = %d}",
39                         value.ok,
40                         value.cancelled);
41 }
42 }  // namespace pw
43 
44 namespace pw::async::test {
45 namespace {
46 
47 struct CallCounter {
48   CallCounts counts;
fnpw::async::test::__anon3d08e71e0111::CallCounter49   auto fn() {
50     return [this](Context&, Status status) {
51       if (status.ok()) {
52         this->counts.ok++;
53       } else if (status.IsCancelled()) {
54         this->counts.cancelled++;
55       }
56     };
57   }
58 };
59 
TEST(FakeDispatcher,UnpostedTasksDontRun)60 TEST(FakeDispatcher, UnpostedTasksDontRun) {
61   FakeDispatcher dispatcher;
62   CallCounter counter;
63   Task task(counter.fn());
64   dispatcher.RunUntilIdle();
65   EXPECT_EQ(counter.counts, CallCounts{});
66 }
67 
TEST(FakeDispatcher,PostedTaskRunsOnce)68 TEST(FakeDispatcher, PostedTaskRunsOnce) {
69   FakeDispatcher dispatcher;
70   CallCounter counter;
71   Task task(counter.fn());
72   dispatcher.Post(task);
73   dispatcher.RunUntilIdle();
74   EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
75 }
76 
TEST(FakeDispatcher,TaskPostedTwiceBeforeRunningRunsOnce)77 TEST(FakeDispatcher, TaskPostedTwiceBeforeRunningRunsOnce) {
78   FakeDispatcher dispatcher;
79   CallCounter counter;
80   Task task(counter.fn());
81   dispatcher.Post(task);
82   dispatcher.Post(task);
83   dispatcher.RunUntilIdle();
84   EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
85 }
86 
TEST(FakeDispatcher,TaskRepostedAfterRunningRunsTwice)87 TEST(FakeDispatcher, TaskRepostedAfterRunningRunsTwice) {
88   FakeDispatcher dispatcher;
89   CallCounter counter;
90   Task task(counter.fn());
91   dispatcher.Post(task);
92   dispatcher.RunUntilIdle();
93   EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
94   dispatcher.Post(task);
95   dispatcher.RunUntilIdle();
96   EXPECT_EQ(counter.counts, CallCounts{.ok = 2});
97 }
98 
TEST(FakeDispatcher,TwoPostedTasksEachRunOnce)99 TEST(FakeDispatcher, TwoPostedTasksEachRunOnce) {
100   FakeDispatcher dispatcher;
101   CallCounter counter_1;
102   Task task_1(counter_1.fn());
103   CallCounter counter_2;
104   Task task_2(counter_2.fn());
105   dispatcher.Post(task_1);
106   dispatcher.Post(task_2);
107   dispatcher.RunUntilIdle();
108   EXPECT_EQ(counter_1.counts, CallCounts{.ok = 1});
109   EXPECT_EQ(counter_2.counts, CallCounts{.ok = 1});
110 }
111 
TEST(FakeDispatcher,PostedTasksRunInOrderForFairness)112 TEST(FakeDispatcher, PostedTasksRunInOrderForFairness) {
113   FakeDispatcher dispatcher;
114   pw::Vector<uint8_t, 3> task_run_order;
115   Task task_1([&task_run_order](auto...) { task_run_order.push_back(1); });
116   Task task_2([&task_run_order](auto...) { task_run_order.push_back(2); });
117   Task task_3([&task_run_order](auto...) { task_run_order.push_back(3); });
118   dispatcher.Post(task_1);
119   dispatcher.Post(task_2);
120   dispatcher.Post(task_3);
121   dispatcher.RunUntilIdle();
122   pw::Vector<uint8_t, 3> expected_run_order({1, 2, 3});
123   EXPECT_EQ(task_run_order, expected_run_order);
124 }
125 
TEST(FakeDispatcher,RequestStopQueuesPreviouslyPostedTaskWithCancel)126 TEST(FakeDispatcher, RequestStopQueuesPreviouslyPostedTaskWithCancel) {
127   FakeDispatcher dispatcher;
128   CallCounter counter;
129   Task task(counter.fn());
130   dispatcher.Post(task);
131   dispatcher.RequestStop();
132   dispatcher.RunUntilIdle();
133   EXPECT_EQ(counter.counts, CallCounts{.cancelled = 1});
134 }
135 
TEST(FakeDispatcher,RequestStopQueuesNewlyPostedTaskWithCancel)136 TEST(FakeDispatcher, RequestStopQueuesNewlyPostedTaskWithCancel) {
137   FakeDispatcher dispatcher;
138   CallCounter counter;
139   Task task(counter.fn());
140   dispatcher.RequestStop();
141   dispatcher.Post(task);
142   dispatcher.RunUntilIdle();
143   EXPECT_EQ(counter.counts, CallCounts{.cancelled = 1});
144 }
145 
TEST(FakeDispatcher,RunUntilIdleDoesNotRunFutureTask)146 TEST(FakeDispatcher, RunUntilIdleDoesNotRunFutureTask) {
147   FakeDispatcher dispatcher;
148   CallCounter counter;
149   // Should not run; RunUntilIdle() does not advance time.
150   Task task(counter.fn());
151   dispatcher.PostAfter(task, chrono::SystemClock::for_at_least(1ms));
152   dispatcher.RunUntilIdle();
153   EXPECT_EQ(counter.counts, CallCounts{});
154 }
155 
TEST(FakeDispatcher,PostAfterRunsTasksInSequence)156 TEST(FakeDispatcher, PostAfterRunsTasksInSequence) {
157   FakeDispatcher dispatcher;
158   pw::Vector<uint8_t, 3> task_run_order;
159   Task task_1([&task_run_order](auto...) { task_run_order.push_back(1); });
160   Task task_2([&task_run_order](auto...) { task_run_order.push_back(2); });
161   Task task_3([&task_run_order](auto...) { task_run_order.push_back(3); });
162   dispatcher.PostAfter(task_1, chrono::SystemClock::for_at_least(50ms));
163   dispatcher.PostAfter(task_2, chrono::SystemClock::for_at_least(25ms));
164   dispatcher.PostAfter(task_3, chrono::SystemClock::for_at_least(100ms));
165   dispatcher.RunFor(chrono::SystemClock::for_at_least(125ms));
166   pw::Vector<uint8_t, 3> expected_run_order({2, 1, 3});
167   EXPECT_EQ(task_run_order, expected_run_order);
168 }
169 
TEST(FakeDispatcher,PostAfterWithEarlierTimeRunsSooner)170 TEST(FakeDispatcher, PostAfterWithEarlierTimeRunsSooner) {
171   FakeDispatcher dispatcher;
172   CallCounter counter;
173   Task task(counter.fn());
174   dispatcher.PostAfter(task, chrono::SystemClock::for_at_least(100ms));
175   dispatcher.PostAfter(task, chrono::SystemClock::for_at_least(50ms));
176   dispatcher.RunFor(chrono::SystemClock::for_at_least(60ms));
177   EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
178 }
179 
TEST(FakeDispatcher,PostAfterWithLaterTimeRunsSooner)180 TEST(FakeDispatcher, PostAfterWithLaterTimeRunsSooner) {
181   FakeDispatcher dispatcher;
182   CallCounter counter;
183   Task task(counter.fn());
184   dispatcher.PostAfter(task, chrono::SystemClock::for_at_least(50ms));
185   dispatcher.PostAfter(task, chrono::SystemClock::for_at_least(100ms));
186   dispatcher.RunFor(chrono::SystemClock::for_at_least(60ms));
187   EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
188 }
189 
TEST(FakeDispatcher,PostThenPostAfterRunsImmediately)190 TEST(FakeDispatcher, PostThenPostAfterRunsImmediately) {
191   FakeDispatcher dispatcher;
192   CallCounter counter;
193   Task task(counter.fn());
194   dispatcher.Post(task);
195   dispatcher.PostAfter(task, chrono::SystemClock::for_at_least(50ms));
196   dispatcher.RunUntilIdle();
197   EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
198 }
199 
TEST(FakeDispatcher,PostAfterThenPostRunsImmediately)200 TEST(FakeDispatcher, PostAfterThenPostRunsImmediately) {
201   FakeDispatcher dispatcher;
202   CallCounter counter;
203   Task task(counter.fn());
204   dispatcher.PostAfter(task, chrono::SystemClock::for_at_least(50ms));
205   dispatcher.Post(task);
206   dispatcher.RunUntilIdle();
207   EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
208 }
209 
TEST(FakeDispatcher,CancelAfterPostStopsTaskFromRunning)210 TEST(FakeDispatcher, CancelAfterPostStopsTaskFromRunning) {
211   FakeDispatcher dispatcher;
212   CallCounter counter;
213   Task task(counter.fn());
214   dispatcher.Post(task);
215   EXPECT_TRUE(dispatcher.Cancel(task));
216   dispatcher.RunUntilIdle();
217   EXPECT_EQ(counter.counts, CallCounts{});
218 }
219 
TEST(FakeDispatcher,CancelAfterPostAfterStopsTaskFromRunning)220 TEST(FakeDispatcher, CancelAfterPostAfterStopsTaskFromRunning) {
221   FakeDispatcher dispatcher;
222   CallCounter counter;
223   Task task(counter.fn());
224   dispatcher.PostAfter(task, chrono::SystemClock::for_at_least(50ms));
225   EXPECT_TRUE(dispatcher.Cancel(task));
226   dispatcher.RunFor(chrono::SystemClock::for_at_least(60ms));
227   EXPECT_EQ(counter.counts, CallCounts{});
228 }
229 
TEST(FakeDispatcher,CancelAfterPostAndPostAfterStopsTaskFromRunning)230 TEST(FakeDispatcher, CancelAfterPostAndPostAfterStopsTaskFromRunning) {
231   FakeDispatcher dispatcher;
232   CallCounter counter;
233   Task task(counter.fn());
234   dispatcher.Post(task);
235   dispatcher.PostAfter(task, chrono::SystemClock::for_at_least(50ms));
236   EXPECT_TRUE(dispatcher.Cancel(task));
237   dispatcher.RunFor(chrono::SystemClock::for_at_least(60ms));
238   EXPECT_EQ(counter.counts, CallCounts{});
239 }
240 
TEST(FakeDispatcher,PostAgainAfterCancelRuns)241 TEST(FakeDispatcher, PostAgainAfterCancelRuns) {
242   FakeDispatcher dispatcher;
243   CallCounter counter;
244   Task task(counter.fn());
245   dispatcher.Post(task);
246   EXPECT_TRUE(dispatcher.Cancel(task));
247   dispatcher.Post(task);
248   dispatcher.RunUntilIdle();
249   EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
250 }
251 
TEST(FakeDispatcher,CancelWithoutPostReturnsFalse)252 TEST(FakeDispatcher, CancelWithoutPostReturnsFalse) {
253   FakeDispatcher dispatcher;
254   CallCounter counter;
255   Task task(counter.fn());
256   EXPECT_FALSE(dispatcher.Cancel(task));
257 }
258 
TEST(FakeDispatcher,CancelAfterRunningReturnsFalse)259 TEST(FakeDispatcher, CancelAfterRunningReturnsFalse) {
260   FakeDispatcher dispatcher;
261   CallCounter counter;
262   Task task(counter.fn());
263   dispatcher.Post(task);
264   dispatcher.RunUntilIdle();
265   EXPECT_EQ(counter.counts, CallCounts{.ok = 1});
266   EXPECT_FALSE(dispatcher.Cancel(task));
267 }
268 
TEST(FakeDispatcher,CancelInsideOtherTaskCancelsTaskWithoutRunningIt)269 TEST(FakeDispatcher, CancelInsideOtherTaskCancelsTaskWithoutRunningIt) {
270   FakeDispatcher dispatcher;
271 
272   CallCounter cancelled_task_counter;
273   Task cancelled_task(cancelled_task_counter.fn());
274 
275   Task canceling_task([&cancelled_task](Context& c, Status status) {
276     PW_TEST_ASSERT_OK(status);
277     ASSERT_TRUE(c.dispatcher->Cancel(cancelled_task));
278   });
279 
280   dispatcher.Post(canceling_task);
281   dispatcher.Post(cancelled_task);
282   dispatcher.RunUntilIdle();
283 
284   // NOTE:  the cancelled task is *not* run with `Cancel`.
285   // This is likely to produce strange behavior, and this contract should
286   // be revisited and carefully documented.
287   EXPECT_EQ(cancelled_task_counter.counts, CallCounts{});
288 }
289 
TEST(FakeDispatcher,CancelInsideCurrentTaskFails)290 TEST(FakeDispatcher, CancelInsideCurrentTaskFails) {
291   FakeDispatcher dispatcher;
292 
293   Task self_cancel_task;
294   self_cancel_task.set_function([&self_cancel_task](Context& c, Status status) {
295     PW_TEST_ASSERT_OK(status);
296     ASSERT_FALSE(c.dispatcher->Cancel(self_cancel_task));
297   });
298   dispatcher.Post(self_cancel_task);
299   dispatcher.RunUntilIdle();
300 }
301 
TEST(FakeDispatcher,RequestStopInsideOtherTaskCancelsOtherTask)302 TEST(FakeDispatcher, RequestStopInsideOtherTaskCancelsOtherTask) {
303   FakeDispatcher dispatcher;
304 
305   // This task is never executed and is cleaned up in RequestStop().
306   CallCounter task_counter;
307   Task task(task_counter.fn());
308 
309   int stop_count = 0;
310   Task stop_task([&stop_count]([[maybe_unused]] Context& c, Status status) {
311     PW_TEST_ASSERT_OK(status);
312     stop_count++;
313     static_cast<FakeDispatcher*>(c.dispatcher)->RequestStop();
314   });
315 
316   dispatcher.Post(stop_task);
317   dispatcher.Post(task);
318 
319   dispatcher.RunUntilIdle();
320   EXPECT_EQ(stop_count, 1);
321   EXPECT_EQ(task_counter.counts, CallCounts{.cancelled = 1});
322 }
323 
TEST(FakeDispatcher,TasksCancelledByDispatcherDestructor)324 TEST(FakeDispatcher, TasksCancelledByDispatcherDestructor) {
325   CallCounter counter;
326   Task task0(counter.fn()), task1(counter.fn()), task2(counter.fn());
327 
328   {
329     FakeDispatcher dispatcher;
330     dispatcher.PostAfter(task0, chrono::SystemClock::for_at_least(10s));
331     dispatcher.PostAfter(task1, chrono::SystemClock::for_at_least(10s));
332     dispatcher.PostAfter(task2, chrono::SystemClock::for_at_least(10s));
333   }
334 
335   ASSERT_EQ(counter.counts, CallCounts{.cancelled = 3});
336 }
337 
TEST(DispatcherBasic,TasksCancelledByRunFor)338 TEST(DispatcherBasic, TasksCancelledByRunFor) {
339   FakeDispatcher dispatcher;
340   CallCounter counter;
341   Task task0(counter.fn()), task1(counter.fn()), task2(counter.fn());
342   dispatcher.PostAfter(task0, chrono::SystemClock::for_at_least(10s));
343   dispatcher.PostAfter(task1, chrono::SystemClock::for_at_least(10s));
344   dispatcher.PostAfter(task2, chrono::SystemClock::for_at_least(10s));
345 
346   dispatcher.RequestStop();
347   dispatcher.RunFor(chrono::SystemClock::for_at_least(5s));
348   ASSERT_EQ(counter.counts, CallCounts{.cancelled = 3});
349 }
350 
351 }  // namespace
352 }  // namespace pw::async::test
353