1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stdio.h>
6 #include <lib/async-testutils/dispatcher_stub.h>
7 #include <lib/async/cpp/exception.h>
8 #include <unittest/unittest.h>
9
10 namespace {
11
12 const zx_handle_t dummy_task = static_cast<zx_handle_t>(1);
13 const uint32_t dummy_options = 99;
14 const zx_koid_t dummy_pid = 23;
15 const zx_koid_t dummy_tid = 42;
16 const zx_port_packet_t dummy_exception{
17 .key = 0,
18 .type = 0,
19 .status = ZX_OK,
20 .exception = {
21 .pid = dummy_pid,
22 .tid = dummy_tid,
23 .reserved0 = 0u,
24 .reserved1 = 0u},
25 };
26
27 class MockDispatcher : public async::DispatcherStub {
28 public:
29 enum class Op {
30 NONE,
31 BIND,
32 UNBIND,
33 };
34
BindExceptionPort(async_exception_t * exception)35 zx_status_t BindExceptionPort(async_exception_t* exception) override {
36 printf("%s: called\n", __func__);
37 last_op = Op::BIND;
38 last_exception = exception;
39 return next_status;
40 }
41
UnbindExceptionPort(async_exception_t * exception)42 zx_status_t UnbindExceptionPort(async_exception_t* exception) override {
43 printf("%s: called\n", __func__);
44 last_op = Op::UNBIND;
45 last_exception = exception;
46 return next_status;
47 }
48
49 Op last_op = Op::NONE;
50 async_exception_t* last_exception = nullptr;
51 zx_status_t next_status = ZX_OK;
52 };
53
54 class Harness {
55 public:
Harness()56 Harness() { Reset(); }
57
Reset()58 void Reset() {
59 handler_ran = false;
60 last_exception = nullptr;
61 last_status = ZX_ERR_INTERNAL;
62 last_report = nullptr;
63 }
64
Handler(async_dispatcher_t * dispatcher,async::ExceptionBase * exception,zx_status_t status,const zx_port_packet_t * report)65 void Handler(async_dispatcher_t* dispatcher, async::ExceptionBase* exception,
66 zx_status_t status, const zx_port_packet_t* report) {
67 handler_ran = true;
68 last_exception = exception;
69 last_status = status;
70 last_report = report;
71 }
72
73 virtual async::ExceptionBase& exception() = 0;
74
75 bool handler_ran;
76 async::ExceptionBase* last_exception;
77 zx_status_t last_status;
78 const zx_port_packet_t* last_report;
79 };
80
81 class LambdaHarness : public Harness {
82 public:
LambdaHarness(zx_handle_t task=ZX_HANDLE_INVALID,uint32_t options=0)83 LambdaHarness(zx_handle_t task = ZX_HANDLE_INVALID,
84 uint32_t options = 0)
85 : exception_{task, options,
86 [this](async_dispatcher_t* dispatcher, async::Exception* exception,
87 zx_status_t status, const zx_port_packet_t* report) {
88 Handler(dispatcher, exception, status, report);
89 }} {}
90
exception()91 async::ExceptionBase& exception() override { return exception_; }
92
93 private:
94 async::Exception exception_;
95 };
96
97 class MethodHarness : public Harness {
98 public:
MethodHarness(zx_handle_t task=ZX_HANDLE_INVALID,uint32_t options=0)99 MethodHarness(zx_handle_t task = ZX_HANDLE_INVALID,
100 uint32_t options = 0)
101 : exception_{this, task, options} {}
102
exception()103 async::ExceptionBase& exception() override { return exception_; }
104
105 private:
106 async::ExceptionMethod<Harness, &Harness::Handler> exception_;
107 };
108
109 template <typename Harness>
exception_is_bound_test()110 bool exception_is_bound_test() {
111 BEGIN_TEST;
112
113 MockDispatcher dispatcher;
114 Harness harness;
115
116 EXPECT_FALSE(harness.exception().is_bound());
117 EXPECT_EQ(harness.exception().Bind(&dispatcher), ZX_OK);
118 EXPECT_TRUE(harness.exception().is_bound());
119 EXPECT_EQ(harness.exception().Unbind(), ZX_OK);
120 EXPECT_FALSE(harness.exception().is_bound());
121
122 END_TEST;
123 }
124
125 template <typename Harness>
exception_bind_test()126 bool exception_bind_test() {
127 BEGIN_TEST;
128
129 MockDispatcher dispatcher;
130
131 {
132 Harness harness(dummy_task, dummy_options);
133 EXPECT_FALSE(harness.exception().is_bound());
134
135 dispatcher.next_status = ZX_OK;
136 EXPECT_EQ(ZX_OK, harness.exception().Bind(&dispatcher));
137 EXPECT_TRUE(harness.exception().is_bound());
138 EXPECT_EQ(MockDispatcher::Op::BIND, dispatcher.last_op);
139 EXPECT_EQ(dummy_task, dispatcher.last_exception->task);
140 EXPECT_EQ(dummy_options, dispatcher.last_exception->options);
141 EXPECT_FALSE(harness.handler_ran);
142
143 harness.Reset();
144 dispatcher.last_op = MockDispatcher::Op::NONE;
145 EXPECT_EQ(ZX_ERR_ALREADY_EXISTS, harness.exception().Bind(&dispatcher));
146 EXPECT_EQ(MockDispatcher::Op::NONE, dispatcher.last_op);
147 EXPECT_FALSE(harness.handler_ran);
148 }
149 EXPECT_EQ(MockDispatcher::Op::UNBIND, dispatcher.last_op);
150
151 {
152 Harness harness(dummy_task, dummy_options);
153 EXPECT_FALSE(harness.exception().is_bound());
154
155 dispatcher.next_status = ZX_ERR_BAD_STATE;
156 EXPECT_EQ(ZX_ERR_BAD_STATE, harness.exception().Bind(&dispatcher));
157 EXPECT_EQ(MockDispatcher::Op::BIND, dispatcher.last_op);
158 EXPECT_FALSE(harness.exception().is_bound());
159 EXPECT_FALSE(harness.handler_ran);
160 }
161 EXPECT_EQ(MockDispatcher::Op::BIND, dispatcher.last_op);
162
163 END_TEST;
164 }
165
166 template <typename Harness>
exception_unbind_test()167 bool exception_unbind_test() {
168 BEGIN_TEST;
169
170 MockDispatcher dispatcher;
171
172 {
173 Harness harness(dummy_task, dummy_options);
174 EXPECT_FALSE(harness.exception().is_bound());
175
176 EXPECT_EQ(ZX_ERR_NOT_FOUND, harness.exception().Unbind());
177 EXPECT_EQ(MockDispatcher::Op::NONE, dispatcher.last_op);
178 EXPECT_FALSE(harness.exception().is_bound());
179
180 EXPECT_EQ(ZX_OK, harness.exception().Bind(&dispatcher));
181 EXPECT_EQ(MockDispatcher::Op::BIND, dispatcher.last_op);
182 EXPECT_TRUE(harness.exception().is_bound());
183
184 EXPECT_EQ(ZX_OK, harness.exception().Unbind());
185 EXPECT_EQ(MockDispatcher::Op::UNBIND, dispatcher.last_op);
186 EXPECT_FALSE(harness.exception().is_bound());
187
188 dispatcher.last_op = MockDispatcher::Op::NONE;
189 EXPECT_EQ(ZX_ERR_NOT_FOUND, harness.exception().Unbind());
190 EXPECT_EQ(MockDispatcher::Op::NONE, dispatcher.last_op);
191 EXPECT_FALSE(harness.exception().is_bound());
192 }
193 EXPECT_EQ(MockDispatcher::Op::NONE, dispatcher.last_op);
194
195 END_TEST;
196 }
197
198 template <typename Harness>
exception_run_handler_test()199 bool exception_run_handler_test() {
200 BEGIN_TEST;
201
202 MockDispatcher dispatcher;
203
204 {
205 Harness harness(dummy_task, dummy_options);
206 EXPECT_FALSE(harness.exception().is_bound());
207
208 EXPECT_EQ(ZX_OK, harness.exception().Bind(&dispatcher));
209 EXPECT_EQ(MockDispatcher::Op::BIND, dispatcher.last_op);
210 EXPECT_TRUE(harness.exception().is_bound());
211
212 harness.Reset();
213 dispatcher.last_exception->handler(&dispatcher, dispatcher.last_exception, ZX_OK, &dummy_exception);
214 EXPECT_TRUE(harness.handler_ran);
215 EXPECT_EQ(&harness.exception(), harness.last_exception);
216 EXPECT_EQ(ZX_OK, harness.last_status);
217 EXPECT_EQ(&dummy_exception, harness.last_report);
218 EXPECT_TRUE(harness.exception().is_bound());
219 }
220 EXPECT_EQ(MockDispatcher::Op::UNBIND, dispatcher.last_op);
221
222 END_TEST;
223 }
224
unsupported_bind_test()225 bool unsupported_bind_test() {
226 BEGIN_TEST;
227
228 async::DispatcherStub dispatcher;
229 async_exception_t exception{};
230 EXPECT_EQ(ZX_ERR_NOT_SUPPORTED,
231 async_bind_exception_port(&dispatcher, &exception),
232 "valid args");
233
234 END_TEST;
235 }
236
unsupported_unbind_test()237 bool unsupported_unbind_test() {
238 BEGIN_TEST;
239
240 async::DispatcherStub dispatcher;
241 async_exception_t exception{};
242 EXPECT_EQ(ZX_ERR_NOT_SUPPORTED,
243 async_unbind_exception_port(&dispatcher, &exception),
244 "valid args");
245
246 END_TEST;
247 }
248
unsupported_resume_test()249 bool unsupported_resume_test() {
250 BEGIN_TEST;
251
252 async::DispatcherStub dispatcher;
253 async_exception_t exception{};
254 EXPECT_EQ(ZX_ERR_NOT_SUPPORTED,
255 async_resume_from_exception(&dispatcher, &exception,
256 ZX_HANDLE_INVALID, 0u),
257 "valid args");
258
259 END_TEST;
260 }
261
262 } // namespace
263
264 BEGIN_TEST_CASE(exception_tests)
265 RUN_TEST((exception_is_bound_test<LambdaHarness>))
266 RUN_TEST((exception_is_bound_test<MethodHarness>))
267 RUN_TEST((exception_bind_test<LambdaHarness>))
268 RUN_TEST((exception_bind_test<MethodHarness>))
269 RUN_TEST((exception_unbind_test<LambdaHarness>))
270 RUN_TEST((exception_unbind_test<MethodHarness>))
271 RUN_TEST((exception_run_handler_test<LambdaHarness>))
272 RUN_TEST((exception_run_handler_test<MethodHarness>))
273 RUN_TEST(unsupported_bind_test)
274 RUN_TEST(unsupported_unbind_test)
275 RUN_TEST(unsupported_resume_test)
276 END_TEST_CASE(exception_tests)
277