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