Flutter Engine
 
Loading...
Searching...
No Matches
fl_binary_messenger_test.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter 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// Included first as it collides with the X11 headers.
6#include "gtest/gtest.h"
7
8#include <pthread.h>
9#include <cstring>
10
17
18// Checks can send a message.
19TEST(FlBinaryMessengerTest, Send) {
20 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
21
22 g_autoptr(FlDartProject) project = fl_dart_project_new();
23 g_autoptr(FlEngine) engine = fl_engine_new(project);
24
25 g_autoptr(GError) error = nullptr;
26 EXPECT_TRUE(fl_engine_start(engine, &error));
27 EXPECT_EQ(error, nullptr);
28
29 FlutterDataCallback response_callback;
30 void* response_callback_user_data;
33 PlatformMessageCreateResponseHandle,
34 ([&response_callback, &response_callback_user_data](
35 auto engine, FlutterDataCallback data_callback, void* user_data,
37 response_callback = data_callback;
38 response_callback_user_data = user_data;
39 return kSuccess;
40 }));
42 SendPlatformMessage,
43 ([&response_callback, &response_callback_user_data](
45 EXPECT_STREQ(message->channel, "test");
46 g_autofree gchar* text =
47 g_strndup(reinterpret_cast<const gchar*>(message->message),
48 message->message_size);
49 EXPECT_STREQ(text, "Marco!");
50
51 const gchar* response = "Polo!";
52 response_callback(reinterpret_cast<const uint8_t*>(response),
53 strlen(response), response_callback_user_data);
54
55 return kSuccess;
56 }));
57
58 g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
59 const char* text = "Marco!";
60 g_autoptr(GBytes) message = g_bytes_new(text, strlen(text));
62 messenger, "test", message, nullptr,
63 [](GObject* object, GAsyncResult* result, gpointer user_data) {
64 g_autoptr(GError) error = nullptr;
66 FL_BINARY_MESSENGER(object), result, &error);
67 EXPECT_NE(message, nullptr);
68 EXPECT_EQ(error, nullptr);
69
70 g_autofree gchar* text = g_strndup(
71 static_cast<const gchar*>(g_bytes_get_data(message, nullptr)),
72 g_bytes_get_size(message));
73 EXPECT_STREQ(text, "Polo!");
74
75 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
76 },
77 loop);
78
79 g_main_loop_run(loop);
80}
81
82// Checks sending nullptr for a message works.
83TEST(FlBinaryMessengerTest, SendNullptr) {
84 g_autoptr(FlDartProject) project = fl_dart_project_new();
85 g_autoptr(FlEngine) engine = fl_engine_new(project);
86
87 g_autoptr(GError) error = nullptr;
88 EXPECT_TRUE(fl_engine_start(engine, &error));
89 EXPECT_EQ(error, nullptr);
90
91 bool called = false;
93 SendPlatformMessage,
94 ([&called](auto engine, const FlutterPlatformMessage* message) {
95 called = true;
96
97 EXPECT_STREQ(message->channel, "test");
98 // Note we don't check message->message as it could be nullptr or a
99 // pointer to an buffer - either way it wouldn't be accessed.
100 EXPECT_EQ(message->message_size, static_cast<size_t>(0));
101
102 return kSuccess;
103 }));
104
105 g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
106 fl_binary_messenger_send_on_channel(messenger, "test", nullptr, nullptr,
107 nullptr, nullptr);
108 EXPECT_TRUE(called);
109}
110
111// Checks sending a zero length message works.
112TEST(FlBinaryMessengerTest, SendEmpty) {
113 g_autoptr(FlDartProject) project = fl_dart_project_new();
114 g_autoptr(FlEngine) engine = fl_engine_new(project);
115
116 g_autoptr(GError) error = nullptr;
117 EXPECT_TRUE(fl_engine_start(engine, &error));
118 EXPECT_EQ(error, nullptr);
119
120 bool called = false;
122 SendPlatformMessage,
123 ([&called](auto engine, const FlutterPlatformMessage* message) {
124 called = true;
125
126 EXPECT_STREQ(message->channel, "test");
127 EXPECT_EQ(message->message_size, static_cast<size_t>(0));
128
129 return kSuccess;
130 }));
131 g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
132 g_autoptr(GBytes) message = g_bytes_new(nullptr, 0);
133 fl_binary_messenger_send_on_channel(messenger, "test", message, nullptr,
134 nullptr, nullptr);
135 EXPECT_TRUE(called);
136}
137
138// Checks the engine returning a nullptr message work.
139TEST(FlBinaryMessengerTest, NullptrResponse) {
140 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
141
142 g_autoptr(FlDartProject) project = fl_dart_project_new();
143 g_autoptr(FlEngine) engine = fl_engine_new(project);
144
145 g_autoptr(GError) error = nullptr;
146 EXPECT_TRUE(fl_engine_start(engine, &error));
147 EXPECT_EQ(error, nullptr);
148
149 FlutterDataCallback response_callback;
150 void* response_callback_user_data;
153 PlatformMessageCreateResponseHandle,
154 ([&response_callback, &response_callback_user_data](
155 auto engine, FlutterDataCallback data_callback, void* user_data,
157 response_callback = data_callback;
158 response_callback_user_data = user_data;
159 return kSuccess;
160 }));
162 SendPlatformMessage,
163 ([&response_callback, &response_callback_user_data](
165 EXPECT_STREQ(message->channel, "test");
166 g_autofree gchar* text =
167 g_strndup(reinterpret_cast<const gchar*>(message->message),
168 message->message_size);
169 EXPECT_STREQ(text, "Hello World!");
170
171 response_callback(nullptr, 0, response_callback_user_data);
172
173 return kSuccess;
174 }));
175
176 g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
177 const char* text = "Hello World!";
178 g_autoptr(GBytes) message = g_bytes_new(text, strlen(text));
180 messenger, "test", message, nullptr,
181 [](GObject* object, GAsyncResult* result, gpointer user_data) {
182 g_autoptr(GError) error = nullptr;
184 FL_BINARY_MESSENGER(object), result, &error);
185 EXPECT_NE(message, nullptr);
186 EXPECT_EQ(error, nullptr);
187
188 EXPECT_EQ(g_bytes_get_size(message), static_cast<gsize>(0));
189
190 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
191 },
192 loop);
193
194 g_main_loop_run(loop);
195}
196
197// Checks the engine reporting a send failure is handled.
198TEST(FlBinaryMessengerTest, SendFailure) {
199 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
200
201 g_autoptr(FlDartProject) project = fl_dart_project_new();
202 g_autoptr(FlEngine) engine = fl_engine_new(project);
203
204 g_autoptr(GError) error = nullptr;
205 EXPECT_TRUE(fl_engine_start(engine, &error));
206 EXPECT_EQ(error, nullptr);
207
209 MOCK_ENGINE_PROC(SendPlatformMessage,
210 ([](auto engine, const FlutterPlatformMessage* message) {
211 EXPECT_STREQ(message->channel, "test");
213 }));
214
215 g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
217 messenger, "test", nullptr, nullptr,
218 [](GObject* object, GAsyncResult* result, gpointer user_data) {
219 g_autoptr(GError) error = nullptr;
221 FL_BINARY_MESSENGER(object), result, &error);
222 EXPECT_EQ(message, nullptr);
223 EXPECT_NE(error, nullptr);
224 EXPECT_STREQ(error->message, "Failed to send platform messages");
225
226 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
227 },
228 loop);
229
230 g_main_loop_run(loop);
231}
232
233// Checks can receive a message.
234TEST(FlBinaryMessengerTest, Receive) {
235 g_autoptr(FlDartProject) project = fl_dart_project_new();
236 g_autoptr(FlEngine) engine = fl_engine_new(project);
237
238 g_autoptr(GError) error = nullptr;
239 EXPECT_TRUE(fl_engine_start(engine, &error));
240 EXPECT_EQ(error, nullptr);
241
242 bool called = false;
245 SendPlatformMessageResponse,
246 ([&called](auto engine,
248 const uint8_t* data, size_t data_length) {
249 called = true;
250
251 int fake_handle = *reinterpret_cast<const int*>(handle);
252 EXPECT_EQ(fake_handle, 42);
253
254 g_autofree gchar* text =
255 g_strndup(reinterpret_cast<const gchar*>(data), data_length);
256 EXPECT_STREQ(text, "Polo!");
257
258 return kSuccess;
259 }));
260
261 FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(engine);
262
263 // Listen for message.
265 messenger, "test",
266 [](FlBinaryMessenger* messenger, const gchar* channel, GBytes* message,
267 FlBinaryMessengerResponseHandle* response_handle, gpointer user_data) {
268 g_autofree gchar* text = g_strndup(
269 static_cast<const gchar*>(g_bytes_get_data(message, nullptr)),
270 g_bytes_get_size(message));
271 EXPECT_STREQ(text, "Marco!");
272
273 const char* response_text = "Polo!";
274 g_autoptr(GBytes) response =
275 g_bytes_new(response_text, strlen(response_text));
276 g_autoptr(GError) error = nullptr;
278 messenger, response_handle, response, &error));
279 EXPECT_EQ(error, nullptr);
280 },
281 nullptr, nullptr);
282
283 // Send message from engine.
284 const char* message_text = "Marco!";
285 g_autoptr(GBytes) message = g_bytes_new(message_text, strlen(message_text));
286 int fake_handle = 42;
288 messenger, "test", message,
289 reinterpret_cast<const FlutterPlatformMessageResponseHandle*>(
290 &fake_handle));
291
292 EXPECT_TRUE(called);
293}
294
295// Checks receieved messages can be responded to on a thread.
296TEST(FlBinaryMessengerTest, ReceiveRespondThread) {
297 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
298
299 g_autoptr(FlDartProject) project = fl_dart_project_new();
300 g_autoptr(FlEngine) engine = fl_engine_new(project);
301
302 g_autoptr(GError) error = nullptr;
303 EXPECT_TRUE(fl_engine_start(engine, &error));
304 EXPECT_EQ(error, nullptr);
305
308 SendPlatformMessageResponse,
309 ([&loop](auto engine,
311 const uint8_t* data, size_t data_length) {
312 int fake_handle = *reinterpret_cast<const int*>(handle);
313 EXPECT_EQ(fake_handle, 42);
314
315 g_autofree gchar* text =
316 g_strndup(reinterpret_cast<const gchar*>(data), data_length);
317 EXPECT_STREQ(text, "Polo!");
318
319 g_main_loop_quit(loop);
320
321 return kSuccess;
322 }));
323
324 FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(engine);
325
326 // Listen for message.
328 messenger, "test",
329 [](FlBinaryMessenger* messenger, const gchar* channel, GBytes* message,
330 FlBinaryMessengerResponseHandle* response_handle, gpointer user_data) {
331 g_autofree gchar* text = g_strndup(
332 static_cast<const gchar*>(g_bytes_get_data(message, nullptr)),
333 g_bytes_get_size(message));
334 EXPECT_STREQ(text, "Marco!");
335
336 // Respond on a thread.
337 typedef struct {
338 FlBinaryMessenger* messenger;
339 FlBinaryMessengerResponseHandle* response_handle;
340 } ThreadData;
341 ThreadData* data = g_new0(ThreadData, 1);
342 data->messenger =
343 static_cast<FlBinaryMessenger*>(g_object_ref(messenger));
344 data->response_handle = static_cast<FlBinaryMessengerResponseHandle*>(
345 g_object_ref(response_handle));
346 g_autoptr(GThread) thread = g_thread_new(
347 nullptr,
348 [](gpointer user_data) {
349 g_autofree ThreadData* data = static_cast<ThreadData*>(user_data);
350 g_autoptr(FlBinaryMessenger) messenger = data->messenger;
351 g_autoptr(FlBinaryMessengerResponseHandle) response_handle =
352 data->response_handle;
353
354 const char* response_text = "Polo!";
355 g_autoptr(GBytes) response =
356 g_bytes_new(response_text, strlen(response_text));
357 g_autoptr(GError) error = nullptr;
359 data->messenger, data->response_handle, response, &error));
360 EXPECT_EQ(error, nullptr);
361
362 return static_cast<gpointer>(nullptr);
363 },
364 data);
365 },
366 nullptr, nullptr);
367
368 // Send message from engine.
369 const char* message_text = "Marco!";
370 g_autoptr(GBytes) message = g_bytes_new(message_text, strlen(message_text));
371 int fake_handle = 42;
373 messenger, "test", message,
374 reinterpret_cast<const FlutterPlatformMessageResponseHandle*>(
375 &fake_handle));
376
377 g_main_loop_run(loop);
378}
379
380// MOCK_ENGINE_PROC is leaky by design.
381// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
382
383// Checks if the 'resize' command is sent and is well-formed.
384TEST(FlBinaryMessengerTest, ResizeChannel) {
385 g_autoptr(FlDartProject) project = fl_dart_project_new();
386 g_autoptr(FlEngine) engine = fl_engine_new(project);
387
388 bool called = false;
389
393 SendPlatformMessage,
394 ([&called, old_handler](auto engine,
396 // Expect to receive a message on the "control" channel.
397 if (strcmp(message->channel, "dev.flutter/channel-buffers") != 0) {
398 return old_handler(engine, message);
399 }
400
401 called = true;
402
403 // The expected content was created from the following Dart code:
404 // MethodCall call = MethodCall('resize', ['flutter/test',3]);
405 // StandardMethodCodec().encodeMethodCall(call).buffer.asUint8List();
406 const int expected_message_size = 29;
407 EXPECT_EQ(message->message_size,
408 static_cast<size_t>(expected_message_size));
409 int expected[expected_message_size] = {
410 7, 6, 114, 101, 115, 105, 122, 101, 12, 2,
411 7, 12, 102, 108, 117, 116, 116, 101, 114, 47,
412 116, 101, 115, 116, 3, 3, 0, 0, 0};
413 for (size_t i = 0; i < expected_message_size; i++) {
414 EXPECT_EQ(message->message[i], expected[i]);
415 }
416
417 return kSuccess;
418 }));
419
420 g_autoptr(GError) error = nullptr;
421 EXPECT_TRUE(fl_engine_start(engine, &error));
422 EXPECT_EQ(error, nullptr);
423
424 g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
425 fl_binary_messenger_resize_channel(messenger, "flutter/test", 3);
426
427 EXPECT_TRUE(called);
428}
429
430// Checks if the 'overflow' command is sent and is well-formed.
431TEST(FlBinaryMessengerTest, WarnsOnOverflowChannel) {
432 g_autoptr(FlDartProject) project = fl_dart_project_new();
433 g_autoptr(FlEngine) engine = fl_engine_new(project);
434
435 bool called = false;
436
440 SendPlatformMessage,
441 ([&called, old_handler](auto engine,
443 // Expect to receive a message on the "control" channel.
444 if (strcmp(message->channel, "dev.flutter/channel-buffers") != 0) {
445 return old_handler(engine, message);
446 }
447
448 called = true;
449
450 // The expected content was created from the following Dart code:
451 // MethodCall call = MethodCall('overflow',['flutter/test', true]);
452 // StandardMethodCodec().encodeMethodCall(call).buffer.asUint8List();
453 const int expected_message_size = 27;
454 EXPECT_EQ(message->message_size,
455 static_cast<size_t>(expected_message_size));
456 int expected[expected_message_size] = {
457 7, 8, 111, 118, 101, 114, 102, 108, 111, 119, 12, 2, 7, 12,
458 102, 108, 117, 116, 116, 101, 114, 47, 116, 101, 115, 116, 1};
459 for (size_t i = 0; i < expected_message_size; i++) {
460 EXPECT_EQ(message->message[i], expected[i]);
461 }
462
463 return kSuccess;
464 }));
465
466 g_autoptr(GError) error = nullptr;
467 EXPECT_TRUE(fl_engine_start(engine, &error));
468 EXPECT_EQ(error, nullptr);
469
470 g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
472 false);
473
474 EXPECT_TRUE(called);
475}
476
477// Checks if error returned when invoking a command on the control channel
478// are handled.
479TEST(FlBinaryMessengerTest, ControlChannelErrorResponse) {
480 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
481
482 g_autoptr(FlDartProject) project = fl_dart_project_new();
483 g_autoptr(FlEngine) engine = fl_engine_new(project);
484
485 g_autoptr(GError) error = nullptr;
486 EXPECT_TRUE(fl_engine_start(engine, &error));
487 EXPECT_EQ(error, nullptr);
488
489 g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
490 bool called = false;
494 SendPlatformMessage,
495 ([&called, old_handler, loop](auto engine,
497 // Expect to receive a message on the "control" channel.
498 if (strcmp(message->channel, "dev.flutter/channel-buffers") != 0) {
499 return old_handler(engine, message);
500 }
501
502 called = true;
503
504 // Register a callback to quit the main loop when binary messenger work
505 // ends.
506 g_idle_add(
507 [](gpointer user_data) {
508 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
509 return FALSE;
510 },
511 loop);
512
513 // Simulates an internal error.
514 return kInvalidArguments;
515 }));
516
518 false);
519
520 EXPECT_TRUE(called);
521
522 g_main_loop_run(loop);
523}
524
525// NOLINTEND(clang-analyzer-core.StackAddressEscape)
526
527TEST(FlBinaryMessengerTest, DeletingEngineClearsHandlers) {
528 g_autoptr(FlDartProject) project = fl_dart_project_new();
529 g_autoptr(FlEngine) engine = fl_engine_new(project);
530
531 g_autoptr(GError) error = nullptr;
532 EXPECT_TRUE(fl_engine_start(engine, &error));
533 EXPECT_EQ(error, nullptr);
534
535 FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(engine);
536
537 // Add handler to check the destroy_notify is called.
538 gboolean destroy_notify_called = FALSE;
540 messenger, "test",
541 [](FlBinaryMessenger* messenger, const gchar* channel, GBytes* message,
542 FlBinaryMessengerResponseHandle* response_handle,
543 gpointer user_data) {},
544 &destroy_notify_called,
545 [](gpointer user_data) { *static_cast<gboolean*>(user_data) = TRUE; });
546
547 g_clear_object(&engine);
548
549 ASSERT_TRUE(destroy_notify_called);
550}
void(* FlutterDataCallback)(const uint8_t *, size_t, void *)
Definition embedder.h:1453
FlutterEngineResult(* FlutterEngineSendPlatformMessageFnPtr)(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage *message)
Definition embedder.h:3606
@ kInternalInconsistency
Definition embedder.h:76
@ kInvalidArguments
Definition embedder.h:75
@ kSuccess
Definition embedder.h:73
FlutterEngine engine
Definition main.cc:84
G_MODULE_EXPORT GBytes * fl_binary_messenger_send_on_channel_finish(FlBinaryMessenger *self, GAsyncResult *result, GError **error)
G_MODULE_EXPORT void fl_binary_messenger_resize_channel(FlBinaryMessenger *self, const gchar *channel, int64_t new_size)
G_MODULE_EXPORT void fl_binary_messenger_set_message_handler_on_channel(FlBinaryMessenger *self, const gchar *channel, FlBinaryMessengerMessageHandler handler, gpointer user_data, GDestroyNotify destroy_notify)
G_MODULE_EXPORT void fl_binary_messenger_send_on_channel(FlBinaryMessenger *self, const gchar *channel, GBytes *message, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
FlBinaryMessenger * fl_binary_messenger_new(FlEngine *engine)
G_MODULE_EXPORT gboolean fl_binary_messenger_send_response(FlBinaryMessenger *self, FlBinaryMessengerResponseHandle *response_handle, GBytes *response, GError **error)
G_MODULE_EXPORT void fl_binary_messenger_set_warns_on_channel_overflow(FlBinaryMessenger *self, const gchar *channel, bool warns)
gboolean fl_binary_messenger_handle_message(FlBinaryMessenger *messenger, const gchar *channel, GBytes *message, const FlutterPlatformMessageResponseHandle *response_handle)
TEST(FlBinaryMessengerTest, Send)
g_autoptr(GMutexLocker) locker
return TRUE
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
G_MODULE_EXPORT FlEngine * fl_engine_new(FlDartProject *project)
Definition fl_engine.cc:697
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition fl_engine.cc:868
G_MODULE_EXPORT FlBinaryMessenger * fl_engine_get_binary_messenger(FlEngine *self)
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition fl_engine.cc:726
const gchar * channel
G_BEGIN_DECLS GBytes * message
const uint8_t uint32_t uint32_t GError ** error
std::u16string text
#define MOCK_ENGINE_PROC(proc, mock_impl)
FlutterEngineSendPlatformMessageFnPtr SendPlatformMessage
Definition embedder.h:3718
FlutterEnginePlatformMessageCreateResponseHandleFnPtr PlatformMessageCreateResponseHandle
Definition embedder.h:3720
FlutterEngineSendPlatformMessageResponseFnPtr SendPlatformMessageResponse
Definition embedder.h:3723
std::shared_ptr< const fml::Mapping > data