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 <fidl/test/spaceship/c/fidl.h>
6 #include <lib/async-loop/loop.h>
7 #include <lib/fidl-utils/bind.h>
8 #include <lib/zx/channel.h>
9 #include <string.h>
10 #include <zircon/fidl.h>
11 #include <zircon/syscalls.h>
12
13 #include <unittest/unittest.h>
14
15 #include <utility>
16
17 namespace {
18
19 class SpaceShip {
20 public:
21 using SpaceShipBinder = fidl::Binder<SpaceShip>;
22
~SpaceShip()23 virtual ~SpaceShip() {};
24
AdjustHeading(const uint32_t * stars_data,size_t stars_count,fidl_txn_t * txn)25 virtual zx_status_t AdjustHeading(const uint32_t* stars_data,
26 size_t stars_count, fidl_txn_t* txn) {
27 EXPECT_EQ(3u, stars_count, "");
28 EXPECT_EQ(11u, stars_data[0], "");
29 EXPECT_EQ(0u, stars_data[1], "");
30 EXPECT_EQ(UINT32_MAX, stars_data[2], "");
31 return fidl_test_spaceship_SpaceShipAdjustHeading_reply(txn, -12);
32 }
33
ScanForLifeforms(fidl_txn_t * txn)34 virtual zx_status_t ScanForLifeforms(fidl_txn_t* txn) {
35 const uint32_t lifesigns[5] = {42u, 43u, UINT32_MAX, 0u, 9u};
36 return fidl_test_spaceship_SpaceShipScanForLifeforms_reply(txn, lifesigns, 5);
37 }
38
ScanForTensorLifeforms(fidl_txn_t * txn)39 virtual zx_status_t ScanForTensorLifeforms(fidl_txn_t* txn) {
40 uint32_t lifesigns[8][5][3] = {};
41 // fill the array with increasing counter
42 uint32_t counter = 0;
43 for (size_t i = 0; i < 8; i++) {
44 for (size_t j = 0; j < 5; j++) {
45 for (size_t k = 0; k < 3; k++) {
46 lifesigns[i][j][k] = counter;
47 counter += 1;
48 }
49 }
50 }
51 return fidl_test_spaceship_SpaceShipScanForTensorLifeforms_reply(txn, lifesigns);
52 }
53
SetAstrometricsListener(zx_handle_t listener)54 virtual zx_status_t SetAstrometricsListener(zx_handle_t listener) {
55 EXPECT_EQ(ZX_OK, fidl_test_spaceship_AstrometricsListenerOnNova(listener), "");
56 EXPECT_EQ(ZX_OK, zx_handle_close(listener), "");
57 return ZX_OK;
58 }
59
SetDefenseCondition(fidl_test_spaceship_Alert alert)60 virtual zx_status_t SetDefenseCondition(fidl_test_spaceship_Alert alert) {
61 EXPECT_EQ(fidl_test_spaceship_Alert_RED, alert, "");
62 return ZX_OK;
63 }
64
GetFuelRemaining(zx_handle_t cancel,fidl_txn_t * txn)65 virtual zx_status_t GetFuelRemaining(zx_handle_t cancel, fidl_txn_t* txn) {
66 EXPECT_EQ(ZX_HANDLE_INVALID, cancel, "");
67 const fidl_test_spaceship_FuelLevel level = {
68 .reaction_mass = 1641u,
69 };
70 return fidl_test_spaceship_SpaceShipGetFuelRemaining_reply(txn, ZX_OK, &level);
71 }
72
AddFuelTank(const fidl_test_spaceship_FuelLevel * level,fidl_txn_t * txn)73 virtual zx_status_t AddFuelTank(const fidl_test_spaceship_FuelLevel* level, fidl_txn_t* txn) {
74 return fidl_test_spaceship_SpaceShipAddFuelTank_reply(txn, level->reaction_mass / 2);
75 }
76
ReportAstrologicalData(const fidl_test_spaceship_AstrologicalData * data,fidl_txn_t * txn)77 virtual zx_status_t ReportAstrologicalData(const fidl_test_spaceship_AstrologicalData* data,
78 fidl_txn_t* txn) {
79 EXPECT_EQ(data->tag, fidl_test_spaceship_AstrologicalDataTag_star, "");
80 for (size_t idx = 0; idx < sizeof(data->star.data); ++idx) {
81 EXPECT_EQ(42, data->star.data[idx], "");
82 }
83
84 const zx_status_t status = ZX_OK;
85 return fidl_test_spaceship_SpaceShipReportAstrologicalData_reply(txn, status);
86 }
87
Bind(async_dispatcher_t * dispatcher,zx::channel channel)88 virtual zx_status_t Bind(async_dispatcher_t* dispatcher, zx::channel channel) {
89 static constexpr fidl_test_spaceship_SpaceShip_ops_t kOps = {
90 .AdjustHeading = SpaceShipBinder::BindMember<&SpaceShip::AdjustHeading>,
91 .ScanForLifeforms = SpaceShipBinder::BindMember<&SpaceShip::ScanForLifeforms>,
92 .SetAstrometricsListener =
93 SpaceShipBinder::BindMember<&SpaceShip::SetAstrometricsListener>,
94 .SetDefenseCondition = SpaceShipBinder::BindMember<&SpaceShip::SetDefenseCondition>,
95 .GetFuelRemaining = SpaceShipBinder::BindMember<&SpaceShip::GetFuelRemaining>,
96 .AddFuelTank = SpaceShipBinder::BindMember<&SpaceShip::AddFuelTank>,
97 .ScanForTensorLifeforms =
98 SpaceShipBinder::BindMember<&SpaceShip::ScanForTensorLifeforms>,
99 .ReportAstrologicalData =
100 SpaceShipBinder::BindMember<&SpaceShip::ReportAstrologicalData>,
101 };
102
103 return SpaceShipBinder::BindOps<fidl_test_spaceship_SpaceShip_dispatch>(
104 dispatcher, std::move(channel), this, &kOps);
105 }
106 };
107
spaceship_test(void)108 static bool spaceship_test(void) {
109 BEGIN_TEST;
110
111 zx::channel client, server;
112 zx_status_t status = zx::channel::create(0, &client, &server);
113 ASSERT_EQ(ZX_OK, status, "");
114
115 async_loop_t* loop = NULL;
116 ASSERT_EQ(ZX_OK, async_loop_create(&kAsyncLoopConfigNoAttachToThread, &loop), "");
117 ASSERT_EQ(ZX_OK, async_loop_start_thread(loop, "spaceship-dispatcher", NULL), "");
118
119 async_dispatcher_t* dispatcher = async_loop_get_dispatcher(loop);
120 SpaceShip ship;
121 ASSERT_EQ(ZX_OK, ship.Bind(dispatcher, std::move(server)));
122
123 {
124 const uint32_t stars[3] = {11u, 0u, UINT32_MAX};
125 int8_t result = 0;
126 ASSERT_EQ(ZX_OK,
127 fidl_test_spaceship_SpaceShipAdjustHeading(client.get(), stars, 3, &result));
128 ASSERT_EQ(-12, result, "");
129 }
130
131 {
132 constexpr uint32_t kNumStarsOverflow = fidl_test_spaceship_MaxStarsAdjustHeading * 2;
133 const uint32_t stars[kNumStarsOverflow] = {};
134 int8_t result = 0;
135 ASSERT_EQ(ZX_ERR_INVALID_ARGS,
136 fidl_test_spaceship_SpaceShipAdjustHeading(client.get(),
137 stars,
138 kNumStarsOverflow,
139 &result));
140 }
141
142 {
143 int8_t result = 0;
144 ASSERT_EQ(
145 ZX_ERR_INVALID_ARGS,
146 fidl_test_spaceship_SpaceShipAdjustHeading(client.get(), nullptr, 1 << 31, &result));
147 }
148
149 {
150 uint32_t lifesigns[64];
151 size_t actual = 0;
152 ASSERT_EQ(ZX_OK, fidl_test_spaceship_SpaceShipScanForLifeforms(client.get(), lifesigns,
153 64, &actual));
154 ASSERT_EQ(5u, actual, "");
155 ASSERT_EQ(42u, lifesigns[0], "");
156 ASSERT_EQ(43u, lifesigns[1], "");
157 ASSERT_EQ(UINT32_MAX, lifesigns[2], "");
158 ASSERT_EQ(0u, lifesigns[3], "");
159 ASSERT_EQ(9u, lifesigns[4], "");
160 }
161
162 {
163 uint32_t lifesigns[8][5][3];
164 ASSERT_EQ(ZX_OK, fidl_test_spaceship_SpaceShipScanForTensorLifeforms(client.get(),
165 lifesigns), "");
166 uint32_t counter = 0;
167 for (size_t i = 0; i < 8; i++) {
168 for (size_t j = 0; j < 5; j++) {
169 for (size_t k = 0; k < 3; k++) {
170 ASSERT_EQ(counter, lifesigns[i][j][k], "");
171 counter += 1;
172 }
173 }
174 }
175 }
176
177 {
178 zx::channel listener_client, listener_server;
179 status = zx::channel::create(0, &listener_client, &listener_server);
180 ASSERT_EQ(ZX_OK, status, "");
181 ASSERT_EQ(ZX_OK,
182 fidl_test_spaceship_SpaceShipSetAstrometricsListener(client.get(),
183 listener_client.release()));
184 ASSERT_EQ(ZX_OK, listener_server.wait_one(ZX_CHANNEL_READABLE, zx::time::infinite(), NULL));
185 ASSERT_EQ(ZX_OK, zx_handle_close(listener_server.release()));
186 }
187
188 {
189 ASSERT_EQ(ZX_OK,
190 fidl_test_spaceship_SpaceShipSetDefenseCondition(client.get(),
191 fidl_test_spaceship_Alert_RED));
192 }
193
194 {
195 fidl_test_spaceship_FuelLevel level;
196 ASSERT_EQ(ZX_OK, fidl_test_spaceship_SpaceShipGetFuelRemaining(client.get(),
197 ZX_HANDLE_INVALID, &status,
198 &level));
199 ASSERT_EQ(ZX_OK, status, "");
200 ASSERT_EQ(1641u, level.reaction_mass, "");
201 }
202
203 {
204 fidl_test_spaceship_FuelLevel level = {
205 .reaction_mass = 9482,
206 };
207 uint32_t out_consumed = 0u;
208 ASSERT_EQ(ZX_OK, fidl_test_spaceship_SpaceShipAddFuelTank(client.get(), &level,
209 &out_consumed));
210 ASSERT_EQ(4741u, out_consumed, "");
211 }
212
213 {
214 fidl_test_spaceship_AstrologicalData data = {};
215 data.tag = fidl_test_spaceship_AstrologicalDataTag_star;
216 memset(&data.star, 42, sizeof(data.star));
217 ASSERT_EQ(ZX_OK, fidl_test_spaceship_SpaceShipReportAstrologicalData(client.get(), &data, &status));
218 ASSERT_EQ(ZX_OK, status);
219 }
220
221 ASSERT_EQ(ZX_OK, zx_handle_close(client.release()));
222
223 async_loop_destroy(loop);
224
225 END_TEST;
226 }
227
228 // A variant of spaceship which responds to requests asynchronously.
229 class AsyncSpaceShip : public SpaceShip {
230 public:
231 using SpaceShipBinder = fidl::Binder<AsyncSpaceShip>;
232
~AsyncSpaceShip()233 virtual ~AsyncSpaceShip() {};
234
235 // Creates a |fidl_async_txn| using the C++ wrapper, and pushes the
236 // computation to a background thread.
237 //
238 // This background thread responds to the original |txn|, and rebinds the connection
239 // to the dispatcher.
AdjustHeading(const uint32_t * stars_data,size_t stars_count,fidl_txn_t * txn)240 zx_status_t AdjustHeading(const uint32_t* stars_data, size_t stars_count,
241 fidl_txn_t* txn) override {
242 EXPECT_EQ(3u, stars_count, "");
243 EXPECT_EQ(11u, stars_data[0], "");
244 EXPECT_EQ(0u, stars_data[1], "");
245 EXPECT_EQ(UINT32_MAX, stars_data[2], "");
246 static constexpr auto handler = [](void* arg) {
247 auto spaceship = reinterpret_cast<AsyncSpaceShip*>(arg);
248 EXPECT_EQ(ZX_OK,
249 fidl_test_spaceship_SpaceShipAdjustHeading_reply(
250 spaceship->async_txn_.Transaction(), -12));
251 EXPECT_EQ(ZX_OK, spaceship->async_txn_.Rebind());
252 return 0;
253 };
254
255 async_txn_.Reset(txn);
256 EXPECT_EQ(thrd_success, thrd_create(&thrd_, handler, this));
257 return ZX_ERR_ASYNC;
258 }
259
260 // Creates a |fidl_async_txn| using the C++ wrapper, and pushes the
261 // computation to a background thread.
262 //
263 // This background thread responds to the original |txn|, but does not rebind
264 // the connection to the dispatcher. This completes the asynchronous transaction and destroys
265 // the original binding.
ScanForLifeforms(fidl_txn_t * txn)266 zx_status_t ScanForLifeforms(fidl_txn_t* txn) override {
267 static constexpr auto handler = [](void* arg) {
268 auto spaceship = reinterpret_cast<AsyncSpaceShip*>(arg);
269 const uint32_t lifesigns[2] = {42u, 43u};
270 EXPECT_EQ(ZX_OK,
271 fidl_test_spaceship_SpaceShipScanForLifeforms_reply(
272 spaceship->async_txn_.Transaction(), lifesigns, 2));
273 spaceship->async_txn_.Reset();
274 return 0;
275 };
276
277 async_txn_.Reset(txn);
278 EXPECT_EQ(thrd_success, thrd_create(&thrd_, handler, this));
279 return ZX_ERR_ASYNC;
280 }
281
Join()282 void Join() {
283 int res;
284 EXPECT_EQ(thrd_success, thrd_join(thrd_, &res));
285 EXPECT_EQ(0, res);
286 }
287
Bind(async_dispatcher_t * dispatcher,zx::channel channel)288 zx_status_t Bind(async_dispatcher_t* dispatcher, zx::channel channel) override {
289 static constexpr fidl_test_spaceship_SpaceShip_ops_t kOps = {
290 .AdjustHeading = SpaceShipBinder::BindMember<&AsyncSpaceShip::AdjustHeading>,
291 .ScanForLifeforms = SpaceShipBinder::BindMember<&AsyncSpaceShip::ScanForLifeforms>,
292 .SetAstrometricsListener =
293 SpaceShipBinder::BindMember<&SpaceShip::SetAstrometricsListener>,
294 .SetDefenseCondition = SpaceShipBinder::BindMember<&SpaceShip::SetDefenseCondition>,
295 .GetFuelRemaining = SpaceShipBinder::BindMember<&SpaceShip::GetFuelRemaining>,
296 .AddFuelTank = SpaceShipBinder::BindMember<&SpaceShip::AddFuelTank>,
297 .ScanForTensorLifeforms =
298 SpaceShipBinder::BindMember<&SpaceShip::ScanForTensorLifeforms>,
299 .ReportAstrologicalData =
300 SpaceShipBinder::BindMember<&SpaceShip::ReportAstrologicalData>,
301 };
302
303 return SpaceShipBinder::BindOps<fidl_test_spaceship_SpaceShip_dispatch>(
304 dispatcher, std::move(channel), this, &kOps);
305 }
306
307 private:
308 thrd_t thrd_;
309 fidl::AsyncTransaction async_txn_;
310 };
311
spaceship_async_test(void)312 static bool spaceship_async_test(void) {
313 BEGIN_TEST;
314
315 zx::channel client, server;
316 zx_status_t status = zx::channel::create(0, &client, &server);
317 ASSERT_EQ(ZX_OK, status, "");
318
319 async_loop_t* loop = NULL;
320 ASSERT_EQ(ZX_OK, async_loop_create(&kAsyncLoopConfigNoAttachToThread, &loop), "");
321 ASSERT_EQ(ZX_OK, async_loop_start_thread(loop, "spaceship-dispatcher", NULL), "");
322
323 async_dispatcher_t* dispatcher = async_loop_get_dispatcher(loop);
324 AsyncSpaceShip ship;
325 ASSERT_EQ(ZX_OK, ship.Bind(dispatcher, std::move(server)));
326
327 // Try invoking a member function which responds asynchronously and rebinds the connection.
328 {
329 const uint32_t stars[3] = {11u, 0u, UINT32_MAX};
330 int8_t result = 0;
331 ASSERT_EQ(ZX_OK,
332 fidl_test_spaceship_SpaceShipAdjustHeading(client.get(), stars, 3, &result));
333 ASSERT_EQ(-12, result, "");
334 ship.Join();
335 }
336
337 // Try invoking a member function which responds asynchronously, but does not rebind the
338 // connection. We should be able to observe that the server terminates the connection.
339 {
340 uint32_t lifesigns[64];
341 size_t actual = 0;
342 ASSERT_EQ(ZX_OK, fidl_test_spaceship_SpaceShipScanForLifeforms(client.get(), lifesigns,
343 64, &actual));
344 ASSERT_EQ(2u, actual, "");
345 ASSERT_EQ(42u, lifesigns[0], "");
346 ASSERT_EQ(43u, lifesigns[1], "");
347
348 zx_signals_t pending;
349 zx::time deadline = zx::deadline_after(zx::sec(5));
350 ASSERT_EQ(ZX_OK, client.wait_one(ZX_CHANNEL_PEER_CLOSED, deadline, &pending));
351 ASSERT_EQ(pending & ZX_CHANNEL_PEER_CLOSED, ZX_CHANNEL_PEER_CLOSED);
352 ship.Join();
353 }
354
355 ASSERT_EQ(ZX_OK, zx_handle_close(client.release()));
356
357 async_loop_destroy(loop);
358
359 END_TEST;
360 }
361
362 // These classes represents a compile-time check:
363 //
364 // We should be able to bind a derived class to its own methods,
365 // but also to methods of the base class.
366 //
367 // However, we should not be able to bind to an unrelated class.
368 class NotDerived {
369 public:
AdjustHeading(const uint32_t * stars_data,size_t stars_count,fidl_txn_t * txn)370 zx_status_t AdjustHeading(const uint32_t* stars_data, size_t stars_count, fidl_txn_t* txn) {
371 return ZX_ERR_NOT_SUPPORTED;
372 }
373 };
374
375 class Derived : public SpaceShip {
376 public:
377 using DerivedBinder = fidl::Binder<Derived>;
378
ScanForLifeforms(fidl_txn_t * txn)379 zx_status_t ScanForLifeforms(fidl_txn_t* txn) override {
380 return ZX_OK;
381 }
382
Bind(async_dispatcher_t * dispatcher,zx::channel channel)383 zx_status_t Bind(async_dispatcher_t* dispatcher, zx::channel channel) override {
384 static constexpr fidl_test_spaceship_SpaceShip_ops_t kOps = {
385 // (Under the failure case) Tries to bind to a member, such that the
386 // context object passed to BindOps does not match the BindMember
387 // callback. This should fail at compile time.
388 #if TEST_WILL_NOT_COMPILE || 0
389 .AdjustHeading = DerivedBinder::BindMember<&NotDerived::AdjustHeading>,
390 #else
391 .AdjustHeading = DerivedBinder::BindMember<&SpaceShip::AdjustHeading>,
392 #endif
393 // Binds a member of the derived class to the derived method:
394 // This is the typical use case of the Binder object.
395 .ScanForLifeforms = DerivedBinder::BindMember<&Derived::ScanForLifeforms>,
396
397 // Binds a member of the derived class to the base method:
398 // The compile time check should allow this, because the "Derived"
399 // class should be castable to a "SpaceShip" class.
400 .SetAstrometricsListener =
401 DerivedBinder::BindMember<&SpaceShip::SetAstrometricsListener>,
402
403 // The remaining functions cover already tested behavior, but just
404 // fill the ops table.
405 .SetDefenseCondition = DerivedBinder::BindMember<&SpaceShip::SetDefenseCondition>,
406 .GetFuelRemaining = DerivedBinder::BindMember<&SpaceShip::GetFuelRemaining>,
407 .AddFuelTank = DerivedBinder::BindMember<&SpaceShip::AddFuelTank>,
408 .ScanForTensorLifeforms = DerivedBinder::BindMember<&Derived::ScanForTensorLifeforms>,
409 .ReportAstrologicalData = DerivedBinder::BindMember<&Derived::ReportAstrologicalData>,
410 };
411
412 return SpaceShipBinder::BindOps<fidl_test_spaceship_SpaceShip_dispatch>(
413 dispatcher, std::move(channel), this, &kOps);
414 }
415 };
416
417 } // namespace
418
419 BEGIN_TEST_CASE(spaceship_tests_cpp)
420 RUN_NAMED_TEST("fidl.test.spaceship.SpaceShip test", spaceship_test)
421 RUN_NAMED_TEST("fidl.test.spaceship.SpaceShip async test", spaceship_async_test)
422 END_TEST_CASE(spaceship_tests_cpp);
423