Flutter Engine
fl_method_channel_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 "flutter/shell/platform/linux/fl_binary_messenger_private.h"
9 #include "flutter/shell/platform/linux/fl_engine_private.h"
10 #include "flutter/shell/platform/linux/fl_method_codec_private.h"
11 #include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h"
12 #include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
13 #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
14 #include "flutter/shell/platform/linux/testing/fl_test.h"
15 #include "flutter/shell/platform/linux/testing/mock_renderer.h"
16 
17 // Called when when the method call response is received in the InvokeMethod
18 // test.
19 static void method_response_cb(GObject* object,
20  GAsyncResult* result,
21  gpointer user_data) {
22  g_autoptr(GError) error = nullptr;
23  g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
24  FL_METHOD_CHANNEL(object), result, &error);
25  EXPECT_NE(response, nullptr);
26  EXPECT_EQ(error, nullptr);
27 
29  EXPECT_NE(r, nullptr);
30  EXPECT_EQ(error, nullptr);
31 
33  EXPECT_STREQ(fl_value_get_string(r), "Hello World!");
34 
35  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
36 }
37 
38 // Checks if invoking a method returns a value.
39 TEST(FlMethodChannelTest, InvokeMethod) {
40  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
41 
42  g_autoptr(FlEngine) engine = make_mock_engine();
43  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
44  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
45  g_autoptr(FlMethodChannel) channel = fl_method_channel_new(
46  messenger, "test/standard-method", FL_METHOD_CODEC(codec));
47 
48  g_autoptr(FlValue) args = fl_value_new_string("Hello World!");
49  fl_method_channel_invoke_method(channel, "Echo", args, nullptr,
50  method_response_cb, loop);
51 
52  // Blocks here until method_response_cb is called.
53  g_main_loop_run(loop);
54 }
55 
56 // Called when when the method call response is received in the
57 // InvokeMethodNullptrArgsMessage test.
58 static void nullptr_args_response_cb(GObject* object,
59  GAsyncResult* result,
60  gpointer user_data) {
61  g_autoptr(GError) error = nullptr;
62  g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
63  FL_METHOD_CHANNEL(object), result, &error);
64  EXPECT_NE(response, nullptr);
65  EXPECT_EQ(error, nullptr);
66 
68  EXPECT_NE(r, nullptr);
69  EXPECT_EQ(error, nullptr);
71 
72  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
73 }
74 
75 // Checks if a method can be invoked with nullptr for arguments.
76 TEST(FlMethodChannelTest, InvokeMethodNullptrArgsMessage) {
77  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
78 
79  g_autoptr(FlEngine) engine = make_mock_engine();
80  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
81  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
82  g_autoptr(FlMethodChannel) channel = fl_method_channel_new(
83  messenger, "test/standard-method", FL_METHOD_CODEC(codec));
84 
85  fl_method_channel_invoke_method(channel, "Echo", nullptr, nullptr,
87 
88  // Blocks here until nullptr_args_response_cb is called.
89  g_main_loop_run(loop);
90 }
91 
92 // Called when when the method call response is received in the
93 // InvokeMethodError test.
94 static void error_response_cb(GObject* object,
95  GAsyncResult* result,
96  gpointer user_data) {
97  g_autoptr(GError) error = nullptr;
98  g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
99  FL_METHOD_CHANNEL(object), result, &error);
100  EXPECT_NE(response, nullptr);
101  EXPECT_EQ(error, nullptr);
102 
103  EXPECT_TRUE(FL_IS_METHOD_ERROR_RESPONSE(response));
104  EXPECT_STREQ(
105  fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(response)),
106  "CODE");
107  EXPECT_STREQ(
108  fl_method_error_response_get_message(FL_METHOD_ERROR_RESPONSE(response)),
109  "MESSAGE");
110  FlValue* details =
111  fl_method_error_response_get_details(FL_METHOD_ERROR_RESPONSE(response));
112  EXPECT_NE(details, nullptr);
113  EXPECT_EQ(fl_value_get_type(details), FL_VALUE_TYPE_STRING);
114  EXPECT_STREQ(fl_value_get_string(details), "DETAILS");
115 
116  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
117 }
118 
119 // Checks if an error response from a method call is handled.
120 TEST(FlMethodChannelTest, InvokeMethodError) {
121  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
122 
123  g_autoptr(FlEngine) engine = make_mock_engine();
124  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
125  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
126  g_autoptr(FlMethodChannel) channel = fl_method_channel_new(
127  messenger, "test/standard-method", FL_METHOD_CODEC(codec));
128 
129  g_autoptr(FlValue) args = fl_value_new_list();
133  fl_method_channel_invoke_method(channel, "Error", args, nullptr,
134  error_response_cb, loop);
135 
136  // Blocks here until error_response_cb is called.
137  g_main_loop_run(loop);
138 }
139 
140 // Called when when the method call response is received in the
141 // InvokeMethodNotImplemented test.
142 static void not_implemented_response_cb(GObject* object,
143  GAsyncResult* result,
144  gpointer user_data) {
145  g_autoptr(GError) error = nullptr;
146  g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
147  FL_METHOD_CHANNEL(object), result, &error);
148  EXPECT_NE(response, nullptr);
149  EXPECT_EQ(error, nullptr);
150 
151  EXPECT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response));
152 
153  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
154 }
155 
156 // Checks if a not implemeneted response from a method call is handled.
157 TEST(FlMethodChannelTest, InvokeMethodNotImplemented) {
158  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
159 
160  g_autoptr(FlEngine) engine = make_mock_engine();
161  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
162  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
163  g_autoptr(FlMethodChannel) channel = fl_method_channel_new(
164  messenger, "test/standard-method", FL_METHOD_CODEC(codec));
165 
166  fl_method_channel_invoke_method(channel, "NotImplemented", nullptr, nullptr,
168 
169  // Blocks here until not_implemented_response_cb is called.
170  g_main_loop_run(loop);
171 }
172 
173 // Called when when the method call response is received in the
174 // InvokeMethodFailure test.
175 static void failure_response_cb(GObject* object,
176  GAsyncResult* result,
177  gpointer user_data) {
178  g_autoptr(GError) error = nullptr;
179  g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
180  FL_METHOD_CHANNEL(object), result, &error);
181  EXPECT_EQ(response, nullptr);
182  EXPECT_NE(error, nullptr);
183 
184  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
185 }
186 
187 // Checks if an engine failure calling a method call is handled.
188 TEST(FlMethodChannelTest, InvokeMethodFailure) {
189  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
190 
191  g_autoptr(FlEngine) engine = make_mock_engine();
192  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
193  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
194  g_autoptr(FlMethodChannel) channel =
195  fl_method_channel_new(messenger, "test/failure", FL_METHOD_CODEC(codec));
196 
197  fl_method_channel_invoke_method(channel, "Echo", nullptr, nullptr,
198  failure_response_cb, loop);
199 
200  // Blocks here until failure_response_cb is called.
201  g_main_loop_run(loop);
202 }
203 
204 // Called when a method call is received from the engine in the
205 // ReceiveMethodCallRespondSuccess test.
206 static void method_call_success_cb(FlMethodChannel* channel,
207  FlMethodCall* method_call,
208  gpointer user_data) {
209  EXPECT_STREQ(fl_method_call_get_name(method_call), "Foo");
210  EXPECT_EQ(fl_value_get_type(fl_method_call_get_args(method_call)),
212  EXPECT_STREQ(fl_value_get_string(fl_method_call_get_args(method_call)),
213  "Marco!");
214 
215  g_autoptr(FlValue) result = fl_value_new_string("Polo!");
216  g_autoptr(GError) error = nullptr;
217  EXPECT_TRUE(fl_method_call_respond_success(method_call, result, &error));
218  EXPECT_EQ(error, nullptr);
219 }
220 
221 // Called when a the test engine notifies us what response we sent in the
222 // ReceiveMethodCallRespondSuccess test.
224  FlBinaryMessenger* messenger,
225  const gchar* channel,
226  GBytes* message,
227  FlBinaryMessengerResponseHandle* response_handle,
228  gpointer user_data) {
229  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
230  g_autoptr(GError) error = nullptr;
231  g_autoptr(FlMethodResponse) response =
232  fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
233  EXPECT_NE(response, nullptr);
234  EXPECT_EQ(error, nullptr);
235 
236  EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
238  FL_METHOD_SUCCESS_RESPONSE(response));
239  EXPECT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_STRING);
240  EXPECT_STREQ(fl_value_get_string(result), "Polo!");
241 
242  fl_binary_messenger_send_response(messenger, response_handle, nullptr,
243  nullptr);
244 
245  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
246 }
247 
248 // Checks the shell able to receive and respond to method calls from the engine.
249 TEST(FlMethodChannelTest, ReceiveMethodCallRespondSuccess) {
250  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
251 
252  g_autoptr(FlEngine) engine = make_mock_engine();
253  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
254  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
255  g_autoptr(FlMethodChannel) channel = fl_method_channel_new(
256  messenger, "test/standard-method", FL_METHOD_CODEC(codec));
258  nullptr, nullptr);
259 
260  // Listen for response from the engine.
262  messenger, "test/responses", method_call_success_response_cb, loop,
263  nullptr);
264 
265  // Trigger the engine to make a method call.
266  g_autoptr(FlValue) args = fl_value_new_list();
267  fl_value_append_take(args, fl_value_new_string("test/standard-method"));
270  fl_method_channel_invoke_method(channel, "InvokeMethod", args, nullptr,
271  nullptr, loop);
272 
273  // Blocks here until method_call_success_response_cb is called.
274  g_main_loop_run(loop);
275 }
276 
277 // Called when a method call is received from the engine in the
278 // ReceiveMethodCallRespondError test.
279 static void method_call_error_cb(FlMethodChannel* channel,
280  FlMethodCall* method_call,
281  gpointer user_data) {
282  EXPECT_STREQ(fl_method_call_get_name(method_call), "Foo");
283  EXPECT_EQ(fl_value_get_type(fl_method_call_get_args(method_call)),
285  EXPECT_STREQ(fl_value_get_string(fl_method_call_get_args(method_call)),
286  "Marco!");
287 
288  g_autoptr(FlValue) details = fl_value_new_string("DETAILS");
289  g_autoptr(GError) error = nullptr;
290  EXPECT_TRUE(fl_method_call_respond_error(method_call, "CODE", "MESSAGE",
291  details, &error));
292  EXPECT_EQ(error, nullptr);
293 }
294 
295 // Called when a the test engine notifies us what response we sent in the
296 // ReceiveMethodCallRespondError test.
298  FlBinaryMessenger* messenger,
299  const gchar* channel,
300  GBytes* message,
301  FlBinaryMessengerResponseHandle* response_handle,
302  gpointer user_data) {
303  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
304  g_autoptr(GError) error = nullptr;
305  g_autoptr(FlMethodResponse) response =
306  fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
307  EXPECT_NE(response, nullptr);
308  EXPECT_EQ(error, nullptr);
309 
310  EXPECT_TRUE(FL_IS_METHOD_ERROR_RESPONSE(response));
311  EXPECT_STREQ(
312  fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(response)),
313  "CODE");
314  EXPECT_STREQ(
315  fl_method_error_response_get_message(FL_METHOD_ERROR_RESPONSE(response)),
316  "MESSAGE");
317  FlValue* details =
318  fl_method_error_response_get_details(FL_METHOD_ERROR_RESPONSE(response));
319  EXPECT_EQ(fl_value_get_type(details), FL_VALUE_TYPE_STRING);
320  EXPECT_STREQ(fl_value_get_string(details), "DETAILS");
321 
322  fl_binary_messenger_send_response(messenger, response_handle, nullptr,
323  nullptr);
324 
325  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
326 }
327 
328 // Checks the shell able to receive and respond to method calls from the engine.
329 TEST(FlMethodChannelTest, ReceiveMethodCallRespondError) {
330  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
331 
332  g_autoptr(FlEngine) engine = make_mock_engine();
333  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
334  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
335  g_autoptr(FlMethodChannel) channel = fl_method_channel_new(
336  messenger, "test/standard-method", FL_METHOD_CODEC(codec));
338  nullptr, nullptr);
339 
340  // Listen for response from the engine.
342  messenger, "test/responses", method_call_error_response_cb, loop,
343  nullptr);
344 
345  // Trigger the engine to make a method call.
346  g_autoptr(FlValue) args = fl_value_new_list();
347  fl_value_append_take(args, fl_value_new_string("test/standard-method"));
350  fl_method_channel_invoke_method(channel, "InvokeMethod", args, nullptr,
351  nullptr, loop);
352 
353  // Blocks here until method_call_error_response_cb is called.
354  g_main_loop_run(loop);
355 }
356 
357 // Called when a method call is received from the engine in the
358 // ReceiveMethodCallRespondNotImplemented test.
359 static void method_call_not_implemented_cb(FlMethodChannel* channel,
360  FlMethodCall* method_call,
361  gpointer user_data) {
362  EXPECT_STREQ(fl_method_call_get_name(method_call), "Foo");
363  EXPECT_EQ(fl_value_get_type(fl_method_call_get_args(method_call)),
365  EXPECT_STREQ(fl_value_get_string(fl_method_call_get_args(method_call)),
366  "Marco!");
367 
368  g_autoptr(GError) error = nullptr;
369  EXPECT_TRUE(fl_method_call_respond_not_implemented(method_call, &error));
370  EXPECT_EQ(error, nullptr);
371 }
372 
373 // Called when a the test engine notifies us what response we sent in the
374 // ReceiveMethodCallRespondNotImplemented test.
376  FlBinaryMessenger* messenger,
377  const gchar* channel,
378  GBytes* message,
379  FlBinaryMessengerResponseHandle* response_handle,
380  gpointer user_data) {
381  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
382  g_autoptr(GError) error = nullptr;
383  g_autoptr(FlMethodResponse) response =
384  fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
385  EXPECT_NE(response, nullptr);
386  EXPECT_EQ(error, nullptr);
387 
388  EXPECT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response));
389 
390  fl_binary_messenger_send_response(messenger, response_handle, nullptr,
391  nullptr);
392 
393  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
394 }
395 
396 // Checks the shell able to receive and respond to method calls from the engine.
397 TEST(FlMethodChannelTest, ReceiveMethodCallRespondNotImplemented) {
398  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
399 
400  g_autoptr(FlEngine) engine = make_mock_engine();
401  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
402  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
403  g_autoptr(FlMethodChannel) channel = fl_method_channel_new(
404  messenger, "test/standard-method", FL_METHOD_CODEC(codec));
406  channel, method_call_not_implemented_cb, nullptr, nullptr);
407 
408  // Listen for response from the engine.
410  messenger, "test/responses", method_call_not_implemented_response_cb,
411  loop, nullptr);
412 
413  // Trigger the engine to make a method call.
414  g_autoptr(FlValue) args = fl_value_new_list();
415  fl_value_append_take(args, fl_value_new_string("test/standard-method"));
418  fl_method_channel_invoke_method(channel, "InvokeMethod", args, nullptr,
419  nullptr, loop);
420 
421  // Blocks here until method_call_not_implemented_response_cb is called.
422  g_main_loop_run(loop);
423 }
424 
425 // A test method codec that always generates errors on responses.
426 G_DECLARE_FINAL_TYPE(TestMethodCodec,
427  test_method_codec,
428  TEST,
429  METHOD_CODEC,
430  FlMethodCodec)
431 
432 struct _TestMethodCodec {
433  FlMethodCodec parent_instance;
434 
435  FlStandardMethodCodec* wrapped_codec;
436 };
437 
438 G_DEFINE_TYPE(TestMethodCodec, test_method_codec, fl_method_codec_get_type())
439 
440 static void test_method_codec_dispose(GObject* object) {
441  TestMethodCodec* self = TEST_METHOD_CODEC(object);
442 
443  g_clear_object(&self->wrapped_codec);
444 
445  G_OBJECT_CLASS(test_method_codec_parent_class)->dispose(object);
446 }
447 
448 // Implements FlMethodCodec::encode_method_call.
449 static GBytes* test_method_codec_encode_method_call(FlMethodCodec* codec,
450  const gchar* name,
451  FlValue* args,
452  GError** error) {
453  EXPECT_TRUE(TEST_IS_METHOD_CODEC(codec));
454  TestMethodCodec* self = TEST_METHOD_CODEC(codec);
456  FL_METHOD_CODEC(self->wrapped_codec), name, args, error);
457 }
458 
459 // Implements FlMethodCodec::decode_method_call.
460 static gboolean test_method_codec_decode_method_call(FlMethodCodec* codec,
461  GBytes* message,
462  gchar** name,
463  FlValue** args,
464  GError** error) {
465  EXPECT_TRUE(TEST_IS_METHOD_CODEC(codec));
466  TestMethodCodec* self = TEST_METHOD_CODEC(codec);
468  FL_METHOD_CODEC(self->wrapped_codec), message, name, args, error);
469 }
470 
471 // Implements FlMethodCodec::encode_success_envelope.
472 static GBytes* test_method_codec_encode_success_envelope(FlMethodCodec* codec,
473  FlValue* result,
474  GError** error) {
476  "Unsupported type");
477  return nullptr;
478 }
479 
480 // Implements FlMethodCodec::encode_error_envelope.
481 static GBytes* test_method_codec_encode_error_envelope(FlMethodCodec* codec,
482  const gchar* code,
483  const gchar* message,
484  FlValue* details,
485  GError** error) {
487  "Unsupported type");
488  return nullptr;
489 }
490 
491 // Implements FlMethodCodec::encode_decode_response.
492 static FlMethodResponse* test_method_codec_decode_response(FlMethodCodec* codec,
493  GBytes* message,
494  GError** error) {
495  EXPECT_TRUE(TEST_IS_METHOD_CODEC(codec));
496  TestMethodCodec* self = TEST_METHOD_CODEC(codec);
497  return fl_method_codec_decode_response(FL_METHOD_CODEC(self->wrapped_codec),
498  message, error);
499 }
500 
501 static void test_method_codec_class_init(TestMethodCodecClass* klass) {
502  G_OBJECT_CLASS(klass)->dispose = test_method_codec_dispose;
503  FL_METHOD_CODEC_CLASS(klass)->encode_method_call =
505  FL_METHOD_CODEC_CLASS(klass)->decode_method_call =
507  FL_METHOD_CODEC_CLASS(klass)->encode_success_envelope =
509  FL_METHOD_CODEC_CLASS(klass)->encode_error_envelope =
511  FL_METHOD_CODEC_CLASS(klass)->decode_response =
513 }
514 
515 static void test_method_codec_init(TestMethodCodec* self) {
516  self->wrapped_codec = fl_standard_method_codec_new();
517 }
518 
519 TestMethodCodec* test_method_codec_new() {
520  return TEST_METHOD_CODEC(g_object_new(test_method_codec_get_type(), nullptr));
521 }
522 
523 // Called when a method call is received from the engine in the
524 // ReceiveMethodCallRespondSuccessError test.
525 static void method_call_success_error_cb(FlMethodChannel* channel,
526  FlMethodCall* method_call,
527  gpointer user_data) {
528  g_autoptr(FlValue) result = fl_value_new_int(42);
529  g_autoptr(GError) response_error = nullptr;
530  EXPECT_FALSE(
531  fl_method_call_respond_success(method_call, result, &response_error));
532  EXPECT_NE(response_error, nullptr);
533 
534  // Respond to stop a warning occurring about not responding.
535  fl_method_call_respond_not_implemented(method_call, nullptr);
536 
537  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
538 }
539 
540 // Checks error correctly handled if provide an unsupported arg in a method call
541 // response.
542 TEST(FlMethodChannelTest, ReceiveMethodCallRespondSuccessError) {
543  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
544 
545  g_autoptr(FlEngine) engine = make_mock_engine();
546  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
547  g_autoptr(TestMethodCodec) codec = test_method_codec_new();
548  g_autoptr(FlMethodChannel) channel = fl_method_channel_new(
549  messenger, "test/standard-method", FL_METHOD_CODEC(codec));
551  channel, method_call_success_error_cb, loop, nullptr);
552 
553  // Trigger the engine to make a method call.
554  g_autoptr(FlValue) args = fl_value_new_list();
555  fl_value_append_take(args, fl_value_new_string("test/standard-method"));
558  fl_method_channel_invoke_method(channel, "InvokeMethod", args, nullptr,
559  nullptr, loop);
560 
561  // Blocks here until method_call_success_error_cb is called.
562  g_main_loop_run(loop);
563 }
564 
565 // Called when a method call is received from the engine in the
566 // ReceiveMethodCallRespondErrorError test.
567 static void method_call_error_error_cb(FlMethodChannel* channel,
568  FlMethodCall* method_call,
569  gpointer user_data) {
570  g_autoptr(FlValue) details = fl_value_new_int(42);
571  g_autoptr(GError) response_error = nullptr;
572  EXPECT_FALSE(fl_method_call_respond_error(method_call, "error", "ERROR",
573  details, &response_error));
574  EXPECT_NE(response_error, nullptr);
575 
576  // Respond to stop a warning occurring about not responding.
577  fl_method_call_respond_not_implemented(method_call, nullptr);
578 
579  g_main_loop_quit(static_cast<GMainLoop*>(user_data));
580 }
581 
582 // Checks error correctly handled if provide an unsupported arg in a method call
583 // response.
584 TEST(FlMethodChannelTest, ReceiveMethodCallRespondErrorError) {
585  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
586 
587  g_autoptr(FlEngine) engine = make_mock_engine();
588  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
589  g_autoptr(TestMethodCodec) codec = test_method_codec_new();
590  g_autoptr(FlMethodChannel) channel = fl_method_channel_new(
591  messenger, "test/standard-method", FL_METHOD_CODEC(codec));
593  loop, nullptr);
594 
595  // Trigger the engine to make a method call.
596  g_autoptr(FlValue) args = fl_value_new_list();
597  fl_value_append_take(args, fl_value_new_string("test/standard-method"));
600  fl_method_channel_invoke_method(channel, "InvokeMethod", args, nullptr,
601  nullptr, loop);
602 
603  // Blocks here until method_call_error_error_cb is called.
604  g_main_loop_run(loop);
605 }
606 
608  GMainLoop* loop;
609  int count;
610 };
611 
612 // This callback parses the user data as UserDataReassignMethod,
613 // increases its `count`, and quits `loop`.
614 static void reassign_method_cb(FlMethodChannel* channel,
615  FlMethodCall* method_call,
616  gpointer raw_user_data) {
618  static_cast<UserDataReassignMethod*>(raw_user_data);
619  user_data->count += 1;
620 
621  g_autoptr(FlValue) result = fl_value_new_string("Polo!");
622  g_autoptr(GError) error = nullptr;
623  EXPECT_TRUE(fl_method_call_respond_success(method_call, result, &error));
624  EXPECT_EQ(error, nullptr);
625 
626  g_main_loop_quit(user_data->loop);
627 }
628 
629 // Make sure that the following steps will work properly:
630 //
631 // 1. Register a method channel.
632 // 2. Dispose the method channel, and it's unregistered.
633 // 3. Register a new channel with the same name.
634 //
635 // This is a regression test to https://github.com/flutter/flutter/issues/90817.
636 TEST(FlMethodChannelTest, ReplaceADisposedMethodChannel) {
637  const char* method_name = "test/standard-method";
638  // The loop is used to pause the main process until the callback is fully
639  // executed.
640  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
641  g_autoptr(FlEngine) engine = make_mock_engine();
642  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
643  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
644 
645  g_autoptr(FlValue) args = fl_value_new_list();
649 
650  // Register the first channel and test if it works.
651  UserDataReassignMethod user_data1{
652  .loop = loop,
653  .count = 100,
654  };
655  FlMethodChannel* channel1 =
656  fl_method_channel_new(messenger, method_name, FL_METHOD_CODEC(codec));
658  &user_data1, nullptr);
659 
660  fl_method_channel_invoke_method(channel1, "InvokeMethod", args, nullptr,
661  nullptr, nullptr);
662  g_main_loop_run(loop);
663  EXPECT_EQ(user_data1.count, 101);
664 
665  // Dispose the first channel.
666  g_object_unref(channel1);
667 
668  // Register the second channel and test if it works.
669  UserDataReassignMethod user_data2{
670  .loop = loop,
671  .count = 100,
672  };
673  g_autoptr(FlMethodChannel) channel2 =
674  fl_method_channel_new(messenger, method_name, FL_METHOD_CODEC(codec));
676  &user_data2, nullptr);
677 
678  fl_method_channel_invoke_method(channel2, "InvokeMethod", args, nullptr,
679  nullptr, nullptr);
680  g_main_loop_run(loop);
681 
682  EXPECT_EQ(user_data1.count, 101);
683  EXPECT_EQ(user_data2.count, 101);
684 }
685 
686 // Make sure that the following steps will work properly:
687 //
688 // 1. Register a method channel.
689 // 2. Register the same name with a new channel.
690 // 3. Dispose the previous method channel.
691 //
692 // This is a regression test to https://github.com/flutter/flutter/issues/90817.
693 TEST(FlMethodChannelTest, DisposeAReplacedMethodChannel) {
694  const char* method_name = "test/standard-method";
695  // The loop is used to pause the main process until the callback is fully
696  // executed.
697  g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
698  g_autoptr(FlEngine) engine = make_mock_engine();
699  FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
700  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
701 
702  g_autoptr(FlValue) args = fl_value_new_list();
706 
707  // Register the first channel and test if it works.
708  UserDataReassignMethod user_data1{
709  .loop = loop,
710  .count = 100,
711  };
712  FlMethodChannel* channel1 =
713  fl_method_channel_new(messenger, method_name, FL_METHOD_CODEC(codec));
715  &user_data1, nullptr);
716 
717  fl_method_channel_invoke_method(channel1, "InvokeMethod", args, nullptr,
718  nullptr, nullptr);
719  g_main_loop_run(loop);
720  EXPECT_EQ(user_data1.count, 101);
721 
722  // Register a new channel to the same name.
723  UserDataReassignMethod user_data2{
724  .loop = loop,
725  .count = 100,
726  };
727  g_autoptr(FlMethodChannel) channel2 =
728  fl_method_channel_new(messenger, method_name, FL_METHOD_CODEC(codec));
730  &user_data2, nullptr);
731 
732  fl_method_channel_invoke_method(channel2, "InvokeMethod", args, nullptr,
733  nullptr, nullptr);
734  g_main_loop_run(loop);
735  EXPECT_EQ(user_data1.count, 101);
736  EXPECT_EQ(user_data2.count, 101);
737 
738  // Dispose the first channel. The new channel should keep working.
739  g_object_unref(channel1);
740 
741  fl_method_channel_invoke_method(channel2, "InvokeMethod", args, nullptr,
742  nullptr, nullptr);
743  g_main_loop_run(loop);
744  EXPECT_EQ(user_data1.count, 101);
745  EXPECT_EQ(user_data2.count, 102);
746 }
G_MODULE_EXPORT FlValue * fl_value_new_list()
Definition: fl_value.cc:338
G_BEGIN_DECLS FlValue * args
static void error_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
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)
GBytes * fl_method_codec_encode_method_call(FlMethodCodec *self, const gchar *name, FlValue *args, GError **error)
G_BEGIN_DECLS FlMethodCall * method_call
G_MODULE_EXPORT FlMethodResponse * fl_method_channel_invoke_method_finish(FlMethodChannel *self, GAsyncResult *result, GError **error)
const uint8_t uint32_t uint32_t GError ** error
static void method_call_not_implemented_response_cb(FlBinaryMessenger *messenger, const gchar *channel, GBytes *message, FlBinaryMessengerResponseHandle *response_handle, gpointer user_data)
G_MODULE_EXPORT FlValueType fl_value_get_type(FlValue *self)
Definition: fl_value.cc:428
static void test_method_codec_class_init(TestMethodCodecClass *klass)
G_MODULE_EXPORT gboolean fl_method_call_respond_not_implemented(FlMethodCall *self, GError **error)
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:40
G_MODULE_EXPORT gboolean fl_method_call_respond_success(FlMethodCall *self, FlValue *result, GError **error)
void * user_data
static void reassign_method_cb(FlMethodChannel *channel, FlMethodCall *method_call, gpointer raw_user_data)
static void test_method_codec_init(TestMethodCodec *self)
static void failure_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
G_MODULE_EXPORT FlValue * fl_method_success_response_get_result(FlMethodSuccessResponse *self)
static void method_call_success_error_cb(FlMethodChannel *channel, FlMethodCall *method_call, gpointer user_data)
GAsyncResult * result
#define FL_MESSAGE_CODEC_ERROR
G_MODULE_EXPORT const gchar * fl_method_error_response_get_message(FlMethodErrorResponse *self)
static void method_call_success_response_cb(FlBinaryMessenger *messenger, const gchar *channel, GBytes *message, FlBinaryMessengerResponseHandle *response_handle, gpointer user_data)
G_MODULE_EXPORT FlValue * fl_method_call_get_args(FlMethodCall *self)
static void method_call_error_cb(FlMethodChannel *channel, FlMethodCall *method_call, gpointer user_data)
TestMethodCodec * test_method_codec_new()
static FlEngine * make_mock_engine()
G_MODULE_EXPORT gboolean fl_binary_messenger_send_response(FlBinaryMessenger *self, FlBinaryMessengerResponseHandle *response_handle, GBytes *response, GError **error)
G_MODULE_EXPORT FlStandardMethodCodec * fl_standard_method_codec_new()
G_MODULE_EXPORT void fl_value_append_take(FlValue *self, FlValue *value)
Definition: fl_value.cc:560
static void method_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
static void not_implemented_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
G_MODULE_EXPORT void fl_method_channel_set_method_call_handler(FlMethodChannel *self, FlMethodChannelMethodCallHandler handler, gpointer user_data, GDestroyNotify destroy_notify)
G_MODULE_EXPORT const gchar * fl_method_call_get_name(FlMethodCall *self)
G_MODULE_EXPORT FlMethodChannel * fl_method_channel_new(FlBinaryMessenger *messenger, const gchar *name, FlMethodCodec *codec)
static gboolean test_method_codec_decode_method_call(FlMethodCodec *codec, GBytes *message, gchar **name, FlValue **args, GError **error)
static void method_call_success_cb(FlMethodChannel *channel, FlMethodCall *method_call, gpointer user_data)
G_MODULE_EXPORT gboolean fl_method_call_respond_error(FlMethodCall *self, const gchar *code, const gchar *message, FlValue *details, GError **error)
static void method_call_error_error_cb(FlMethodChannel *channel, FlMethodCall *method_call, gpointer user_data)
FlMethodResponse * fl_method_codec_decode_response(FlMethodCodec *self, GBytes *message, GError **error)
static GBytes * test_method_codec_encode_method_call(FlMethodCodec *codec, const gchar *name, FlValue *args, GError **error)
G_MODULE_EXPORT FlValue * fl_value_new_int(int64_t value)
Definition: fl_value.cc:251
TEST(FlMethodChannelTest, InvokeMethod)
G_MODULE_EXPORT FlValue * fl_value_new_string(const gchar *value)
Definition: fl_value.cc:265
G_MODULE_EXPORT void fl_method_channel_invoke_method(FlMethodChannel *self, const gchar *method, FlValue *args, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
const char * name
Definition: fuchsia.cc:50
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
G_MODULE_EXPORT const gchar * fl_value_get_string(FlValue *self)
Definition: fl_value.cc:642
G_MODULE_EXPORT const gchar * fl_method_error_response_get_code(FlMethodErrorResponse *self)
static FlMethodResponse * test_method_codec_decode_response(FlMethodCodec *codec, GBytes *message, GError **error)
static GBytes * test_method_codec_encode_success_envelope(FlMethodCodec *codec, FlValue *result, GError **error)
G_MODULE_EXPORT FlValue * fl_method_response_get_result(FlMethodResponse *self, GError **error)
static void nullptr_args_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
G_MODULE_EXPORT GType fl_method_codec_get_type()
static void test_method_codec_dispose(GObject *object)
static GBytes * test_method_codec_encode_error_envelope(FlMethodCodec *codec, const gchar *code, const gchar *message, FlValue *details, GError **error)
FlBinaryMessenger * fl_binary_messenger_new(FlEngine *engine)
gboolean fl_method_codec_decode_method_call(FlMethodCodec *self, GBytes *message, gchar **name, FlValue **args, GError **error)
G_DECLARE_FINAL_TYPE(TestMethodCodec, test_method_codec, TEST, METHOD_CODEC, FlMethodCodec) struct _TestMethodCodec
G_MODULE_EXPORT FlValue * fl_method_error_response_get_details(FlMethodErrorResponse *self)
static void method_call_error_response_cb(FlBinaryMessenger *messenger, const gchar *channel, GBytes *message, FlBinaryMessengerResponseHandle *response_handle, gpointer user_data)
static void method_call_not_implemented_cb(FlMethodChannel *channel, FlMethodCall *method_call, gpointer user_data)