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