1 // Copyright 2024 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
15 #include "pw_async_fuchsia/dispatcher.h"
16
17 #include <lib/async-testing/test_loop.h>
18
19 #include "pw_async_fuchsia/util.h"
20 #include "pw_unit_test/framework.h"
21
22 #define ASSERT_CANCELLED(status) ASSERT_EQ(Status::Cancelled(), status)
23
24 using namespace std::chrono_literals;
25
26 namespace pw::async_fuchsia {
27
28 class DispatcherFuchsiaTest : public ::testing::Test {
29 public:
dispatcher()30 async_dispatcher_t* dispatcher() { return loop_.dispatcher(); }
RunLoopUntilIdle()31 void RunLoopUntilIdle() { loop_.RunUntilIdle(); }
RunLoopFor(zx::duration duration)32 void RunLoopFor(zx::duration duration) { loop_.RunFor(duration); }
33
34 private:
35 ::async::TestLoop loop_;
36 };
37
TEST_F(DispatcherFuchsiaTest,TimeConversions)38 TEST_F(DispatcherFuchsiaTest, TimeConversions) {
39 zx::time time{timespec{123, 456}};
40 chrono::SystemClock::time_point tp =
41 pw::async_fuchsia::ZxTimeToTimepoint(time);
42 EXPECT_EQ(tp.time_since_epoch(), 123s + 456ns);
43 EXPECT_EQ(pw::async_fuchsia::TimepointToZxTime(tp), time);
44 }
45
TEST_F(DispatcherFuchsiaTest,Basic)46 TEST_F(DispatcherFuchsiaTest, Basic) {
47 FuchsiaDispatcher fuchsia_dispatcher(dispatcher());
48
49 bool set = false;
50 async::Task task([&set](async::Context& ctx, Status status) {
51 PW_TEST_ASSERT_OK(status);
52 set = true;
53 });
54 fuchsia_dispatcher.Post(task);
55
56 RunLoopUntilIdle();
57 EXPECT_TRUE(set);
58 }
59
TEST_F(DispatcherFuchsiaTest,DelayedTasks)60 TEST_F(DispatcherFuchsiaTest, DelayedTasks) {
61 FuchsiaDispatcher fuchsia_dispatcher(dispatcher());
62
63 int c = 0;
64 async::Task first([&c](async::Context& ctx, Status status) {
65 PW_TEST_ASSERT_OK(status);
66 c = c * 10 + 1;
67 });
68 async::Task second([&c](async::Context& ctx, Status status) {
69 PW_TEST_ASSERT_OK(status);
70 c = c * 10 + 2;
71 });
72 async::Task third([&c](async::Context& ctx, Status status) {
73 PW_TEST_ASSERT_OK(status);
74 c = c * 10 + 3;
75 });
76
77 fuchsia_dispatcher.PostAfter(third, 20ms);
78 fuchsia_dispatcher.PostAfter(first, 5ms);
79 fuchsia_dispatcher.PostAfter(second, 10ms);
80
81 RunLoopFor(zx::msec(25));
82 EXPECT_EQ(c, 123);
83 }
84
TEST_F(DispatcherFuchsiaTest,CancelTask)85 TEST_F(DispatcherFuchsiaTest, CancelTask) {
86 FuchsiaDispatcher fuchsia_dispatcher(dispatcher());
87
88 async::Task task([](async::Context& ctx, Status status) { FAIL(); });
89 fuchsia_dispatcher.Post(task);
90 EXPECT_TRUE(fuchsia_dispatcher.Cancel(task));
91
92 RunLoopUntilIdle();
93 }
94
95 class DestructionChecker {
96 public:
DestructionChecker(bool * flag)97 explicit DestructionChecker(bool* flag) : flag_(flag) {}
DestructionChecker(DestructionChecker && other)98 DestructionChecker(DestructionChecker&& other) {
99 flag_ = other.flag_;
100 other.flag_ = nullptr;
101 }
~DestructionChecker()102 ~DestructionChecker() {
103 if (flag_) {
104 *flag_ = true;
105 }
106 }
107
108 private:
109 bool* flag_;
110 };
111
TEST_F(DispatcherFuchsiaTest,HeapAllocatedTasks)112 TEST_F(DispatcherFuchsiaTest, HeapAllocatedTasks) {
113 FuchsiaDispatcher fuchsia_dispatcher(dispatcher());
114
115 int c = 0;
116 for (int i = 0; i < 3; i++) {
117 Post(&fuchsia_dispatcher, [&c](async::Context& ctx, Status status) {
118 PW_TEST_ASSERT_OK(status);
119 c++;
120 });
121 }
122
123 EXPECT_EQ(c, 0);
124 RunLoopUntilIdle();
125 EXPECT_EQ(c, 3);
126
127 // Test that the lambda is destroyed after being called.
128 bool flag = false;
129 Post(&fuchsia_dispatcher,
130 [checker = DestructionChecker(&flag)](async::Context& ctx,
131 Status status) {});
132 EXPECT_FALSE(flag);
133 RunLoopUntilIdle();
134 EXPECT_TRUE(flag);
135 }
136
TEST_F(DispatcherFuchsiaTest,ChainedTasks)137 TEST_F(DispatcherFuchsiaTest, ChainedTasks) {
138 FuchsiaDispatcher fuchsia_dispatcher(dispatcher());
139
140 int c = 0;
141
142 Post(&fuchsia_dispatcher, [&c](async::Context& ctx, Status status) {
143 PW_TEST_ASSERT_OK(status);
144 c++;
145 Post(ctx.dispatcher, [&c](async::Context& ctx, Status status) {
146 PW_TEST_ASSERT_OK(status);
147 c++;
148 Post(ctx.dispatcher, [&c](async::Context& ctx, Status status) {
149 PW_TEST_ASSERT_OK(status);
150 c++;
151 });
152 });
153 });
154
155 RunLoopUntilIdle();
156 EXPECT_EQ(c, 3);
157 }
158
159 } // namespace pw::async_fuchsia
160