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