From 9eb650c1cf71145c5edfd2dafeb42ce2cbf751bf Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 28 Nov 2024 19:22:15 +1300 Subject: [PATCH 1/4] Always check for errors when propagating task values. This could occur if a request is cancelled, without this it might not chain up to the original caller correctly. --- shell/platform/linux/fl_basic_message_channel.cc | 5 ++++- shell/platform/linux/fl_binary_messenger.cc | 5 ++++- shell/platform/linux/fl_method_channel.cc | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/shell/platform/linux/fl_basic_message_channel.cc b/shell/platform/linux/fl_basic_message_channel.cc index 7d2b5afe60a6a..5d23bd9d4984b 100644 --- a/shell/platform/linux/fl_basic_message_channel.cc +++ b/shell/platform/linux/fl_basic_message_channel.cc @@ -258,7 +258,10 @@ G_MODULE_EXPORT FlValue* fl_basic_message_channel_send_finish( g_return_val_if_fail(g_task_is_valid(result, self), nullptr); g_autoptr(GTask) task = G_TASK(result); - GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, nullptr)); + GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, error)); + if (r == nullptr) { + return nullptr; + } g_autoptr(GBytes) message = fl_binary_messenger_send_on_channel_finish(self->messenger, r, error); diff --git a/shell/platform/linux/fl_binary_messenger.cc b/shell/platform/linux/fl_binary_messenger.cc index 16dcd2b86bbc1..9dd08b02ff70c 100644 --- a/shell/platform/linux/fl_binary_messenger.cc +++ b/shell/platform/linux/fl_binary_messenger.cc @@ -291,7 +291,10 @@ static GBytes* send_on_channel_finish(FlBinaryMessenger* messenger, g_return_val_if_fail(g_task_is_valid(result, self), FALSE); g_autoptr(GTask) task = G_TASK(result); - GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, nullptr)); + GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, error)); + if (r == nullptr) { + return nullptr; + } g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); if (engine == nullptr) { diff --git a/shell/platform/linux/fl_method_channel.cc b/shell/platform/linux/fl_method_channel.cc index 266bfa7d812d7..e9f691aecbdb0 100644 --- a/shell/platform/linux/fl_method_channel.cc +++ b/shell/platform/linux/fl_method_channel.cc @@ -197,7 +197,10 @@ G_MODULE_EXPORT FlMethodResponse* fl_method_channel_invoke_method_finish( g_return_val_if_fail(g_task_is_valid(result, self), nullptr); g_autoptr(GTask) task = G_TASK(result); - GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, nullptr)); + GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, error)); + if (r == nullptr) { + return nullptr; + } g_autoptr(GBytes) response = fl_binary_messenger_send_on_channel_finish(self->messenger, r, error); From a252b6471ef66ce457889e9f41c27540e8b1b4e4 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 28 Nov 2024 21:28:36 +1300 Subject: [PATCH 2/4] Fix GTask reference counting Incorrect reference counting of GTask objects meant platform channel method calls would leave tasks alive that would leak memory and leave unclosed references to the binary memssenger. --- shell/platform/linux/fl_basic_message_channel.cc | 9 +++++---- shell/platform/linux/fl_binary_messenger.cc | 9 +++++---- shell/platform/linux/fl_method_channel.cc | 9 +++++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/shell/platform/linux/fl_basic_message_channel.cc b/shell/platform/linux/fl_basic_message_channel.cc index 5d23bd9d4984b..5bb45a413c83a 100644 --- a/shell/platform/linux/fl_basic_message_channel.cc +++ b/shell/platform/linux/fl_basic_message_channel.cc @@ -103,8 +103,8 @@ static void message_cb(FlBinaryMessenger* messenger, static void message_response_cb(GObject* object, GAsyncResult* result, gpointer user_data) { - GTask* task = G_TASK(user_data); - g_task_return_pointer(task, result, g_object_unref); + g_autoptr(GTask) task = G_TASK(user_data); + g_task_return_pointer(task, g_object_ref(result), g_object_unref); } // Called when the channel handler is closed. @@ -257,8 +257,9 @@ G_MODULE_EXPORT FlValue* fl_basic_message_channel_send_finish( g_return_val_if_fail(FL_IS_BASIC_MESSAGE_CHANNEL(self), nullptr); g_return_val_if_fail(g_task_is_valid(result, self), nullptr); - g_autoptr(GTask) task = G_TASK(result); - GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, error)); + GTask* task = G_TASK(result); + g_autoptr(GAsyncResult) r = + G_ASYNC_RESULT(g_task_propagate_pointer(task, error)); if (r == nullptr) { return nullptr; } diff --git a/shell/platform/linux/fl_binary_messenger.cc b/shell/platform/linux/fl_binary_messenger.cc index 9dd08b02ff70c..4453456ec1113 100644 --- a/shell/platform/linux/fl_binary_messenger.cc +++ b/shell/platform/linux/fl_binary_messenger.cc @@ -260,8 +260,8 @@ static gboolean send_response(FlBinaryMessenger* messenger, static void platform_message_ready_cb(GObject* object, GAsyncResult* result, gpointer user_data) { - GTask* task = G_TASK(user_data); - g_task_return_pointer(task, result, g_object_unref); + g_autoptr(GTask) task = G_TASK(user_data); + g_task_return_pointer(task, g_object_ref(result), g_object_unref); } static void send_on_channel(FlBinaryMessenger* messenger, @@ -290,8 +290,9 @@ static GBytes* send_on_channel_finish(FlBinaryMessenger* messenger, FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(messenger); g_return_val_if_fail(g_task_is_valid(result, self), FALSE); - g_autoptr(GTask) task = G_TASK(result); - GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, error)); + GTask* task = G_TASK(result); + g_autoptr(GAsyncResult) r = + G_ASYNC_RESULT(g_task_propagate_pointer(task, error)); if (r == nullptr) { return nullptr; } diff --git a/shell/platform/linux/fl_method_channel.cc b/shell/platform/linux/fl_method_channel.cc index e9f691aecbdb0..1177d63f05a9d 100644 --- a/shell/platform/linux/fl_method_channel.cc +++ b/shell/platform/linux/fl_method_channel.cc @@ -63,8 +63,8 @@ static void message_cb(FlBinaryMessenger* messenger, static void message_response_cb(GObject* object, GAsyncResult* result, gpointer user_data) { - GTask* task = G_TASK(user_data); - g_task_return_pointer(task, result, g_object_unref); + g_autoptr(GTask) task = G_TASK(user_data); + g_task_return_pointer(task, g_object_ref(result), g_object_unref); } // Called when the channel handler is closed. @@ -196,8 +196,9 @@ G_MODULE_EXPORT FlMethodResponse* fl_method_channel_invoke_method_finish( g_return_val_if_fail(FL_IS_METHOD_CHANNEL(self), nullptr); g_return_val_if_fail(g_task_is_valid(result, self), nullptr); - g_autoptr(GTask) task = G_TASK(result); - GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, error)); + GTask* task = G_TASK(result); + g_autoptr(GAsyncResult) r = + G_ASYNC_RESULT(g_task_propagate_pointer(task, error)); if (r == nullptr) { return nullptr; } From 3104059965a707f0fddb10a5d72ec40599c51b52 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 28 Nov 2024 11:13:39 +1300 Subject: [PATCH 3/4] Make a mock messenger that can easily mock channels The previous mock required knowing the specific functions used in the binary messenger, this method instead allows test code to provide complete platform channel implementation for testing and make simulated platform channel calls into embedder code. --- shell/platform/linux/BUILD.gn | 3 +- .../linux/fl_binary_messenger_test.cc | 31 +- .../linux/fl_key_channel_responder.cc | 17 +- .../platform/linux/fl_key_channel_responder.h | 25 +- .../linux/fl_key_channel_responder_test.cc | 196 +- .../linux/fl_keyboard_handler_test.cc | 54 +- shell/platform/linux/fl_method_channel.cc | 31 +- shell/platform/linux/fl_method_codec.cc | 26 + .../platform/linux/fl_method_codec_private.h | 18 +- .../linux/fl_platform_handler_test.cc | 245 ++- .../linux/fl_settings_handler_test.cc | 175 +- .../linux/fl_text_input_handler_test.cc | 1732 ++++++++--------- .../linux/fl_window_state_monitor_test.cc | 235 ++- .../linux/testing/fl_mock_binary_messenger.cc | 609 ++++++ .../linux/testing/fl_mock_binary_messenger.h | 160 ++ .../linux/testing/mock_binary_messenger.cc | 160 -- .../linux/testing/mock_binary_messenger.h | 91 - .../mock_binary_messenger_response_handle.cc | 25 - .../mock_binary_messenger_response_handle.h | 23 - 19 files changed, 2165 insertions(+), 1691 deletions(-) create mode 100644 shell/platform/linux/testing/fl_mock_binary_messenger.cc create mode 100644 shell/platform/linux/testing/fl_mock_binary_messenger.h delete mode 100644 shell/platform/linux/testing/mock_binary_messenger.cc delete mode 100644 shell/platform/linux/testing/mock_binary_messenger.h delete mode 100644 shell/platform/linux/testing/mock_binary_messenger_response_handle.cc delete mode 100644 shell/platform/linux/testing/mock_binary_messenger_response_handle.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 78b0bed8d85e0..5b5dba54fdaee 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -252,8 +252,7 @@ executable("flutter_linux_unittests") { "testing/fl_test.cc", "testing/fl_test_gtk_logs.cc", "testing/fl_test_gtk_logs.h", - "testing/mock_binary_messenger.cc", - "testing/mock_binary_messenger_response_handle.cc", + "testing/fl_mock_binary_messenger.cc", "testing/mock_engine.cc", "testing/mock_epoxy.cc", "testing/mock_im_context.cc", diff --git a/shell/platform/linux/fl_binary_messenger_test.cc b/shell/platform/linux/fl_binary_messenger_test.cc index 133f6e4b57c20..8f03eb514057c 100644 --- a/shell/platform/linux/fl_binary_messenger_test.cc +++ b/shell/platform/linux/fl_binary_messenger_test.cc @@ -16,9 +16,34 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h" #include "flutter/shell/platform/linux/testing/fl_test.h" -#include "flutter/shell/platform/linux/testing/mock_binary_messenger_response_handle.h" #include "flutter/shell/platform/linux/testing/mock_renderer.h" +G_DECLARE_FINAL_TYPE(FlFakeBinaryMessengerResponseHandle, + fl_fake_binary_messenger_response_handle, + FL, + FAKE_BINARY_MESSENGER_RESPONSE_HANDLE, + FlBinaryMessengerResponseHandle) + +struct _FlFakeBinaryMessengerResponseHandle { + FlBinaryMessengerResponseHandle parent_instance; +}; + +G_DEFINE_TYPE(FlFakeBinaryMessengerResponseHandle, + fl_fake_binary_messenger_response_handle, + fl_binary_messenger_response_handle_get_type()); + +static void fl_fake_binary_messenger_response_handle_class_init( + FlFakeBinaryMessengerResponseHandleClass* klass) {} + +static void fl_fake_binary_messenger_response_handle_init( + FlFakeBinaryMessengerResponseHandle* self) {} + +FlFakeBinaryMessengerResponseHandle* +fl_fake_binary_messenger_response_handle_new() { + return FL_FAKE_BINARY_MESSENGER_RESPONSE_HANDLE( + g_object_new(fl_fake_binary_messenger_response_handle_get_type(), NULL)); +} + G_DECLARE_FINAL_TYPE(FlFakeBinaryMessenger, fl_fake_binary_messenger, FL, @@ -55,7 +80,7 @@ static gboolean send_message_cb(gpointer user_data) { g_autoptr(GBytes) message = g_bytes_new(text, strlen(text)); self->message_handler(FL_BINARY_MESSENGER(self), "CHANNEL", message, FL_BINARY_MESSENGER_RESPONSE_HANDLE( - fl_mock_binary_messenger_response_handle_new()), + fl_fake_binary_messenger_response_handle_new()), self->message_handler_user_data); return FALSE; @@ -83,7 +108,7 @@ static gboolean send_response(FlBinaryMessenger* messenger, GError** error) { FlFakeBinaryMessenger* self = FL_FAKE_BINARY_MESSENGER(messenger); - EXPECT_TRUE(FL_IS_MOCK_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle)); + EXPECT_TRUE(FL_IS_FAKE_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle)); g_autofree gchar* text = g_strndup(static_cast(g_bytes_get_data(response, nullptr)), diff --git a/shell/platform/linux/fl_key_channel_responder.cc b/shell/platform/linux/fl_key_channel_responder.cc index 757cc5d893937..5eadb6dc33275 100644 --- a/shell/platform/linux/fl_key_channel_responder.cc +++ b/shell/platform/linux/fl_key_channel_responder.cc @@ -94,8 +94,6 @@ struct _FlKeyChannelResponder { GObject parent_instance; FlBasicMessageChannel* channel; - - FlKeyChannelResponderMock* mock; }; G_DEFINE_TYPE(FlKeyChannelResponder, fl_key_channel_responder, G_TYPE_OBJECT) @@ -117,9 +115,6 @@ static void handle_response(GObject* object, FlBasicMessageChannel* messageChannel = FL_BASIC_MESSAGE_CHANNEL(object); FlValue* message = fl_basic_message_channel_send_finish(messageChannel, result, &error); - if (self->mock != nullptr && self->mock->value_converter != nullptr) { - message = self->mock->value_converter(message); - } bool handled = false; if (error != nullptr) { g_warning("Unable to retrieve framework response: %s", error->message); @@ -152,22 +147,16 @@ static void fl_key_channel_responder_init(FlKeyChannelResponder* self) {} // Creates a new FlKeyChannelResponder instance, with a messenger used to send // messages to the framework, and an FlTextInputHandler that is used to handle -// key events that the framework doesn't handle. Mainly for testing purposes, it -// also takes an optional callback to call when a response is received, and an -// optional channel name to use when sending messages. +// key events that the framework doesn't handle. FlKeyChannelResponder* fl_key_channel_responder_new( - FlBinaryMessenger* messenger, - FlKeyChannelResponderMock* mock) { + FlBinaryMessenger* messenger) { g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr); FlKeyChannelResponder* self = FL_KEY_CHANNEL_RESPONDER( g_object_new(fl_key_channel_responder_get_type(), nullptr)); - self->mock = mock; g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new(); - const char* channel_name = - mock == nullptr ? kChannelName : mock->channel_name; - self->channel = fl_basic_message_channel_new(messenger, channel_name, + self->channel = fl_basic_message_channel_new(messenger, kChannelName, FL_MESSAGE_CODEC(codec)); return self; diff --git a/shell/platform/linux/fl_key_channel_responder.h b/shell/platform/linux/fl_key_channel_responder.h index 66a47d22bd1ac..13d77875275d4 100644 --- a/shell/platform/linux/fl_key_channel_responder.h +++ b/shell/platform/linux/fl_key_channel_responder.h @@ -11,27 +11,6 @@ typedef FlValue* (*FlValueConverter)(FlValue*); -/** - * FlKeyChannelResponderMock: - * - * Allows mocking of FlKeyChannelResponder methods and values. Only used in - * unittests. - */ -typedef struct _FlKeyChannelResponderMock { - /** - * FlKeyChannelResponderMock::value_converter: - * If #value_converter is not nullptr, then this function is applied to the - * reply of the message, whose return value is taken as the message reply. - */ - FlValueConverter value_converter; - - /** - * FlKeyChannelResponderMock::channel_name: - * Mocks the channel name to send the message. - */ - const char* channel_name; -} FlKeyChannelResponderMock; - G_BEGIN_DECLS G_DECLARE_FINAL_TYPE(FlKeyChannelResponder, @@ -64,15 +43,13 @@ typedef void (*FlKeyChannelResponderAsyncCallback)(bool handled, /** * fl_key_channel_responder_new: * @messenger: the messenger that the message channel should be built on. - * @mock: options to mock several functionalities. Only used in unittests. * * Creates a new #FlKeyChannelResponder. * * Returns: a new #FlKeyChannelResponder. */ FlKeyChannelResponder* fl_key_channel_responder_new( - FlBinaryMessenger* messenger, - FlKeyChannelResponderMock* mock = nullptr); + FlBinaryMessenger* messenger); /** * fl_key_channel_responder_handle_event: diff --git a/shell/platform/linux/fl_key_channel_responder_test.cc b/shell/platform/linux/fl_key_channel_responder_test.cc index 2c68d40b0d12a..7b25eb2c955d2 100644 --- a/shell/platform/linux/fl_key_channel_responder_test.cc +++ b/shell/platform/linux/fl_key_channel_responder_test.cc @@ -7,64 +7,81 @@ #include "gtest/gtest.h" #include "flutter/shell/platform/linux/fl_binary_messenger_private.h" -#include "flutter/shell/platform/linux/fl_engine_private.h" -#include "flutter/shell/platform/linux/testing/fl_test.h" +#include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h" -static const char* expected_value = nullptr; -static gboolean expected_handled = FALSE; +typedef struct { + const gchar* expected_message; + gboolean handled; +} KeyEventData; -static FlValue* echo_response_cb(FlValue* echoed_value) { - gchar* text = fl_value_to_string(echoed_value); - EXPECT_STREQ(text, expected_value); - g_free(text); +static FlValue* key_event_cb(FlMockBinaryMessenger* messenger, + FlValue* message, + gpointer user_data) { + KeyEventData* data = static_cast(user_data); - FlValue* value = fl_value_new_map(); - fl_value_set_string_take(value, "handled", - fl_value_new_bool(expected_handled)); - return value; + g_autofree gchar* message_string = fl_value_to_string(message); + EXPECT_STREQ(message_string, data->expected_message); + + FlValue* response = fl_value_new_map(); + fl_value_set_string_take(response, "handled", + fl_value_new_bool(data->handled)); + + free(data); + + return response; } -static void responder_callback(bool handled, gpointer user_data) { - EXPECT_EQ(handled, expected_handled); - g_main_loop_quit(static_cast(user_data)); +static void set_key_event_channel(FlMockBinaryMessenger* messenger, + const gchar* expected_message, + gboolean handled) { + KeyEventData* data = g_new0(KeyEventData, 1); + data->expected_message = expected_message; + data->handled = handled; + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/keyevent", key_event_cb, data); } // Test sending a letter "A"; TEST(FlKeyChannelResponderTest, SendKeyEvent) { g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); - g_autoptr(FlEngine) engine = make_mock_engine(); - g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); - FlKeyChannelResponderMock mock{ - .value_converter = echo_response_cb, - .channel_name = "test/echo", - }; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); g_autoptr(FlKeyChannelResponder) responder = - fl_key_channel_responder_new(messenger, &mock); + fl_key_channel_responder_new(FL_BINARY_MESSENGER(messenger)); + set_key_event_channel( + messenger, + "{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, keyCode: 65, " + "modifiers: 0, unicodeScalarValues: 65}", + FALSE); g_autoptr(FlKeyEvent) event1 = fl_key_event_new( 12345, TRUE, 0x04, GDK_KEY_A, static_cast(0), 0); - fl_key_channel_responder_handle_event(responder, event1, 0, - responder_callback, loop); - expected_value = - "{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, keyCode: 65, " - "modifiers: 0, unicodeScalarValues: 65}"; - expected_handled = FALSE; - - // Blocks here until echo_response_cb is called. + fl_key_channel_responder_handle_event( + responder, event1, 0, + [](bool handled, gpointer user_data) { + EXPECT_FALSE(handled); + g_main_loop_quit(static_cast(user_data)); + }, + loop); g_main_loop_run(loop); + set_key_event_channel( + messenger, + "{type: keyup, keymap: linux, scanCode: 4, toolkit: gtk, keyCode: 65, " + "modifiers: 0, unicodeScalarValues: 65}", + FALSE); g_autoptr(FlKeyEvent) event2 = fl_key_event_new( 23456, FALSE, 0x04, GDK_KEY_A, static_cast(0), 0); - fl_key_channel_responder_handle_event(responder, event2, 0, - responder_callback, loop); - expected_value = - "{type: keyup, keymap: linux, scanCode: 4, toolkit: gtk, keyCode: 65, " - "modifiers: 0, unicodeScalarValues: 65}"; - expected_handled = FALSE; - - // Blocks here until echo_response_cb is called. + fl_key_channel_responder_handle_event( + responder, event2, 0, + [](bool handled, gpointer user_data) { + EXPECT_FALSE(handled); + g_main_loop_quit(static_cast(user_data)); + }, + loop); g_main_loop_run(loop); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } void test_lock_event(guint key_code, @@ -72,34 +89,35 @@ void test_lock_event(guint key_code, const char* up_expected) { g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); - g_autoptr(FlEngine) engine = make_mock_engine(); - g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); - FlKeyChannelResponderMock mock{ - .value_converter = echo_response_cb, - .channel_name = "test/echo", - }; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); g_autoptr(FlKeyChannelResponder) responder = - fl_key_channel_responder_new(messenger, &mock); + fl_key_channel_responder_new(FL_BINARY_MESSENGER(messenger)); + set_key_event_channel(messenger, down_expected, FALSE); g_autoptr(FlKeyEvent) event1 = fl_key_event_new( 12345, TRUE, 0x04, key_code, static_cast(0), 0); - fl_key_channel_responder_handle_event(responder, event1, 0, - responder_callback, loop); - expected_value = down_expected; - expected_handled = FALSE; - - // Blocks here until echo_response_cb is called. + fl_key_channel_responder_handle_event( + responder, event1, 0, + [](bool handled, gpointer user_data) { + EXPECT_FALSE(handled); + g_main_loop_quit(static_cast(user_data)); + }, + loop); g_main_loop_run(loop); - expected_value = up_expected; - expected_handled = FALSE; + set_key_event_channel(messenger, up_expected, FALSE); g_autoptr(FlKeyEvent) event2 = fl_key_event_new( 12346, FALSE, 0x04, key_code, static_cast(0), 0); - fl_key_channel_responder_handle_event(responder, event2, 0, - responder_callback, loop); - - // Blocks here until echo_response_cb is called. + fl_key_channel_responder_handle_event( + responder, event2, 0, + [](bool handled, gpointer user_data) { + EXPECT_FALSE(handled); + g_main_loop_quit(static_cast(user_data)); + }, + loop); g_main_loop_run(loop); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } // Test sending a "NumLock" keypress. @@ -132,50 +150,52 @@ TEST(FlKeyChannelResponderTest, SendShiftLockKeyEvent) { TEST(FlKeyChannelResponderTest, TestKeyEventHandledByFramework) { g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); - g_autoptr(FlEngine) engine = make_mock_engine(); - g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); - FlKeyChannelResponderMock mock{ - .value_converter = echo_response_cb, - .channel_name = "test/echo", - }; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); g_autoptr(FlKeyChannelResponder) responder = - fl_key_channel_responder_new(messenger, &mock); + fl_key_channel_responder_new(FL_BINARY_MESSENGER(messenger)); + set_key_event_channel( + messenger, + "{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, " + "keyCode: 65, modifiers: 0, unicodeScalarValues: 65}", + TRUE); g_autoptr(FlKeyEvent) event = fl_key_event_new( 12345, TRUE, 0x04, GDK_KEY_A, static_cast(0), 0); - fl_key_channel_responder_handle_event(responder, event, 0, responder_callback, - loop); - expected_handled = TRUE; - expected_value = - "{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, " - "keyCode: 65, modifiers: 0, unicodeScalarValues: 65}"; - - // Blocks here until echo_response_cb is called. + fl_key_channel_responder_handle_event( + responder, event, 0, + [](bool handled, gpointer user_data) { + EXPECT_TRUE(handled); + g_main_loop_quit(static_cast(user_data)); + }, + loop); g_main_loop_run(loop); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlKeyChannelResponderTest, UseSpecifiedLogicalKey) { g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); - g_autoptr(FlEngine) engine = make_mock_engine(); - g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); - FlKeyChannelResponderMock mock{ - .value_converter = echo_response_cb, - .channel_name = "test/echo", - }; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); g_autoptr(FlKeyChannelResponder) responder = - fl_key_channel_responder_new(messenger, &mock); + fl_key_channel_responder_new(FL_BINARY_MESSENGER(messenger)); - g_autoptr(FlKeyEvent) event = fl_key_event_new( - 12345, TRUE, 0x04, GDK_KEY_A, static_cast(0), 0); - fl_key_channel_responder_handle_event(responder, event, 888, - responder_callback, loop); - expected_handled = TRUE; - expected_value = + set_key_event_channel( + messenger, "{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, " "keyCode: 65, modifiers: 0, unicodeScalarValues: 65, " - "specifiedLogicalKey: 888}"; - - // Blocks here until echo_response_cb is called. + "specifiedLogicalKey: 888}", + TRUE); + g_autoptr(FlKeyEvent) event = fl_key_event_new( + 12345, TRUE, 0x04, GDK_KEY_A, static_cast(0), 0); + fl_key_channel_responder_handle_event( + responder, event, 888, + [](bool handled, gpointer user_data) { + EXPECT_TRUE(handled); + g_main_loop_quit(static_cast(user_data)); + }, + loop); g_main_loop_run(loop); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } diff --git a/shell/platform/linux/fl_keyboard_handler_test.cc b/shell/platform/linux/fl_keyboard_handler_test.cc index 26fd103263657..5cb1f6c86b210 100644 --- a/shell/platform/linux/fl_keyboard_handler_test.cc +++ b/shell/platform/linux/fl_keyboard_handler_test.cc @@ -4,9 +4,10 @@ #include "flutter/shell/platform/linux/fl_keyboard_handler.h" +#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" #include "flutter/shell/platform/linux/fl_method_codec_private.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h" -#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h" +#include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -26,19 +27,6 @@ G_DECLARE_FINAL_TYPE(FlMockKeyboardHandlerDelegate, G_END_DECLS -MATCHER_P(MethodSuccessResponse, result, "") { - g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); - g_autoptr(FlMethodResponse) response = - fl_method_codec_decode_response(FL_METHOD_CODEC(codec), arg, nullptr); - fl_method_response_get_result(response, nullptr); - if (fl_value_equal(fl_method_response_get_result(response, nullptr), - result)) { - return true; - } - *result_listener << ::testing::PrintToString(response); - return false; -} - struct _FlMockKeyboardHandlerDelegate { GObject parent_instance; }; @@ -74,8 +62,7 @@ static FlMockKeyboardHandlerDelegate* fl_mock_keyboard_handler_delegate_new() { } TEST(FlKeyboardHandlerTest, KeyboardChannelGetPressedState) { - ::testing::NiceMock messenger; - + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); g_autoptr(FlEngine) engine = FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", FL_BINARY_MESSENGER(messenger), nullptr)); @@ -95,21 +82,28 @@ TEST(FlKeyboardHandlerTest, KeyboardChannelGetPressedState) { }, nullptr); g_autoptr(FlKeyboardHandler) handler = - fl_keyboard_handler_new(messenger, manager); + fl_keyboard_handler_new(FL_BINARY_MESSENGER(messenger), manager); EXPECT_NE(handler, nullptr); - g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); - g_autoptr(GBytes) message = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), kGetKeyboardStateMethod, nullptr, nullptr); - - g_autoptr(FlValue) response = fl_value_new_map(); - fl_value_set_take(response, fl_value_new_int(kMockPhysicalKey), - fl_value_new_int(kMockLogicalKey)); - EXPECT_CALL(messenger, - fl_binary_messenger_send_response( - ::testing::Eq(messenger), ::testing::_, - MethodSuccessResponse(response), ::testing::_)) - .WillOnce(::testing::Return(true)); + gboolean called = FALSE; + fl_mock_binary_messenger_invoke_standard_method( + messenger, kKeyboardChannelName, kGetKeyboardStateMethod, nullptr, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + + g_autoptr(FlValue) expected_result = fl_value_new_map(); + fl_value_set_take(expected_result, fl_value_new_int(kMockPhysicalKey), + fl_value_new_int(kMockLogicalKey)); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); - messenger.ReceiveMessage(kKeyboardChannelName, message); + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } diff --git a/shell/platform/linux/fl_method_channel.cc b/shell/platform/linux/fl_method_channel.cc index 1177d63f05a9d..e0d94e7bb3781 100644 --- a/shell/platform/linux/fl_method_channel.cc +++ b/shell/platform/linux/fl_method_channel.cc @@ -220,34 +220,13 @@ gboolean fl_method_channel_respond( g_return_val_if_fail(FL_IS_METHOD_CHANNEL(self), FALSE); g_return_val_if_fail(FL_IS_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle), FALSE); - g_return_val_if_fail(FL_IS_METHOD_SUCCESS_RESPONSE(response) || - FL_IS_METHOD_ERROR_RESPONSE(response) || - FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response), - FALSE); + g_return_val_if_fail(FL_IS_METHOD_RESPONSE(response), FALSE); - g_autoptr(GBytes) message = nullptr; - if (FL_IS_METHOD_SUCCESS_RESPONSE(response)) { - FlMethodSuccessResponse* r = FL_METHOD_SUCCESS_RESPONSE(response); - message = fl_method_codec_encode_success_envelope( - self->codec, fl_method_success_response_get_result(r), error); - if (message == nullptr) { - return FALSE; - } - } else if (FL_IS_METHOD_ERROR_RESPONSE(response)) { - FlMethodErrorResponse* r = FL_METHOD_ERROR_RESPONSE(response); - message = fl_method_codec_encode_error_envelope( - self->codec, fl_method_error_response_get_code(r), - fl_method_error_response_get_message(r), - fl_method_error_response_get_details(r), error); - if (message == nullptr) { - return FALSE; - } - } else if (FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response)) { - message = nullptr; - } else { - g_assert_not_reached(); + g_autoptr(GBytes) message = + fl_method_codec_encode_response(self->codec, response, error); + if (message == nullptr) { + return FALSE; } - return fl_binary_messenger_send_response(self->messenger, response_handle, message, error); } diff --git a/shell/platform/linux/fl_method_codec.cc b/shell/platform/linux/fl_method_codec.cc index 5b6d87c47a39a..a0c023ca759c3 100644 --- a/shell/platform/linux/fl_method_codec.cc +++ b/shell/platform/linux/fl_method_codec.cc @@ -59,6 +59,32 @@ GBytes* fl_method_codec_encode_error_envelope(FlMethodCodec* self, self, code, message, details, error); } +GBytes* fl_method_codec_encode_response(FlMethodCodec* self, + FlMethodResponse* response, + GError** error) { + g_return_val_if_fail(FL_IS_METHOD_CODEC(self), nullptr); + g_return_val_if_fail(FL_IS_METHOD_SUCCESS_RESPONSE(response) || + FL_IS_METHOD_ERROR_RESPONSE(response) || + FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response), + nullptr); + + if (FL_IS_METHOD_SUCCESS_RESPONSE(response)) { + FlMethodSuccessResponse* r = FL_METHOD_SUCCESS_RESPONSE(response); + return fl_method_codec_encode_success_envelope( + self, fl_method_success_response_get_result(r), error); + } else if (FL_IS_METHOD_ERROR_RESPONSE(response)) { + FlMethodErrorResponse* r = FL_METHOD_ERROR_RESPONSE(response); + return fl_method_codec_encode_error_envelope( + self, fl_method_error_response_get_code(r), + fl_method_error_response_get_message(r), + fl_method_error_response_get_details(r), error); + } else if (FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response)) { + return g_bytes_new(nullptr, 0); + } else { + g_assert_not_reached(); + } +} + FlMethodResponse* fl_method_codec_decode_response(FlMethodCodec* self, GBytes* message, GError** error) { diff --git a/shell/platform/linux/fl_method_codec_private.h b/shell/platform/linux/fl_method_codec_private.h index 8912093adc29c..1880b09cd0983 100644 --- a/shell/platform/linux/fl_method_codec_private.h +++ b/shell/platform/linux/fl_method_codec_private.h @@ -85,6 +85,21 @@ GBytes* fl_method_codec_encode_error_envelope(FlMethodCodec* codec, FlValue* details, GError** error); +/** + * fl_method_codec_encode_response: + * @codec: an #FlMethodCodec. + * @response: response to encode. + * @error: (allow-none): #GError location to store the error occurring, or + * %NULL. + * + * Encodes a response to a method call. + * + * Returns: a new #FlMethodResponse or %NULL on error. + */ +GBytes* fl_method_codec_encode_response(FlMethodCodec* codec, + FlMethodResponse* response, + GError** error); + /** * fl_method_codec_decode_response: * @codec: an #FlMethodCodec. @@ -92,8 +107,7 @@ GBytes* fl_method_codec_encode_error_envelope(FlMethodCodec* codec, * @error: (allow-none): #GError location to store the error occurring, or * %NULL. * - * Decodes a response to a method call. If the call resulted in an error then - * @error_code is set, otherwise it is %NULL. + * Decodes a response to a method call. * * Returns: a new #FlMethodResponse or %NULL on error. */ diff --git a/shell/platform/linux/fl_platform_handler_test.cc b/shell/platform/linux/fl_platform_handler_test.cc index 98369d227beff..4de202e1eb047 100644 --- a/shell/platform/linux/fl_platform_handler_test.cc +++ b/shell/platform/linux/fl_platform_handler_test.cc @@ -5,90 +5,14 @@ #include #include "flutter/shell/platform/linux/fl_binary_messenger_private.h" -#include "flutter/shell/platform/linux/fl_method_codec_private.h" #include "flutter/shell/platform/linux/fl_platform_handler.h" -#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h" +#include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h" #include "flutter/shell/platform/linux/testing/fl_test.h" -#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h" -#include "flutter/testing/testing.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -MATCHER_P(SuccessResponse, result, "") { - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(FlMethodResponse) response = - fl_method_codec_decode_response(FL_METHOD_CODEC(codec), arg, nullptr); - if (fl_value_equal(fl_method_response_get_result(response, nullptr), - result)) { - return true; - } - *result_listener << ::testing::PrintToString(response); - return false; -} - -class MethodCallMatcher { - public: - using is_gtest_matcher = void; - - explicit MethodCallMatcher(::testing::Matcher name, - ::testing::Matcher args) - : name_(std::move(name)), args_(std::move(args)) {} - - bool MatchAndExplain(GBytes* method_call, - ::testing::MatchResultListener* result_listener) const { - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GError) error = nullptr; - g_autofree gchar* name = nullptr; - g_autoptr(FlValue) args = nullptr; - gboolean result = fl_method_codec_decode_method_call( - FL_METHOD_CODEC(codec), method_call, &name, &args, &error); - if (!result) { - *result_listener << ::testing::PrintToString(error->message); - return false; - } - if (!name_.MatchAndExplain(name, result_listener)) { - *result_listener << " where the name doesn't match: \"" << name << "\""; - return false; - } - if (!args_.MatchAndExplain(args, result_listener)) { - *result_listener << " where the args don't match: " - << ::testing::PrintToString(args); - return false; - } - return true; - } - - void DescribeTo(std::ostream* os) const { - *os << "method name "; - name_.DescribeTo(os); - *os << " and args "; - args_.DescribeTo(os); - } - - void DescribeNegationTo(std::ostream* os) const { - *os << "method name "; - name_.DescribeNegationTo(os); - *os << " or args "; - args_.DescribeNegationTo(os); - } - - private: - ::testing::Matcher name_; - ::testing::Matcher args_; -}; - -static ::testing::Matcher MethodCall( - const std::string& name, - ::testing::Matcher args) { - return MethodCallMatcher(::testing::StrEq(name), std::move(args)); -} - -MATCHER_P(FlValueEq, value, "equal to " + ::testing::PrintToString(value)) { - return fl_value_equal(arg, value); -} - G_DECLARE_FINAL_TYPE(FlTestApplication, fl_test_application, FL, @@ -116,26 +40,35 @@ static void fl_test_application_startup(GApplication* application) { static void fl_test_application_activate(GApplication* application) { G_APPLICATION_CLASS(fl_test_application_parent_class)->activate(application); - ::testing::NiceMock messenger; - g_autoptr(FlPlatformHandler) handler = fl_platform_handler_new(messenger); + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + g_autoptr(FlPlatformHandler) handler = + fl_platform_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - - g_autoptr(FlValue) exit_result = fl_value_new_map(); - fl_value_set_string_take(exit_result, "response", - fl_value_new_string("exit")); - EXPECT_CALL(messenger, - fl_binary_messenger_send_response( - ::testing::Eq(messenger), ::testing::_, - SuccessResponse(exit_result), ::testing::_)) - .WillOnce(::testing::Return(true)); // Request app exit. + gboolean called = FALSE; g_autoptr(FlValue) args = fl_value_new_map(); fl_value_set_string_take(args, "type", fl_value_new_string("required")); - g_autoptr(GBytes) message = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "System.exitApplication", args, nullptr); - messenger.ReceiveMessage("flutter/platform", message); + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/platform", "System.exitApplication", args, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + + g_autoptr(FlValue) expected_result = fl_value_new_map(); + fl_value_set_string_take(expected_result, "response", + fl_value_new_string("exit")); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } static void fl_test_application_dispose(GObject* object) { @@ -171,59 +104,107 @@ FlTestApplication* fl_test_application_new(gboolean* dispose_called) { } TEST(FlPlatformHandlerTest, PlaySound) { - ::testing::NiceMock messenger; - - g_autoptr(FlPlatformHandler) handler = fl_platform_handler_new(messenger); + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + g_autoptr(FlPlatformHandler) handler = + fl_platform_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); + gboolean called = FALSE; g_autoptr(FlValue) args = fl_value_new_string("SystemSoundType.alert"); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) message = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "SystemSound.play", args, nullptr); - - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/platform", message); + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/platform", "SystemSound.play", args, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + + g_autoptr(FlValue) expected_result = fl_value_new_null(); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlPlatformHandlerTest, ExitApplication) { - ::testing::NiceMock messenger; + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); - g_autoptr(FlPlatformHandler) handler = fl_platform_handler_new(messenger); + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + g_autoptr(FlPlatformHandler) handler = + fl_platform_handler_new(FL_BINARY_MESSENGER(messenger)); EXPECT_NE(handler, nullptr); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - - g_autoptr(FlValue) null = fl_value_new_null(); - ON_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillByDefault(testing::Return(TRUE)); // Indicate that the binding is initialized. - g_autoptr(GError) error = nullptr; - g_autoptr(GBytes) init_message = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "System.initializationComplete", nullptr, &error); - messenger.ReceiveMessage("flutter/platform", init_message); - - g_autoptr(FlValue) request_args = fl_value_new_map(); - fl_value_set_string_take(request_args, "type", - fl_value_new_string("cancelable")); - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/platform"), - MethodCall("System.requestAppExit", FlValueEq(request_args)), - ::testing::_, ::testing::_, ::testing::_)); + gboolean called = FALSE; + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/platform", "System.initializationComplete", nullptr, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + + g_autoptr(FlValue) expected_result = fl_value_new_null(); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); + + gboolean request_exit_called = FALSE; + fl_mock_binary_messenger_set_json_method_channel( + messenger, "flutter/platform", + [](FlMockBinaryMessenger* messenger, const gchar* name, FlValue* args, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_STREQ(name, "System.requestAppExit"); + + g_autoptr(FlValue) expected_args = fl_value_new_map(); + fl_value_set_string_take(expected_args, "type", + fl_value_new_string("cancelable")); + EXPECT_TRUE(fl_value_equal(args, expected_args)); + + // Cancel so it doesn't try and exit this app (i.e. the current test) + g_autoptr(FlValue) result = fl_value_new_map(); + fl_value_set_string_take(result, "response", + fl_value_new_string("cancel")); + return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); + }, + &request_exit_called); g_autoptr(FlValue) args = fl_value_new_map(); fl_value_set_string_take(args, "type", fl_value_new_string("cancelable")); - g_autoptr(GBytes) message = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "System.exitApplication", args, nullptr); - messenger.ReceiveMessage("flutter/platform", message); + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/platform", "System.exitApplication", args, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + + g_autoptr(FlValue) expected_result = fl_value_new_map(); + fl_value_set_string_take(expected_result, "response", + fl_value_new_string("cancel")); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + + g_main_loop_quit(static_cast(user_data)); + }, + loop); + + g_main_loop_run(loop); + + EXPECT_TRUE(request_exit_called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlPlatformHandlerTest, ExitApplicationDispose) { diff --git a/shell/platform/linux/fl_settings_handler_test.cc b/shell/platform/linux/fl_settings_handler_test.cc index 3bffcc2f23313..e8ebd34934100 100644 --- a/shell/platform/linux/fl_settings_handler_test.cc +++ b/shell/platform/linux/fl_settings_handler_test.cc @@ -5,114 +5,187 @@ #include "flutter/shell/platform/linux/fl_settings_handler.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" +#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" #include "flutter/shell/platform/linux/fl_engine_private.h" -#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" -#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h" -#include "flutter/shell/platform/linux/public/flutter_linux/fl_value.h" +#include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h" #include "flutter/shell/platform/linux/testing/fl_test.h" -#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h" #include "flutter/shell/platform/linux/testing/mock_settings.h" #include "flutter/testing/testing.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -MATCHER_P2(HasSetting, key, value, "") { - g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new(); - g_autoptr(FlValue) message = - fl_message_codec_decode_message(FL_MESSAGE_CODEC(codec), arg, nullptr); - if (fl_value_equal(fl_value_lookup_string(message, key), value)) { - return true; - } - *result_listener << ::testing::PrintToString(message); - return false; -} - -#define EXPECT_SETTING(messenger, key, value) \ - EXPECT_CALL( \ - messenger, \ - fl_binary_messenger_send_on_channel( \ - ::testing::Eq(messenger), \ - ::testing::StrEq("flutter/settings"), HasSetting(key, value), \ - ::testing::A(), ::testing::A(), \ - ::testing::A())) - TEST(FlSettingsHandlerTest, AlwaysUse24HourFormat) { ::testing::NiceMock settings; - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); g_autoptr(FlEngine) engine = FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", FL_BINARY_MESSENGER(messenger), nullptr)); g_autoptr(FlSettingsHandler) handler = fl_settings_handler_new(engine); - g_autoptr(FlValue) use_12h = fl_value_new_bool(false); - g_autoptr(FlValue) use_24h = fl_value_new_bool(true); - EXPECT_CALL(settings, fl_settings_get_clock_format( ::testing::Eq(settings))) .WillOnce(::testing::Return(FL_CLOCK_FORMAT_12H)) .WillOnce(::testing::Return(FL_CLOCK_FORMAT_24H)); - EXPECT_SETTING(messenger, "alwaysUse24HourFormat", use_12h); - + gboolean called = FALSE; + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/settings", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_MAP); + FlValue* value = + fl_value_lookup_string(message, "alwaysUse24HourFormat"); + EXPECT_NE(value, nullptr); + EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_BOOL); + EXPECT_FALSE(fl_value_get_bool(value)); + + return fl_value_new_null(); + }, + &called); fl_settings_handler_start(handler, settings); - - EXPECT_SETTING(messenger, "alwaysUse24HourFormat", use_24h); - + EXPECT_TRUE(called); + + called = FALSE; + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/settings", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_MAP); + FlValue* value = + fl_value_lookup_string(message, "alwaysUse24HourFormat"); + EXPECT_NE(value, nullptr); + EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_BOOL); + EXPECT_TRUE(fl_value_get_bool(value)); + + return fl_value_new_null(); + }, + &called); fl_settings_emit_changed(settings); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlSettingsHandlerTest, PlatformBrightness) { ::testing::NiceMock settings; - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); g_autoptr(FlEngine) engine = FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", FL_BINARY_MESSENGER(messenger), nullptr)); g_autoptr(FlSettingsHandler) handler = fl_settings_handler_new(engine); - g_autoptr(FlValue) light = fl_value_new_string("light"); - g_autoptr(FlValue) dark = fl_value_new_string("dark"); - EXPECT_CALL(settings, fl_settings_get_color_scheme( ::testing::Eq(settings))) .WillOnce(::testing::Return(FL_COLOR_SCHEME_LIGHT)) .WillOnce(::testing::Return(FL_COLOR_SCHEME_DARK)); - EXPECT_SETTING(messenger, "platformBrightness", light); - + gboolean called = FALSE; + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/settings", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_MAP); + FlValue* value = fl_value_lookup_string(message, "platformBrightness"); + EXPECT_NE(value, nullptr); + EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(value), "light"); + + return fl_value_new_null(); + }, + &called); fl_settings_handler_start(handler, settings); - - EXPECT_SETTING(messenger, "platformBrightness", dark); - + EXPECT_TRUE(called); + + called = FALSE; + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/settings", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_MAP); + FlValue* value = fl_value_lookup_string(message, "platformBrightness"); + EXPECT_NE(value, nullptr); + EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(value), "dark"); + + return fl_value_new_null(); + }, + &called); fl_settings_emit_changed(settings); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlSettingsHandlerTest, TextScaleFactor) { ::testing::NiceMock settings; - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); g_autoptr(FlEngine) engine = FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", FL_BINARY_MESSENGER(messenger), nullptr)); g_autoptr(FlSettingsHandler) handler = fl_settings_handler_new(engine); - g_autoptr(FlValue) one = fl_value_new_float(1.0); - g_autoptr(FlValue) two = fl_value_new_float(2.0); - EXPECT_CALL(settings, fl_settings_get_text_scaling_factor( ::testing::Eq(settings))) .WillOnce(::testing::Return(1.0)) .WillOnce(::testing::Return(2.0)); - EXPECT_SETTING(messenger, "textScaleFactor", one); - + gboolean called = FALSE; + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/settings", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_MAP); + FlValue* value = fl_value_lookup_string(message, "textScaleFactor"); + EXPECT_NE(value, nullptr); + EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT); + EXPECT_EQ(fl_value_get_float(value), 1.0); + + return fl_value_new_null(); + }, + &called); fl_settings_handler_start(handler, settings); - - EXPECT_SETTING(messenger, "textScaleFactor", two); - + EXPECT_TRUE(called); + + called = FALSE; + fl_mock_binary_messenger_set_json_message_channel( + messenger, "flutter/settings", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_MAP); + FlValue* value = fl_value_lookup_string(message, "textScaleFactor"); + EXPECT_NE(value, nullptr); + EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT); + EXPECT_EQ(fl_value_get_float(value), 2.0); + + return fl_value_new_null(); + }, + &called); fl_settings_emit_changed(settings); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } // MOCK_ENGINE_PROC is leaky by design diff --git a/shell/platform/linux/fl_text_input_handler_test.cc b/shell/platform/linux/fl_text_input_handler_test.cc index 7787b422c3c6c..a124b236ff3c0 100644 --- a/shell/platform/linux/fl_text_input_handler_test.cc +++ b/shell/platform/linux/fl_text_input_handler_test.cc @@ -4,14 +4,11 @@ #include +#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" #include "flutter/shell/platform/linux/fl_method_codec_private.h" #include "flutter/shell/platform/linux/fl_text_input_handler.h" -#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" -#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h" -#include "flutter/shell/platform/linux/public/flutter_linux/fl_value.h" +#include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h" #include "flutter/shell/platform/linux/testing/fl_test.h" -#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h" -#include "flutter/shell/platform/linux/testing/mock_binary_messenger_response_handle.h" #include "flutter/shell/platform/linux/testing/mock_im_context.h" #include "flutter/shell/platform/linux/testing/mock_text_input_view_delegate.h" #include "flutter/testing/testing.h" @@ -19,83 +16,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -void printTo(FlMethodResponse* response, ::std::ostream* os) { - *os << ::testing::PrintToString( - fl_method_response_get_result(response, nullptr)); -} - -MATCHER_P(SuccessResponse, result, "") { - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(FlMethodResponse) response = - fl_method_codec_decode_response(FL_METHOD_CODEC(codec), arg, nullptr); - if (fl_value_equal(fl_method_response_get_result(response, nullptr), - result)) { - return true; - } - *result_listener << ::testing::PrintToString(response); - return false; -} - -MATCHER_P(FlValueEq, value, "equal to " + ::testing::PrintToString(value)) { - return fl_value_equal(arg, value); -} - -class MethodCallMatcher { - public: - using is_gtest_matcher = void; - - explicit MethodCallMatcher(::testing::Matcher name, - ::testing::Matcher args) - : name_(std::move(name)), args_(std::move(args)) {} - - bool MatchAndExplain(GBytes* method_call, - ::testing::MatchResultListener* result_listener) const { - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GError) error = nullptr; - g_autofree gchar* name = nullptr; - g_autoptr(FlValue) args = nullptr; - gboolean result = fl_method_codec_decode_method_call( - FL_METHOD_CODEC(codec), method_call, &name, &args, &error); - if (!result) { - *result_listener << ::testing::PrintToString(error->message); - return false; - } - if (!name_.MatchAndExplain(name, result_listener)) { - *result_listener << " where the name doesn't match: \"" << name << "\""; - return false; - } - if (!args_.MatchAndExplain(args, result_listener)) { - *result_listener << " where the args don't match: " - << ::testing::PrintToString(args); - return false; - } - return true; - } - - void DescribeTo(std::ostream* os) const { - *os << "method name "; - name_.DescribeTo(os); - *os << " and args "; - args_.DescribeTo(os); - } - - void DescribeNegationTo(std::ostream* os) const { - *os << "method name "; - name_.DescribeNegationTo(os); - *os << " or args "; - args_.DescribeNegationTo(os); - } - - private: - ::testing::Matcher name_; - ::testing::Matcher args_; -}; - -::testing::Matcher MethodCall(const std::string& name, - ::testing::Matcher args) { - return MethodCallMatcher(::testing::StrEq(name), std::move(args)); -} - static FlValue* build_map(std::map args) { FlValue* value = fl_value_new_map(); for (auto it = args.begin(); it != args.end(); ++it) { @@ -178,6 +98,49 @@ static FlValue* build_editing_delta(EditingDelta delta) { }); } +static void set_client(FlMockBinaryMessenger* messenger, InputConfig config) { + gboolean called = FALSE; + g_autoptr(FlValue) args = build_input_config(config); + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/textinput", "TextInput.setClient", args, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + + g_autoptr(FlValue) expected_result = fl_value_new_null(); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); +} + +static void set_editing_state(FlMockBinaryMessenger* messenger, + EditingState state) { + gboolean called = FALSE; + g_autoptr(FlValue) args = build_editing_state(state); + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/textinput", "TextInput.setEditingState", args, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + + g_autoptr(FlValue) expected_result = fl_value_new_null(); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); +} + static void send_key_event(FlTextInputHandler* handler, gint keyval, gint state = 0) { @@ -189,420 +152,391 @@ static void send_key_event(FlTextInputHandler* handler, } TEST(FlTextInputHandlerTest, MessageHandler) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); - EXPECT_TRUE(messenger.HasMessageHandler("flutter/textinput")); + EXPECT_TRUE( + fl_mock_binary_messenger_has_handler(messenger, "flutter/textinput")); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, SetClient) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); - g_autoptr(FlValue) args = build_input_config({.client_id = 1}); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) message = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr); + set_client(messenger, {.client_id = 1}); - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", message); + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, Show) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); EXPECT_CALL(context, gtk_im_context_focus_in(::testing::Eq(context))); - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); + gboolean called = FALSE; + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/textinput", "TextInput.show", nullptr, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) message = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.show", nullptr, nullptr); + g_autoptr(FlValue) expected_result = fl_value_new_null(); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); - messenger.ReceiveMessage("flutter/textinput", message); + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, Hide) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); EXPECT_CALL(context, gtk_im_context_focus_out(::testing::Eq(context))); - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); + gboolean called = FALSE; + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/textinput", "TextInput.hide", nullptr, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) message = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.hide", nullptr, nullptr); + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); - messenger.ReceiveMessage("flutter/textinput", message); + g_autoptr(FlValue) expected_result = fl_value_new_null(); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, ClearClient) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); + gboolean called = FALSE; + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/textinput", "TextInput.clearClient", nullptr, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) message = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.clearClient", nullptr, nullptr); + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + + g_autoptr(FlValue) expected_result = fl_value_new_null(); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); - messenger.ReceiveMessage("flutter/textinput", message); + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, PerformAction) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); - // set input config - g_autoptr(FlValue) config = build_input_config({ - .client_id = 1, - .input_type = "TextInputType.multiline", - .input_action = "TextInputAction.newline", - }); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr); - - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_client); - - // set editing state - g_autoptr(FlValue) state = build_editing_state({ - .text = "Flutter", - .selection_base = 7, - .selection_extent = 7, - }); - g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr); - - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_state); - - // update editing state - g_autoptr(FlValue) new_state = build_list({ - fl_value_new_int(1), // client_id - build_editing_state({ - .text = "Flutter\n", - .selection_base = 8, - .selection_extent = 8, - }), - }); - - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingState", - FlValueEq(new_state)), - ::testing::_, ::testing::_, ::testing::_)); - - // perform action - g_autoptr(FlValue) action = build_list({ - fl_value_new_int(1), // client_id - fl_value_new_string("TextInputAction.newline"), - }); - - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.performAction", - FlValueEq(action)), - ::testing::_, ::testing::_, ::testing::_)); + set_client(messenger, { + .client_id = 1, + .input_type = "TextInputType.multiline", + .input_action = "TextInputAction.newline", + }); + set_editing_state(messenger, { + .text = "Flutter", + .selection_base = 7, + .selection_extent = 7, + }); + + // Client will update editing state and perform action + int call_count = 0; + fl_mock_binary_messenger_set_json_method_channel( + messenger, "flutter/textinput", + [](FlMockBinaryMessenger* messenger, const gchar* name, FlValue* args, + gpointer user_data) { + int* call_count = static_cast(user_data); + + if (strcmp(name, "TextInputClient.updateEditingState") == 0) { + g_autoptr(FlValue) expected_args = build_list({ + fl_value_new_int(1), // client_id + build_editing_state({ + .text = "Flutter\n", + .selection_base = 8, + .selection_extent = 8, + }), + }); + EXPECT_TRUE(fl_value_equal(args, expected_args)); + EXPECT_EQ(*call_count, 0); + (*call_count)++; + } else if (strcmp(name, "TextInputClient.performAction") == 0) { + g_autoptr(FlValue) expected_args = build_list({ + fl_value_new_int(1), // client_id + fl_value_new_string("TextInputAction.newline"), + }); + EXPECT_TRUE(fl_value_equal(args, expected_args)); + EXPECT_EQ(*call_count, 1); + (*call_count)++; + } + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + }, + &call_count); send_key_event(handler, GDK_KEY_Return); + EXPECT_EQ(call_count, 2); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } // Regression test for https://github.com/flutter/flutter/issues/125879. TEST(FlTextInputHandlerTest, MultilineWithSendAction) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); - // Set input config. - g_autoptr(FlValue) config = build_input_config({ - .client_id = 1, - .input_type = "TextInputType.multiline", - .input_action = "TextInputAction.send", - }); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr); - - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_client); - - // Set editing state. - g_autoptr(FlValue) state = build_editing_state({ - .text = "Flutter", - .selection_base = 7, - .selection_extent = 7, - }); - g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr); - - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_state); - - // Perform action. - g_autoptr(FlValue) action = build_list({ - fl_value_new_int(1), // client_id - fl_value_new_string("TextInputAction.send"), - }); + set_client(messenger, { + .client_id = 1, + .input_type = "TextInputType.multiline", + .input_action = "TextInputAction.send", + }); + set_editing_state(messenger, { + .text = "Flutter", + .selection_base = 7, + .selection_extent = 7, + }); // Because the input action is not set to TextInputAction.newline, the next // expected call is "TextInputClient.performAction". If the input action was // set to TextInputAction.newline the next call would be // "TextInputClient.updateEditingState" (this case is tested in the test named // 'PerformAction'). - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.performAction", - FlValueEq(action)), - ::testing::_, ::testing::_, ::testing::_)); + int call_count = 0; + fl_mock_binary_messenger_set_json_method_channel( + messenger, "flutter/textinput", + [](FlMockBinaryMessenger* messenger, const gchar* name, FlValue* args, + gpointer user_data) { + int* call_count = static_cast(user_data); + + EXPECT_STREQ(name, "TextInputClient.performAction"); + g_autoptr(FlValue) expected_args = nullptr; + switch (*call_count) { + case 0: + // Perform action. + expected_args = build_list({ + fl_value_new_int(1), // client_id + fl_value_new_string("TextInputAction.send"), + }); + break; + default: + g_assert_not_reached(); + break; + } + EXPECT_TRUE(fl_value_equal(args, expected_args)); + (*call_count)++; + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + }, + &call_count); send_key_event(handler, GDK_KEY_Return); + EXPECT_EQ(call_count, 1); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, MoveCursor) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); - // set input config - g_autoptr(FlValue) config = build_input_config({.client_id = 1}); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr); - - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_client); - - // set editing state - g_autoptr(FlValue) state = build_editing_state({ - .text = "Flutter", - .selection_base = 4, - .selection_extent = 4, - }); - g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr); - - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_state); - - // move cursor to beginning - g_autoptr(FlValue) beginning = build_list({ - fl_value_new_int(1), // client_id - build_editing_state({ - .text = "Flutter", - .selection_base = 0, - .selection_extent = 0, - }), - }); - - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingState", - FlValueEq(beginning)), - ::testing::_, ::testing::_, ::testing::_)); + set_client(messenger, {.client_id = 1}); + set_editing_state(messenger, { + .text = "Flutter", + .selection_base = 4, + .selection_extent = 4, + }); + + int call_count = 0; + fl_mock_binary_messenger_set_json_method_channel( + messenger, "flutter/textinput", + [](FlMockBinaryMessenger* messenger, const gchar* name, FlValue* args, + gpointer user_data) { + int* call_count = static_cast(user_data); + + EXPECT_STREQ(name, "TextInputClient.updateEditingState"); + g_autoptr(FlValue) expected_args = nullptr; + switch (*call_count) { + case 0: + // move cursor to beginning + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_editing_state({ + .text = "Flutter", + .selection_base = 0, + .selection_extent = 0, + }), + }); + break; + case 1: + // move cursor to end + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_editing_state({ + .text = "Flutter", + .selection_base = 7, + .selection_extent = 7, + }), + }); + break; + default: + g_assert_not_reached(); + break; + } + EXPECT_TRUE(fl_value_equal(args, expected_args)); + (*call_count)++; + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + }, + &call_count); send_key_event(handler, GDK_KEY_Home); - - // move cursor to end - g_autoptr(FlValue) end = build_list({ - fl_value_new_int(1), // client_id - build_editing_state({ - .text = "Flutter", - .selection_base = 7, - .selection_extent = 7, - }), - }); - - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingState", - FlValueEq(end)), - ::testing::_, ::testing::_, ::testing::_)); - send_key_event(handler, GDK_KEY_End); + EXPECT_EQ(call_count, 2); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, Select) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); - // set input config - g_autoptr(FlValue) config = build_input_config({.client_id = 1}); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr); - - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_client); - - // set editing state - g_autoptr(FlValue) state = build_editing_state({ - .text = "Flutter", - .selection_base = 4, - .selection_extent = 4, - }); - g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr); - - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_state); - - // select to end - g_autoptr(FlValue) select_to_end = build_list({ - fl_value_new_int(1), // client_id - build_editing_state({ - .text = "Flutter", - .selection_base = 4, - .selection_extent = 7, - }), - }); - - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingState", - FlValueEq(select_to_end)), - ::testing::_, ::testing::_, ::testing::_)); + set_client(messenger, {.client_id = 1}); + set_editing_state(messenger, { + .text = "Flutter", + .selection_base = 4, + .selection_extent = 4, + }); + + int call_count = 0; + fl_mock_binary_messenger_set_json_method_channel( + messenger, "flutter/textinput", + [](FlMockBinaryMessenger* messenger, const gchar* name, FlValue* args, + gpointer user_data) { + int* call_count = static_cast(user_data); + + EXPECT_STREQ(name, "TextInputClient.updateEditingState"); + g_autoptr(FlValue) expected_args = nullptr; + switch (*call_count) { + case 0: + // select to end + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_editing_state({ + .text = "Flutter", + .selection_base = 4, + .selection_extent = 7, + }), + }); + break; + case 1: + // select to beginning + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_editing_state({ + .text = "Flutter", + .selection_base = 4, + .selection_extent = 0, + }), + }); + break; + default: + g_assert_not_reached(); + break; + } + EXPECT_TRUE(fl_value_equal(args, expected_args)); + (*call_count)++; + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + }, + &call_count); send_key_event(handler, GDK_KEY_End, GDK_SHIFT_MASK); - - // select to beginning - g_autoptr(FlValue) select_to_beginning = build_list({ - fl_value_new_int(1), // client_id - build_editing_state({ - .text = "Flutter", - .selection_base = 4, - .selection_extent = 0, - }), - }); - - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingState", - FlValueEq(select_to_beginning)), - ::testing::_, ::testing::_, ::testing::_)); - send_key_event(handler, GDK_KEY_Home, GDK_SHIFT_MASK); + EXPECT_EQ(call_count, 2); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, Composing) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); - g_signal_emit_by_name(context, "preedit-start", nullptr); - // update EXPECT_CALL(context, gtk_im_context_get_preedit_string( @@ -612,94 +546,85 @@ TEST(FlTextInputHandlerTest, Composing) { ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter")), ::testing::SetArgPointee<3>(0))); - g_autoptr(FlValue) state = build_list({ - fl_value_new_int(-1), // client_id - build_editing_state({ - .text = "Flutter", - .selection_base = 0, - .selection_extent = 0, - .composing_base = 0, - .composing_extent = 7, - }), - }); - - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingState", - FlValueEq(state)), - ::testing::_, ::testing::_, ::testing::_)); + int call_count = 0; + fl_mock_binary_messenger_set_json_method_channel( + messenger, "flutter/textinput", + [](FlMockBinaryMessenger* messenger, const gchar* name, FlValue* args, + gpointer user_data) { + int* call_count = static_cast(user_data); + + EXPECT_STREQ(name, "TextInputClient.updateEditingState"); + g_autoptr(FlValue) expected_args = nullptr; + switch (*call_count) { + case 0: + expected_args = build_list({ + fl_value_new_int(-1), // client_id + build_editing_state({ + .text = "Flutter", + .selection_base = 0, + .selection_extent = 0, + .composing_base = 0, + .composing_extent = 7, + }), + }); + break; + case 1: + // commit + expected_args = build_list({ + fl_value_new_int(-1), // client_id + build_editing_state({ + .text = "engine", + .selection_base = 6, + .selection_extent = 6, + }), + }); + break; + case 2: + // end + expected_args = build_list({ + fl_value_new_int(-1), // client_id + build_editing_state({ + .text = "engine", + .selection_base = 6, + .selection_extent = 6, + }), + }); + break; + default: + g_assert_not_reached(); + break; + } + EXPECT_TRUE(fl_value_equal(args, expected_args)); + (*call_count)++; + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + }, + &call_count); + g_signal_emit_by_name(context, "preedit-start", nullptr); g_signal_emit_by_name(context, "preedit-changed", nullptr); - - // commit - g_autoptr(FlValue) commit = build_list({ - fl_value_new_int(-1), // client_id - build_editing_state({ - .text = "engine", - .selection_base = 6, - .selection_extent = 6, - }), - }); - - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingState", - FlValueEq(commit)), - ::testing::_, ::testing::_, ::testing::_)); - g_signal_emit_by_name(context, "commit", "engine", nullptr); - - // end - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingState", - ::testing::_), - ::testing::_, ::testing::_, ::testing::_)); - g_signal_emit_by_name(context, "preedit-end", nullptr); + EXPECT_EQ(call_count, 3); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, SurroundingText) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); - // set input config - g_autoptr(FlValue) config = build_input_config({.client_id = 1}); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr); - - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_client); - - // set editing state - g_autoptr(FlValue) state = build_editing_state({ - .text = "Flutter", - .selection_base = 3, - .selection_extent = 3, - }); - g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr); - - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_state); + set_client(messenger, {.client_id = 1}); + set_editing_state(messenger, { + .text = "Flutter", + .selection_base = 3, + .selection_extent = 3, + }); // retrieve EXPECT_CALL(context, gtk_im_context_set_surrounding( @@ -710,35 +635,53 @@ TEST(FlTextInputHandlerTest, SurroundingText) { g_signal_emit_by_name(context, "retrieve-surrounding", &retrieved, nullptr); EXPECT_TRUE(retrieved); - // delete - g_autoptr(FlValue) update = build_list({ - fl_value_new_int(1), // client_id - build_editing_state({ - .text = "Flutr", - .selection_base = 3, - .selection_extent = 3, - }), - }); - - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingState", - FlValueEq(update)), - ::testing::_, ::testing::_, ::testing::_)); + int call_count = 0; + fl_mock_binary_messenger_set_json_method_channel( + messenger, "flutter/textinput", + [](FlMockBinaryMessenger* messenger, const gchar* name, FlValue* args, + gpointer user_data) { + int* call_count = static_cast(user_data); + + EXPECT_STREQ(name, "TextInputClient.updateEditingState"); + g_autoptr(FlValue) expected_args = nullptr; + switch (*call_count) { + case 0: + // delete + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_editing_state({ + .text = "Flutr", + .selection_base = 3, + .selection_extent = 3, + }), + }); + break; + default: + g_assert_not_reached(); + break; + } + EXPECT_TRUE(fl_value_equal(args, expected_args)); + (*call_count)++; + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + }, + &call_count); gboolean deleted = false; g_signal_emit_by_name(context, "delete-surrounding", 1, 2, &deleted, nullptr); EXPECT_TRUE(deleted); + EXPECT_EQ(call_count, 1); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, SetMarkedTextRect) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); g_signal_emit_by_name(context, "preedit-start", nullptr); @@ -767,35 +710,24 @@ TEST(FlTextInputHandlerTest, SetMarkedTextRect) { }), }, }); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) set_editable_size_and_transform = - fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setEditableSizeAndTransform", - size_and_transform, nullptr); - - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", - set_editable_size_and_transform); - - // set marked text rect - g_autoptr(FlValue) rect = build_map({ - {"x", fl_value_new_float(1)}, - {"y", fl_value_new_float(2)}, - {"width", fl_value_new_float(3)}, - {"height", fl_value_new_float(4)}, - }); - g_autoptr(GBytes) set_marked_text_rect = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setMarkedTextRect", rect, nullptr); - - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); + gboolean called = FALSE; + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/textinput", "TextInput.setEditableSizeAndTransform", + size_and_transform, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + + g_autoptr(FlValue) expected_result = fl_value_new_null(); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); EXPECT_CALL(delegate, fl_text_input_view_delegate_translate_coordinates( ::testing::Eq(delegate), @@ -812,34 +744,47 @@ TEST(FlTextInputHandlerTest, SetMarkedTextRect) { ::testing::Field(&GdkRectangle::width, 0), ::testing::Field(&GdkRectangle::height, 0))))); - messenger.ReceiveMessage("flutter/textinput", set_marked_text_rect); + // set marked text rect + g_autoptr(FlValue) rect = build_map({ + {"x", fl_value_new_float(1)}, + {"y", fl_value_new_float(2)}, + {"width", fl_value_new_float(3)}, + {"height", fl_value_new_float(4)}, + }); + called = FALSE; + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/textinput", "TextInput.setMarkedTextRect", rect, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + + g_autoptr(FlValue) expected_result = fl_value_new_null(); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, TextInputTypeNone) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); - g_autoptr(FlValue) args = build_input_config({ - .client_id = 1, - .input_type = "TextInputType.none", - }); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr); - - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::A(), - SuccessResponse(null), ::testing::A())) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_client); + set_client(messenger, { + .client_id = 1, + .input_type = "TextInputType.none", + }); EXPECT_CALL(context, gtk_im_context_focus_in(::testing::Eq(context))) @@ -847,115 +792,106 @@ TEST(FlTextInputHandlerTest, TextInputTypeNone) { EXPECT_CALL(context, gtk_im_context_focus_out(::testing::Eq(context))); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); + gboolean called = FALSE; + fl_mock_binary_messenger_invoke_json_method( + messenger, "flutter/textinput", "TextInput.show", nullptr, + [](FlMockBinaryMessenger* messenger, FlMethodResponse* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); - g_autoptr(GBytes) show = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.show", nullptr, nullptr); + g_autoptr(FlValue) expected_result = fl_value_new_null(); + EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)), + expected_result)); + }, + &called); + EXPECT_TRUE(called); - messenger.ReceiveMessage("flutter/textinput", show); + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, TextEditingDelta) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); - // set config - g_autoptr(FlValue) args = build_input_config({ - .client_id = 1, - .enable_delta_model = true, - }); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr); - - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::A(), - SuccessResponse(null), ::testing::A())) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_client); - - // set editing state - g_autoptr(FlValue) state = build_editing_state({ - .text = "Flutter", - .selection_base = 7, - .selection_extent = 7, - }); - g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr); - - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::_, SuccessResponse(null), ::testing::_)) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_state); + set_client(messenger, { + .client_id = 1, + .enable_delta_model = true, + }); + set_editing_state(messenger, { + .text = "Flutter", + .selection_base = 7, + .selection_extent = 7, + }); // update editing state with deltas - g_autoptr(FlValue) deltas = build_list({ - fl_value_new_int(1), // client_id - build_map({{ - "deltas", - build_list({ - build_editing_delta({ - .old_text = "Flutter", - .delta_text = "Flutter", - .delta_start = 7, - .delta_end = 7, - .selection_base = 0, - .selection_extent = 0, - }), - }), - }}), - }); - - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingStateWithDeltas", - FlValueEq(deltas)), - ::testing::_, ::testing::_, ::testing::_)); + int call_count = 0; + fl_mock_binary_messenger_set_json_method_channel( + messenger, "flutter/textinput", + [](FlMockBinaryMessenger* messenger, const gchar* name, FlValue* args, + gpointer user_data) { + int* call_count = static_cast(user_data); + + EXPECT_STREQ(name, "TextInputClient.updateEditingStateWithDeltas"); + g_autoptr(FlValue) expected_args = nullptr; + switch (*call_count) { + case 0: + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Flutter", + .delta_text = "Flutter", + .delta_start = 7, + .delta_end = 7, + .selection_base = 0, + .selection_extent = 0, + }), + }), + }}), + }); + break; + default: + g_assert_not_reached(); + break; + } + EXPECT_TRUE(fl_value_equal(args, expected_args)); + (*call_count)++; + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + }, + &call_count); send_key_event(handler, GDK_KEY_Home); + EXPECT_EQ(call_count, 1); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, ComposingDelta) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); // set config - g_autoptr(FlValue) args = build_input_config({ - .client_id = 1, - .enable_delta_model = true, - }); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr); - - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::A(), - SuccessResponse(null), ::testing::A())) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_client); + set_client(messenger, { + .client_id = 1, + .enable_delta_model = true, + }); g_signal_emit_by_name(context, "preedit-start", nullptr); @@ -968,325 +904,283 @@ TEST(FlTextInputHandlerTest, ComposingDelta) { ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter ")), ::testing::SetArgPointee<3>(8))); - g_autoptr(FlValue) update = build_list({ - fl_value_new_int(1), // client_id - build_map({{ - "deltas", - build_list({ - build_editing_delta({ - .old_text = "", - .delta_text = "Flutter ", - .delta_start = 0, - .delta_end = 0, - .selection_base = 8, - .selection_extent = 8, - .composing_base = 0, - .composing_extent = 8, - }), - }), - }}), - }); - - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingStateWithDeltas", - FlValueEq(update)), - ::testing::_, ::testing::_, ::testing::_)); + int call_count = 0; + fl_mock_binary_messenger_set_json_method_channel( + messenger, "flutter/textinput", + [](FlMockBinaryMessenger* messenger, const gchar* name, FlValue* args, + gpointer user_data) { + int* call_count = static_cast(user_data); + + EXPECT_STREQ(name, "TextInputClient.updateEditingStateWithDeltas"); + g_autoptr(FlValue) expected_args = nullptr; + switch (*call_count) { + case 0: + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "", + .delta_text = "Flutter ", + .delta_start = 0, + .delta_end = 0, + .selection_base = 8, + .selection_extent = 8, + .composing_base = 0, + .composing_extent = 8, + }), + }), + }}), + }); + break; + case 1: + // commit + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Flutter ", + .delta_text = "Flutter engine", + .delta_start = 0, + .delta_end = 8, + .selection_base = 14, + .selection_extent = 14, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + break; + case 2: + // end + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Flutter engine", + .selection_base = 14, + .selection_extent = 14, + }), + }), + }}), + }); + break; + default: + g_assert_not_reached(); + break; + } + EXPECT_TRUE(fl_value_equal(args, expected_args)); + (*call_count)++; + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + }, + &call_count); g_signal_emit_by_name(context, "preedit-changed", nullptr); - - // commit - g_autoptr(FlValue) commit = build_list({ - fl_value_new_int(1), // client_id - build_map({{ - "deltas", - build_list({ - build_editing_delta({ - .old_text = "Flutter ", - .delta_text = "Flutter engine", - .delta_start = 0, - .delta_end = 8, - .selection_base = 14, - .selection_extent = 14, - .composing_base = -1, - .composing_extent = -1, - }), - }), - }}), - }); - - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingStateWithDeltas", - FlValueEq(commit)), - ::testing::_, ::testing::_, ::testing::_)); - g_signal_emit_by_name(context, "commit", "Flutter engine", nullptr); - - // end - g_autoptr(FlValue) end = build_list({ - fl_value_new_int(1), // client_id - build_map({{ - "deltas", - build_list({ - build_editing_delta({ - .old_text = "Flutter engine", - .selection_base = 14, - .selection_extent = 14, - }), - }), - }}), - }); - - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingStateWithDeltas", - FlValueEq(end)), - ::testing::_, ::testing::_, ::testing::_)); - g_signal_emit_by_name(context, "preedit-end", nullptr); + EXPECT_EQ(call_count, 3); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlTextInputHandlerTest, NonComposingDelta) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock context; ::testing::NiceMock delegate; - g_autoptr(FlTextInputHandler) handler = - fl_text_input_handler_new(messenger, context, delegate); + g_autoptr(FlTextInputHandler) handler = fl_text_input_handler_new( + FL_BINARY_MESSENGER(messenger), context, delegate); EXPECT_NE(handler, nullptr); // set config - g_autoptr(FlValue) args = build_input_config({ - .client_id = 1, - .enable_delta_model = true, - }); - g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); - g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call( - FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr); - - g_autoptr(FlValue) null = fl_value_new_null(); - EXPECT_CALL(messenger, fl_binary_messenger_send_response( - ::testing::Eq(messenger), - ::testing::A(), - SuccessResponse(null), ::testing::A())) - .WillOnce(::testing::Return(true)); - - messenger.ReceiveMessage("flutter/textinput", set_client); - - // commit F - g_autoptr(FlValue) commit = build_list({ - fl_value_new_int(1), // client_id - build_map({{ - "deltas", - build_list({ - build_editing_delta({ - .old_text = "", - .delta_text = "F", - .delta_start = 0, - .delta_end = 0, - .selection_base = 1, - .selection_extent = 1, - .composing_base = -1, - .composing_extent = -1, - }), - }), - }}), - }); - - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingStateWithDeltas", - FlValueEq(commit)), - ::testing::_, ::testing::_, ::testing::_)); + set_client(messenger, { + .client_id = 1, + .enable_delta_model = true, + }); + + int call_count = 0; + fl_mock_binary_messenger_set_json_method_channel( + messenger, "flutter/textinput", + [](FlMockBinaryMessenger* messenger, const gchar* name, FlValue* args, + gpointer user_data) { + int* call_count = static_cast(user_data); + + EXPECT_STREQ(name, "TextInputClient.updateEditingStateWithDeltas"); + g_autoptr(FlValue) expected_args = nullptr; + switch (*call_count) { + case 0: + // commit F + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "", + .delta_text = "F", + .delta_start = 0, + .delta_end = 0, + .selection_base = 1, + .selection_extent = 1, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + break; + case 1: + // commit l + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "F", + .delta_text = "l", + .delta_start = 1, + .delta_end = 1, + .selection_base = 2, + .selection_extent = 2, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + break; + case 2: + // commit u + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Fl", + .delta_text = "u", + .delta_start = 2, + .delta_end = 2, + .selection_base = 3, + .selection_extent = 3, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + break; + case 3: + // commit t + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Flu", + .delta_text = "t", + .delta_start = 3, + .delta_end = 3, + .selection_base = 4, + .selection_extent = 4, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + break; + case 4: + // commit t again + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Flut", + .delta_text = "t", + .delta_start = 4, + .delta_end = 4, + .selection_base = 5, + .selection_extent = 5, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + break; + case 5: + // commit e + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Flutt", + .delta_text = "e", + .delta_start = 5, + .delta_end = 5, + .selection_base = 6, + .selection_extent = 6, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + break; + case 6: + // commit r + expected_args = build_list({ + fl_value_new_int(1), // client_id + build_map({{ + "deltas", + build_list({ + build_editing_delta({ + .old_text = "Flutte", + .delta_text = "r", + .delta_start = 6, + .delta_end = 6, + .selection_base = 7, + .selection_extent = 7, + .composing_base = -1, + .composing_extent = -1, + }), + }), + }}), + }); + break; + default: + g_assert_not_reached(); + break; + } + EXPECT_TRUE(fl_value_equal(args, expected_args)); + (*call_count)++; + + return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + }, + &call_count); g_signal_emit_by_name(context, "commit", "F", nullptr); - - // commit l - g_autoptr(FlValue) commitL = build_list({ - fl_value_new_int(1), // client_id - build_map({{ - "deltas", - build_list({ - build_editing_delta({ - .old_text = "F", - .delta_text = "l", - .delta_start = 1, - .delta_end = 1, - .selection_base = 2, - .selection_extent = 2, - .composing_base = -1, - .composing_extent = -1, - }), - }), - }}), - }); - - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingStateWithDeltas", - FlValueEq(commitL)), - ::testing::_, ::testing::_, ::testing::_)); - g_signal_emit_by_name(context, "commit", "l", nullptr); - - // commit u - g_autoptr(FlValue) commitU = build_list({ - fl_value_new_int(1), // client_id - build_map({{ - "deltas", - build_list({ - build_editing_delta({ - .old_text = "Fl", - .delta_text = "u", - .delta_start = 2, - .delta_end = 2, - .selection_base = 3, - .selection_extent = 3, - .composing_base = -1, - .composing_extent = -1, - }), - }), - }}), - }); - - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingStateWithDeltas", - FlValueEq(commitU)), - ::testing::_, ::testing::_, ::testing::_)); - g_signal_emit_by_name(context, "commit", "u", nullptr); - - // commit t - g_autoptr(FlValue) commitTa = build_list({ - fl_value_new_int(1), // client_id - build_map({{ - "deltas", - build_list({ - build_editing_delta({ - .old_text = "Flu", - .delta_text = "t", - .delta_start = 3, - .delta_end = 3, - .selection_base = 4, - .selection_extent = 4, - .composing_base = -1, - .composing_extent = -1, - }), - }), - }}), - }); - - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingStateWithDeltas", - FlValueEq(commitTa)), - ::testing::_, ::testing::_, ::testing::_)); - g_signal_emit_by_name(context, "commit", "t", nullptr); - - // commit t again - g_autoptr(FlValue) commitTb = build_list({ - fl_value_new_int(1), // client_id - build_map({{ - "deltas", - build_list({ - build_editing_delta({ - .old_text = "Flut", - .delta_text = "t", - .delta_start = 4, - .delta_end = 4, - .selection_base = 5, - .selection_extent = 5, - .composing_base = -1, - .composing_extent = -1, - }), - }), - }}), - }); - - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingStateWithDeltas", - FlValueEq(commitTb)), - ::testing::_, ::testing::_, ::testing::_)); - g_signal_emit_by_name(context, "commit", "t", nullptr); - - // commit e - g_autoptr(FlValue) commitE = build_list({ - fl_value_new_int(1), // client_id - build_map({{ - "deltas", - build_list({ - build_editing_delta({ - .old_text = "Flutt", - .delta_text = "e", - .delta_start = 5, - .delta_end = 5, - .selection_base = 6, - .selection_extent = 6, - .composing_base = -1, - .composing_extent = -1, - }), - }), - }}), - }); - - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingStateWithDeltas", - FlValueEq(commitE)), - ::testing::_, ::testing::_, ::testing::_)); - g_signal_emit_by_name(context, "commit", "e", nullptr); - - // commit r - g_autoptr(FlValue) commitR = build_list({ - fl_value_new_int(1), // client_id - build_map({{ - "deltas", - build_list({ - build_editing_delta({ - .old_text = "Flutte", - .delta_text = "r", - .delta_start = 6, - .delta_end = 6, - .selection_base = 7, - .selection_extent = 7, - .composing_base = -1, - .composing_extent = -1, - }), - }), - }}), - }); - - EXPECT_CALL(messenger, - fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/textinput"), - MethodCall("TextInputClient.updateEditingStateWithDeltas", - FlValueEq(commitR)), - ::testing::_, ::testing::_, ::testing::_)); - g_signal_emit_by_name(context, "commit", "r", nullptr); + EXPECT_EQ(call_count, 7); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } diff --git a/shell/platform/linux/fl_window_state_monitor_test.cc b/shell/platform/linux/fl_window_state_monitor_test.cc index f88450741e10c..8aa6588d44ddb 100644 --- a/shell/platform/linux/fl_window_state_monitor_test.cc +++ b/shell/platform/linux/fl_window_state_monitor_test.cc @@ -3,254 +3,287 @@ // found in the LICENSE file. #include "flutter/shell/platform/linux/fl_window_state_monitor.h" +#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_string_codec.h" -#include "flutter/shell/platform/linux/testing/fl_test.h" -#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h" +#include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h" #include "flutter/shell/platform/linux/testing/mock_window.h" #include "gtest/gtest.h" -// Matches if a FlValue is a the supplied string. -class FlValueStringMatcher { - public: - using is_gtest_matcher = void; - - explicit FlValueStringMatcher(::testing::Matcher value) - : value_(std::move(value)) {} - - bool MatchAndExplain(GBytes* data, - ::testing::MatchResultListener* result_listener) const { - g_autoptr(FlStringCodec) codec = fl_string_codec_new(); - g_autoptr(GError) error = nullptr; - g_autoptr(FlValue) value = - fl_message_codec_decode_message(FL_MESSAGE_CODEC(codec), data, &error); - if (value == nullptr) { - *result_listener << ::testing::PrintToString(error->message); - return false; - } - if (!value_.MatchAndExplain(fl_value_get_string(value), result_listener)) { - *result_listener << " where the value doesn't match: \"" << value << "\""; - return false; - } - return true; - } - - void DescribeTo(std::ostream* os) const { - *os << "value "; - value_.DescribeTo(os); - } - - void DescribeNegationTo(std::ostream* os) const { - *os << "value "; - value_.DescribeNegationTo(os); - } - - private: - ::testing::Matcher value_; -}; - -::testing::Matcher LifecycleString(const std::string& value) { - return FlValueStringMatcher(::testing::StrEq(value)); -} - TEST(FlWindowStateMonitorTest, GainFocus) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock mock_window; gtk_init(0, nullptr); EXPECT_CALL(mock_window, gdk_window_get_state) .WillOnce(::testing::Return(static_cast(0))); - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/lifecycle"), - LifecycleString("AppLifecycleState.resumed"), - ::testing::_, ::testing::_, ::testing::_)); + + gboolean called = TRUE; + fl_mock_binary_messenger_set_string_message_channel( + messenger, "flutter/lifecycle", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + EXPECT_STREQ(fl_value_get_string(message), "AppLifecycleState.resumed"); + return fl_value_new_string(""); + }, + &called); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlWindowStateMonitor) monitor = - fl_window_state_monitor_new(messenger, window); + fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window); GdkEvent event = { .window_state = {.new_window_state = GDK_WINDOW_STATE_FOCUSED}}; gboolean handled; g_signal_emit_by_name(window, "window-state-event", &event, &handled); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlWindowStateMonitorTest, LoseFocus) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock mock_window; gtk_init(0, nullptr); EXPECT_CALL(mock_window, gdk_window_get_state) .WillOnce(::testing::Return(GDK_WINDOW_STATE_FOCUSED)); - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/lifecycle"), - LifecycleString("AppLifecycleState.inactive"), - ::testing::_, ::testing::_, ::testing::_)); + gboolean called = TRUE; + fl_mock_binary_messenger_set_string_message_channel( + messenger, "flutter/lifecycle", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + EXPECT_STREQ(fl_value_get_string(message), + "AppLifecycleState.inactive"); + return fl_value_new_string(""); + }, + &called); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlWindowStateMonitor) monitor = - fl_window_state_monitor_new(messenger, window); + fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window); GdkEvent event = { .window_state = {.new_window_state = static_cast(0)}}; gboolean handled; g_signal_emit_by_name(window, "window-state-event", &event, &handled); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlWindowStateMonitorTest, EnterIconified) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock mock_window; gtk_init(0, nullptr); EXPECT_CALL(mock_window, gdk_window_get_state) .WillOnce(::testing::Return(static_cast(0))); - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/lifecycle"), - LifecycleString("AppLifecycleState.hidden"), - ::testing::_, ::testing::_, ::testing::_)); + gboolean called = TRUE; + fl_mock_binary_messenger_set_string_message_channel( + messenger, "flutter/lifecycle", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + EXPECT_STREQ(fl_value_get_string(message), "AppLifecycleState.hidden"); + return fl_value_new_string(""); + }, + &called); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlWindowStateMonitor) monitor = - fl_window_state_monitor_new(messenger, window); + fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window); GdkEvent event = { .window_state = {.new_window_state = GDK_WINDOW_STATE_ICONIFIED}}; gboolean handled; g_signal_emit_by_name(window, "window-state-event", &event, &handled); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlWindowStateMonitorTest, LeaveIconified) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock mock_window; gtk_init(0, nullptr); EXPECT_CALL(mock_window, gdk_window_get_state) .WillOnce(::testing::Return(GDK_WINDOW_STATE_ICONIFIED)); - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/lifecycle"), - LifecycleString("AppLifecycleState.inactive"), - ::testing::_, ::testing::_, ::testing::_)); + gboolean called = TRUE; + fl_mock_binary_messenger_set_string_message_channel( + messenger, "flutter/lifecycle", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + EXPECT_STREQ(fl_value_get_string(message), + "AppLifecycleState.inactive"); + return fl_value_new_string(""); + }, + &called); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlWindowStateMonitor) monitor = - fl_window_state_monitor_new(messenger, window); + fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window); GdkEvent event = { .window_state = {.new_window_state = static_cast(0)}}; gboolean handled; g_signal_emit_by_name(window, "window-state-event", &event, &handled); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlWindowStateMonitorTest, LeaveIconifiedFocused) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock mock_window; gtk_init(0, nullptr); EXPECT_CALL(mock_window, gdk_window_get_state) .WillOnce(::testing::Return(GDK_WINDOW_STATE_ICONIFIED)); - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/lifecycle"), - LifecycleString("AppLifecycleState.resumed"), - ::testing::_, ::testing::_, ::testing::_)); + gboolean called = TRUE; + fl_mock_binary_messenger_set_string_message_channel( + messenger, "flutter/lifecycle", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + EXPECT_STREQ(fl_value_get_string(message), "AppLifecycleState.resumed"); + return fl_value_new_string(""); + }, + &called); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlWindowStateMonitor) monitor = - fl_window_state_monitor_new(messenger, window); + fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window); GdkEvent event = { .window_state = {.new_window_state = static_cast( GDK_WINDOW_STATE_FOCUSED)}}; gboolean handled; g_signal_emit_by_name(window, "window-state-event", &event, &handled); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlWindowStateMonitorTest, EnterWithdrawn) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock mock_window; gtk_init(0, nullptr); EXPECT_CALL(mock_window, gdk_window_get_state) .WillOnce(::testing::Return(static_cast(0))); - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/lifecycle"), - LifecycleString("AppLifecycleState.hidden"), - ::testing::_, ::testing::_, ::testing::_)); + gboolean called = TRUE; + fl_mock_binary_messenger_set_string_message_channel( + messenger, "flutter/lifecycle", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + EXPECT_STREQ(fl_value_get_string(message), "AppLifecycleState.hidden"); + return fl_value_new_string(""); + }, + &called); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlWindowStateMonitor) monitor = - fl_window_state_monitor_new(messenger, window); + fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window); GdkEvent event = { .window_state = {.new_window_state = GDK_WINDOW_STATE_WITHDRAWN}}; gboolean handled; g_signal_emit_by_name(window, "window-state-event", &event, &handled); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlWindowStateMonitorTest, LeaveWithdrawn) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock mock_window; gtk_init(0, nullptr); EXPECT_CALL(mock_window, gdk_window_get_state) .WillOnce(::testing::Return(GDK_WINDOW_STATE_WITHDRAWN)); - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/lifecycle"), - LifecycleString("AppLifecycleState.inactive"), - ::testing::_, ::testing::_, ::testing::_)); + gboolean called = TRUE; + fl_mock_binary_messenger_set_string_message_channel( + messenger, "flutter/lifecycle", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + EXPECT_STREQ(fl_value_get_string(message), + "AppLifecycleState.inactive"); + return fl_value_new_string(""); + }, + &called); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlWindowStateMonitor) monitor = - fl_window_state_monitor_new(messenger, window); + fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window); GdkEvent event = { .window_state = {.new_window_state = static_cast(0)}}; gboolean handled; g_signal_emit_by_name(window, "window-state-event", &event, &handled); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } TEST(FlWindowStateMonitorTest, LeaveWithdrawnFocused) { - ::testing::NiceMock messenger; + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); ::testing::NiceMock mock_window; gtk_init(0, nullptr); EXPECT_CALL(mock_window, gdk_window_get_state) .WillOnce(::testing::Return(GDK_WINDOW_STATE_WITHDRAWN)); - EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( - ::testing::Eq(messenger), - ::testing::StrEq("flutter/lifecycle"), - LifecycleString("AppLifecycleState.resumed"), - ::testing::_, ::testing::_, ::testing::_)); + gboolean called = TRUE; + fl_mock_binary_messenger_set_string_message_channel( + messenger, "flutter/lifecycle", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + EXPECT_STREQ(fl_value_get_string(message), "AppLifecycleState.resumed"); + return fl_value_new_string(""); + }, + &called); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlWindowStateMonitor) monitor = - fl_window_state_monitor_new(messenger, window); + fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window); GdkEvent event = { .window_state = {.new_window_state = static_cast( GDK_WINDOW_STATE_FOCUSED)}}; gboolean handled; g_signal_emit_by_name(window, "window-state-event", &event, &handled); + EXPECT_TRUE(called); + + fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger)); } diff --git a/shell/platform/linux/testing/fl_mock_binary_messenger.cc b/shell/platform/linux/testing/fl_mock_binary_messenger.cc new file mode 100644 index 0000000000000..e26f9bcfcbb49 --- /dev/null +++ b/shell/platform/linux/testing/fl_mock_binary_messenger.cc @@ -0,0 +1,609 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h" + +#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" +#include "flutter/shell/platform/linux/fl_method_codec_private.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_string_codec.h" + +G_DECLARE_FINAL_TYPE(FlMockBinaryMessengerResponseHandle, + fl_mock_binary_messenger_response_handle, + FL, + MOCK_BINARY_MESSENGER_RESPONSE_HANDLE, + FlBinaryMessengerResponseHandle) + +struct _FlMockBinaryMessengerResponseHandle { + FlBinaryMessengerResponseHandle parent_instance; + + FlMockBinaryMessengerCallback callback; + gpointer user_data; +}; + +G_DEFINE_TYPE(FlMockBinaryMessengerResponseHandle, + fl_mock_binary_messenger_response_handle, + fl_binary_messenger_response_handle_get_type()) + +static void fl_mock_binary_messenger_response_handle_class_init( + FlMockBinaryMessengerResponseHandleClass* klass) {} + +static void fl_mock_binary_messenger_response_handle_init( + FlMockBinaryMessengerResponseHandle* self) {} + +FlMockBinaryMessengerResponseHandle* +fl_mock_binary_messenger_response_handle_new( + FlMockBinaryMessengerCallback callback, + gpointer user_data) { + FlMockBinaryMessengerResponseHandle* self = + FL_MOCK_BINARY_MESSENGER_RESPONSE_HANDLE(g_object_new( + fl_mock_binary_messenger_response_handle_get_type(), nullptr)); + self->callback = callback; + self->user_data = user_data; + return self; +} + +struct _FlMockBinaryMessenger { + GObject parent_instance; + + // Handlers the embedder has registered. + GHashTable* handlers; + + // Mocked Dart channels. + GHashTable* mock_channels; + GHashTable* mock_message_channels; + GHashTable* mock_method_channels; +}; + +typedef struct { + FlMockBinaryMessengerChannelHandler callback; + gpointer user_data; +} MockChannel; + +static MockChannel* mock_channel_new( + FlMockBinaryMessengerChannelHandler callback, + gpointer user_data) { + MockChannel* channel = g_new0(MockChannel, 1); + channel->callback = callback; + channel->user_data = user_data; + return channel; +} + +static void mock_channel_free(MockChannel* channel) { + g_free(channel); +} + +typedef struct { + FlMessageCodec* codec; + FlMockBinaryMessengerMessageChannelHandler callback; + gpointer user_data; +} MockMessageChannel; + +static MockMessageChannel* mock_message_channel_new( + FlMockBinaryMessengerMessageChannelHandler callback, + FlMessageCodec* codec, + gpointer user_data) { + MockMessageChannel* channel = g_new0(MockMessageChannel, 1); + channel->codec = FL_MESSAGE_CODEC(g_object_ref(codec)); + channel->callback = callback; + channel->user_data = user_data; + return channel; +} + +static void mock_message_channel_free(MockMessageChannel* channel) { + g_object_unref(channel->codec); + g_free(channel); +} + +typedef struct { + FlMethodCodec* codec; + FlMockBinaryMessengerMethodChannelHandler callback; + gpointer user_data; +} MockMethodChannel; + +static MockMethodChannel* mock_method_channel_new( + FlMockBinaryMessengerMethodChannelHandler callback, + FlMethodCodec* codec, + gpointer user_data) { + MockMethodChannel* channel = g_new0(MockMethodChannel, 1); + channel->codec = FL_METHOD_CODEC(g_object_ref(codec)); + channel->callback = callback; + channel->user_data = user_data; + return channel; +} + +static void mock_method_channel_free(MockMethodChannel* channel) { + g_object_unref(channel->codec); + g_free(channel); +} + +typedef struct { + FlBinaryMessengerMessageHandler callback; + gpointer user_data; + GDestroyNotify destroy_notify; +} Handler; + +static Handler* handler_new(FlBinaryMessengerMessageHandler callback, + gpointer user_data, + GDestroyNotify destroy_notify) { + Handler* handler = g_new0(Handler, 1); + handler->callback = callback; + handler->user_data = user_data; + handler->destroy_notify = destroy_notify; + return handler; +} + +static void handler_free(Handler* handler) { + if (handler->destroy_notify) { + handler->destroy_notify(handler->user_data); + } + g_free(handler); +} + +static void fl_mock_binary_messenger_iface_init( + FlBinaryMessengerInterface* iface); + +G_DEFINE_TYPE_WITH_CODE( + FlMockBinaryMessenger, + fl_mock_binary_messenger, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(fl_binary_messenger_get_type(), + fl_mock_binary_messenger_iface_init)) + +static void fl_mock_binary_messenger_set_message_handler_on_channel( + FlBinaryMessenger* messenger, + const gchar* channel, + FlBinaryMessengerMessageHandler handler, + gpointer user_data, + GDestroyNotify destroy_notify) { + FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(messenger); + g_hash_table_insert(self->handlers, g_strdup(channel), + handler_new(handler, user_data, destroy_notify)); +} + +static gboolean fl_mock_binary_messenger_send_response( + FlBinaryMessenger* messenger, + FlBinaryMessengerResponseHandle* response_handle, + GBytes* response, + GError** error) { + FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(messenger); + + g_return_val_if_fail( + FL_IS_MOCK_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle), FALSE); + FlMockBinaryMessengerResponseHandle* handle = + FL_MOCK_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle); + + handle->callback(self, response, handle->user_data); + + return TRUE; +} + +static void fl_mock_binary_messenger_send_on_channel( + FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + GCancellable* cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(messenger); + g_autoptr(GTask) task = g_task_new(self, cancellable, callback, user_data); + + MockChannel* mock_channel = static_cast( + g_hash_table_lookup(self->mock_channels, channel)); + MockMessageChannel* mock_message_channel = static_cast( + g_hash_table_lookup(self->mock_message_channels, channel)); + MockMethodChannel* mock_method_channel = static_cast( + g_hash_table_lookup(self->mock_method_channels, channel)); + g_autoptr(GBytes) response = nullptr; + if (mock_channel != nullptr) { + response = mock_channel->callback(self, message, mock_channel->user_data); + } else if (mock_message_channel != nullptr) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) message_value = fl_message_codec_decode_message( + mock_message_channel->codec, message, &error); + if (message_value == nullptr) { + g_warning("Failed to decode message: %s", error->message); + } else { + g_autoptr(FlValue) response_value = mock_message_channel->callback( + self, message_value, mock_message_channel->user_data); + response = fl_message_codec_encode_message(mock_message_channel->codec, + response_value, &error); + if (response == nullptr) { + g_warning("Failed to encode message: %s", error->message); + } + } + } else if (mock_method_channel != nullptr) { + g_autofree gchar* name = nullptr; + g_autoptr(FlValue) args = nullptr; + g_autoptr(GError) error = nullptr; + if (!fl_method_codec_decode_method_call(mock_method_channel->codec, message, + &name, &args, &error)) { + g_warning("Failed to decode method call: %s", error->message); + } else { + g_autoptr(FlMethodResponse) response_value = + mock_method_channel->callback(self, name, args, + mock_method_channel->user_data); + response = fl_method_codec_encode_response(mock_method_channel->codec, + response_value, &error); + if (response == nullptr) { + g_warning("Failed to encode method response: %s", error->message); + } + } + } + + if (response == nullptr) { + response = g_bytes_new(nullptr, 0); + } + + g_task_return_pointer(task, g_bytes_ref(response), + reinterpret_cast(g_bytes_unref)); +} + +static GBytes* fl_mock_binary_messenger_send_on_channel_finish( + FlBinaryMessenger* messenger, + GAsyncResult* result, + GError** error) { + return static_cast(g_task_propagate_pointer(G_TASK(result), error)); +} + +static void fl_mock_binary_messenger_resize_channel( + FlBinaryMessenger* messenger, + const gchar* channel, + int64_t new_size) {} + +static void fl_mock_binary_messenger_set_warns_on_channel_overflow( + FlBinaryMessenger* messenger, + const gchar* channel, + bool warns) {} + +static void fl_mock_binary_messenger_shutdown(FlBinaryMessenger* messenger) { + FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(messenger); + g_hash_table_remove_all(self->handlers); +} + +static void fl_mock_binary_messenger_dispose(GObject* object) { + FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(object); + + g_clear_pointer(&self->mock_channels, g_hash_table_unref); + g_clear_pointer(&self->mock_message_channels, g_hash_table_unref); + g_clear_pointer(&self->mock_method_channels, g_hash_table_unref); + + G_OBJECT_CLASS(fl_mock_binary_messenger_parent_class)->dispose(object); +} + +static void fl_mock_binary_messenger_class_init( + FlMockBinaryMessengerClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_mock_binary_messenger_dispose; +} + +static void fl_mock_binary_messenger_iface_init( + FlBinaryMessengerInterface* iface) { + iface->set_message_handler_on_channel = + fl_mock_binary_messenger_set_message_handler_on_channel; + iface->send_response = fl_mock_binary_messenger_send_response; + iface->send_on_channel = fl_mock_binary_messenger_send_on_channel; + iface->send_on_channel_finish = + fl_mock_binary_messenger_send_on_channel_finish; + iface->resize_channel = fl_mock_binary_messenger_resize_channel; + iface->set_warns_on_channel_overflow = + fl_mock_binary_messenger_set_warns_on_channel_overflow; + iface->shutdown = fl_mock_binary_messenger_shutdown; +} + +static void fl_mock_binary_messenger_init(FlMockBinaryMessenger* self) { + self->handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)handler_free); + + self->mock_channels = g_hash_table_new_full( + g_str_hash, g_str_equal, g_free, (GDestroyNotify)mock_channel_free); + self->mock_message_channels = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)mock_message_channel_free); + self->mock_method_channels = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)mock_method_channel_free); +} + +FlMockBinaryMessenger* fl_mock_binary_messenger_new() { + FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER( + g_object_new(fl_mock_binary_messenger_get_type(), nullptr)); + return self; +} + +gboolean fl_mock_binary_messenger_has_handler(FlMockBinaryMessenger* self, + const gchar* channel) { + g_return_val_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self), FALSE); + return g_hash_table_lookup(self->handlers, channel) != nullptr; +} + +void fl_mock_binary_messenger_set_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerChannelHandler handler, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + + g_hash_table_insert(self->mock_channels, g_strdup(channel), + mock_channel_new(handler, user_data)); +} + +void fl_mock_binary_messenger_set_message_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMessageCodec* codec, + FlMockBinaryMessengerMessageChannelHandler handler, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + + g_hash_table_insert(self->mock_message_channels, g_strdup(channel), + mock_message_channel_new(handler, codec, user_data)); +} + +void fl_mock_binary_messenger_set_standard_message_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerMessageChannelHandler handler, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); + return fl_mock_binary_messenger_set_message_channel( + self, channel, FL_MESSAGE_CODEC(codec), handler, user_data); +} + +void fl_mock_binary_messenger_set_string_message_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerMessageChannelHandler handler, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + + g_autoptr(FlStringCodec) codec = fl_string_codec_new(); + return fl_mock_binary_messenger_set_message_channel( + self, channel, FL_MESSAGE_CODEC(codec), handler, user_data); +} + +void fl_mock_binary_messenger_set_json_message_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerMessageChannelHandler handler, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + + g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new(); + return fl_mock_binary_messenger_set_message_channel( + self, channel, FL_MESSAGE_CODEC(codec), handler, user_data); +} + +void fl_mock_binary_messenger_set_method_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMethodCodec* codec, + FlMockBinaryMessengerMethodChannelHandler handler, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + + g_hash_table_insert(self->mock_method_channels, g_strdup(channel), + mock_method_channel_new(handler, codec, user_data)); +} + +void fl_mock_binary_messenger_set_standard_method_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerMethodChannelHandler handler, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + fl_mock_binary_messenger_set_method_channel( + self, channel, FL_METHOD_CODEC(codec), handler, user_data); +} + +void fl_mock_binary_messenger_set_json_method_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerMethodChannelHandler handler, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); + fl_mock_binary_messenger_set_method_channel( + self, channel, FL_METHOD_CODEC(codec), handler, user_data); +} + +void fl_mock_binary_messenger_send(FlMockBinaryMessenger* self, + const gchar* channel, + GBytes* message, + FlMockBinaryMessengerCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + + Handler* handler = + static_cast(g_hash_table_lookup(self->handlers, channel)); + if (handler == nullptr) { + return; + } + + handler->callback( + FL_BINARY_MESSENGER(self), channel, message, + FL_BINARY_MESSENGER_RESPONSE_HANDLE( + fl_mock_binary_messenger_response_handle_new(callback, user_data)), + handler->user_data); +} + +typedef struct { + FlMessageCodec* codec; + FlMockBinaryMessengerMessageCallback callback; + gpointer user_data; +} SendMessageData; + +static SendMessageData* send_message_data_new( + FlMessageCodec* codec, + FlMockBinaryMessengerMessageCallback callback, + gpointer user_data) { + SendMessageData* data = g_new0(SendMessageData, 1); + data->codec = FL_MESSAGE_CODEC(g_object_ref(codec)); + data->callback = callback; + data->user_data = user_data; + return data; +} + +static void send_message_data_free(SendMessageData* data) { + g_object_unref(data->codec); + free(data); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(SendMessageData, send_message_data_free) + +static void send_message_cb(FlMockBinaryMessenger* self, + GBytes* response, + gpointer user_data) { + g_autoptr(SendMessageData) data = static_cast(user_data); + + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) response_value = + fl_message_codec_decode_message(data->codec, response, &error); + if (response_value == nullptr) { + g_warning("Failed to decode message response: %s", error->message); + return; + } + + data->callback(self, response_value, data->user_data); +} + +void fl_mock_binary_messenger_send_message( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMessageCodec* codec, + FlValue* message, + FlMockBinaryMessengerMessageCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + + g_autoptr(GError) error = nullptr; + g_autoptr(GBytes) encoded_message = + fl_message_codec_encode_message(codec, message, &error); + if (encoded_message == nullptr) { + g_warning("Failed to encode message: %s", error->message); + return; + } + + fl_mock_binary_messenger_send( + self, channel, encoded_message, send_message_cb, + send_message_data_new(codec, callback, user_data)); +} + +void fl_mock_binary_messenger_send_standard_message( + FlMockBinaryMessenger* self, + const gchar* channel, + FlValue* message, + FlMockBinaryMessengerMessageCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); + fl_mock_binary_messenger_send_message(self, channel, FL_MESSAGE_CODEC(codec), + message, callback, user_data); +} + +void fl_mock_binary_messenger_send_json_message( + FlMockBinaryMessenger* self, + const gchar* channel, + FlValue* message, + FlMockBinaryMessengerMessageCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new(); + fl_mock_binary_messenger_send_message(self, channel, FL_MESSAGE_CODEC(codec), + message, callback, user_data); +} + +typedef struct { + FlMethodCodec* codec; + FlMockBinaryMessengerMethodCallback callback; + gpointer user_data; +} InvokeMethodData; + +static InvokeMethodData* invoke_method_data_new( + FlMethodCodec* codec, + FlMockBinaryMessengerMethodCallback callback, + gpointer user_data) { + InvokeMethodData* data = g_new0(InvokeMethodData, 1); + data->codec = FL_METHOD_CODEC(g_object_ref(codec)); + data->callback = callback; + data->user_data = user_data; + return data; +} + +static void invoke_method_data_free(InvokeMethodData* data) { + g_object_unref(data->codec); + free(data); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(InvokeMethodData, invoke_method_data_free) + +static void invoke_method_cb(FlMockBinaryMessenger* self, + GBytes* response, + gpointer user_data) { + g_autoptr(InvokeMethodData) data = static_cast(user_data); + + g_autoptr(GError) error = nullptr; + g_autoptr(FlMethodResponse) method_response = + fl_method_codec_decode_response(data->codec, response, &error); + if (method_response == nullptr) { + g_warning("Failed to decode method response: %s", error->message); + return; + } + + data->callback(self, method_response, data->user_data); +} + +void fl_mock_binary_messenger_invoke_method( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMethodCodec* codec, + const char* name, + FlValue* args, + FlMockBinaryMessengerMethodCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + + g_autoptr(GError) error = nullptr; + g_autoptr(GBytes) message = + fl_method_codec_encode_method_call(codec, name, args, &error); + if (message == nullptr) { + g_warning("Failed to encode method call: %s", error->message); + return; + } + + fl_mock_binary_messenger_send( + self, channel, message, invoke_method_cb, + invoke_method_data_new(codec, callback, user_data)); +} + +void fl_mock_binary_messenger_invoke_standard_method( + FlMockBinaryMessenger* self, + const gchar* channel, + const char* name, + FlValue* args, + FlMockBinaryMessengerMethodCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + fl_mock_binary_messenger_invoke_method(self, channel, FL_METHOD_CODEC(codec), + name, args, callback, user_data); +} + +void fl_mock_binary_messenger_invoke_json_method( + FlMockBinaryMessenger* self, + const gchar* channel, + const char* name, + FlValue* args, + FlMockBinaryMessengerMethodCallback callback, + gpointer user_data) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); + fl_mock_binary_messenger_invoke_method(self, channel, FL_METHOD_CODEC(codec), + name, args, callback, user_data); +} diff --git a/shell/platform/linux/testing/fl_mock_binary_messenger.h b/shell/platform/linux/testing/fl_mock_binary_messenger.h new file mode 100644 index 0000000000000..ab6276e2917e3 --- /dev/null +++ b/shell/platform/linux/testing/fl_mock_binary_messenger.h @@ -0,0 +1,160 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_TESTING_FL_MOCK_BINARY_MESSENGER_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_TESTING_FL_MOCK_BINARY_MESSENGER_H_ + +#include "flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_value.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlMockBinaryMessenger, + fl_mock_binary_messenger, + FL, + MOCK_BINARY_MESSENGER, + GObject) + +typedef GBytes* (*FlMockBinaryMessengerChannelHandler)( + FlMockBinaryMessenger* messenger, + GBytes* message, + gpointer user_data); + +typedef FlValue* (*FlMockBinaryMessengerMessageChannelHandler)( + FlMockBinaryMessenger* messenger, + FlValue* message, + gpointer user_data); + +typedef FlMethodResponse* (*FlMockBinaryMessengerMethodChannelHandler)( + FlMockBinaryMessenger* messenger, + const gchar* name, + FlValue* args, + gpointer user_data); + +typedef void (*FlMockBinaryMessengerCallback)(FlMockBinaryMessenger* messenger, + GBytes* response, + gpointer user_data); + +typedef void (*FlMockBinaryMessengerMessageCallback)( + FlMockBinaryMessenger* messenger, + FlValue* response, + gpointer user_data); + +typedef void (*FlMockBinaryMessengerMethodCallback)( + FlMockBinaryMessenger* messenger, + FlMethodResponse* response, + gpointer user_data); + +FlMockBinaryMessenger* fl_mock_binary_messenger_new(); + +gboolean fl_mock_binary_messenger_has_handler(FlMockBinaryMessenger* self, + const gchar* channel); + +void fl_mock_binary_messenger_set_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerChannelHandler handler, + gpointer user_data); + +void fl_mock_binary_messenger_set_message_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMessageCodec* codec, + FlMockBinaryMessengerMessageChannelHandler handler, + gpointer user_data); + +void fl_mock_binary_messenger_set_standard_message_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerMessageChannelHandler handler, + gpointer user_data); + +void fl_mock_binary_messenger_set_string_message_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerMessageChannelHandler handler, + gpointer user_data); + +void fl_mock_binary_messenger_set_json_message_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerMessageChannelHandler handler, + gpointer user_data); + +void fl_mock_binary_messenger_set_method_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMethodCodec* codec, + FlMockBinaryMessengerMethodChannelHandler handler, + gpointer user_data); + +void fl_mock_binary_messenger_set_standard_method_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerMethodChannelHandler handler, + gpointer user_data); + +void fl_mock_binary_messenger_set_json_method_channel( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMockBinaryMessengerMethodChannelHandler handler, + gpointer user_data); + +void fl_mock_binary_messenger_send(FlMockBinaryMessenger* self, + const gchar* channel, + GBytes* message, + FlMockBinaryMessengerCallback callback, + gpointer user_data); + +void fl_mock_binary_messenger_send_message( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMessageCodec* codec, + FlValue* message, + FlMockBinaryMessengerMessageCallback callback, + gpointer user_data); + +void fl_mock_binary_messenger_send_standard_message( + FlMockBinaryMessenger* self, + const gchar* channel, + FlValue* message, + FlMockBinaryMessengerMessageCallback callback, + gpointer user_data); + +void fl_mock_binary_messenger_send_json_message( + FlMockBinaryMessenger* self, + const gchar* channel, + FlValue* message, + FlMockBinaryMessengerMessageCallback callback, + gpointer user_data); + +void fl_mock_binary_messenger_invoke_method( + FlMockBinaryMessenger* self, + const gchar* channel, + FlMethodCodec* codec, + const char* name, + FlValue* args, + FlMockBinaryMessengerMethodCallback callback, + gpointer user_data); + +void fl_mock_binary_messenger_invoke_standard_method( + FlMockBinaryMessenger* self, + const gchar* channel, + const char* name, + FlValue* args, + FlMockBinaryMessengerMethodCallback callback, + gpointer user_data); + +void fl_mock_binary_messenger_invoke_json_method( + FlMockBinaryMessenger* self, + const gchar* channel, + const char* name, + FlValue* args, + FlMockBinaryMessengerMethodCallback callback, + gpointer user_data); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_TESTING_FL_MOCK_BINARY_MESSENGER_H_ diff --git a/shell/platform/linux/testing/mock_binary_messenger.cc b/shell/platform/linux/testing/mock_binary_messenger.cc deleted file mode 100644 index 8b22f2291bd75..0000000000000 --- a/shell/platform/linux/testing/mock_binary_messenger.cc +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h" -#include "flutter/shell/platform/linux/testing/mock_binary_messenger_response_handle.h" - -using namespace flutter::testing; - -G_DECLARE_FINAL_TYPE(FlMockBinaryMessenger, - fl_mock_binary_messenger, - FL, - MOCK_BINARY_MESSENGER, - GObject) - -struct _FlMockBinaryMessenger { - GObject parent_instance; - MockBinaryMessenger* mock; -}; - -static FlBinaryMessenger* fl_mock_binary_messenger_new( - MockBinaryMessenger* mock) { - FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER( - g_object_new(fl_mock_binary_messenger_get_type(), nullptr)); - self->mock = mock; - return FL_BINARY_MESSENGER(self); -} - -MockBinaryMessenger::MockBinaryMessenger() - : instance_(fl_mock_binary_messenger_new(this)) {} - -MockBinaryMessenger::~MockBinaryMessenger() { - if (FL_IS_BINARY_MESSENGER(instance_)) { - g_clear_object(&instance_); - } -} - -MockBinaryMessenger::operator FlBinaryMessenger*() { - return instance_; -} - -bool MockBinaryMessenger::HasMessageHandler(const gchar* channel) const { - return message_handlers_.at(channel) != nullptr; -} - -void MockBinaryMessenger::SetMessageHandler( - const gchar* channel, - FlBinaryMessengerMessageHandler handler, - gpointer user_data) { - message_handlers_[channel] = handler; - user_datas_[channel] = user_data; -} - -void MockBinaryMessenger::ReceiveMessage(const gchar* channel, - GBytes* message) { - FlBinaryMessengerMessageHandler handler = message_handlers_[channel]; - if (response_handles_[channel] == nullptr) { - response_handles_[channel] = FL_BINARY_MESSENGER_RESPONSE_HANDLE( - fl_mock_binary_messenger_response_handle_new()); - } - handler(instance_, channel, message, response_handles_[channel], - user_datas_[channel]); -} - -static void fl_mock_binary_messenger_iface_init( - FlBinaryMessengerInterface* iface); - -G_DEFINE_TYPE_WITH_CODE( - FlMockBinaryMessenger, - fl_mock_binary_messenger, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(fl_binary_messenger_get_type(), - fl_mock_binary_messenger_iface_init)) - -static void fl_mock_binary_messenger_class_init( - FlMockBinaryMessengerClass* klass) {} - -static void fl_mock_binary_messenger_set_message_handler_on_channel( - FlBinaryMessenger* messenger, - const gchar* channel, - FlBinaryMessengerMessageHandler handler, - gpointer user_data, - GDestroyNotify destroy_notify) { - g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(messenger)); - FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(messenger); - self->mock->SetMessageHandler(channel, handler, user_data); - self->mock->fl_binary_messenger_set_message_handler_on_channel( - messenger, channel, handler, user_data, destroy_notify); -} - -static gboolean fl_mock_binary_messenger_send_response( - FlBinaryMessenger* messenger, - FlBinaryMessengerResponseHandle* response_handle, - GBytes* response, - GError** error) { - g_return_val_if_fail(FL_IS_MOCK_BINARY_MESSENGER(messenger), false); - FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(messenger); - return self->mock->fl_binary_messenger_send_response( - messenger, response_handle, response, error); -} - -static void fl_mock_binary_messenger_send_on_channel( - FlBinaryMessenger* messenger, - const gchar* channel, - GBytes* message, - GCancellable* cancellable, - GAsyncReadyCallback callback, - gpointer user_data) { - g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(messenger)); - FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(messenger); - self->mock->fl_binary_messenger_send_on_channel( - messenger, channel, message, cancellable, callback, user_data); -} - -static GBytes* fl_mock_binary_messenger_send_on_channel_finish( - FlBinaryMessenger* messenger, - GAsyncResult* result, - GError** error) { - g_return_val_if_fail(FL_IS_MOCK_BINARY_MESSENGER(messenger), nullptr); - FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(messenger); - return self->mock->fl_binary_messenger_send_on_channel_finish(messenger, - result, error); -} - -static void fl_mock_binary_messenger_resize_channel( - FlBinaryMessenger* messenger, - const gchar* channel, - int64_t new_size) { - g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(messenger)); - FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(messenger); - self->mock->fl_binary_messenger_resize_channel(messenger, channel, new_size); -} - -static void fl_mock_binary_messenger_set_warns_on_channel_overflow( - FlBinaryMessenger* messenger, - const gchar* channel, - bool warns) { - g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(messenger)); - FlMockBinaryMessenger* self = FL_MOCK_BINARY_MESSENGER(messenger); - self->mock->fl_binary_messenger_set_warns_on_channel_overflow(messenger, - channel, warns); -} - -static void fl_mock_binary_messenger_shutdown(FlBinaryMessenger* messenger) {} - -static void fl_mock_binary_messenger_iface_init( - FlBinaryMessengerInterface* iface) { - iface->set_message_handler_on_channel = - fl_mock_binary_messenger_set_message_handler_on_channel; - iface->send_response = fl_mock_binary_messenger_send_response; - iface->send_on_channel = fl_mock_binary_messenger_send_on_channel; - iface->send_on_channel_finish = - fl_mock_binary_messenger_send_on_channel_finish; - iface->resize_channel = fl_mock_binary_messenger_resize_channel; - iface->set_warns_on_channel_overflow = - fl_mock_binary_messenger_set_warns_on_channel_overflow; - iface->shutdown = fl_mock_binary_messenger_shutdown; -} - -static void fl_mock_binary_messenger_init(FlMockBinaryMessenger* self) {} diff --git a/shell/platform/linux/testing/mock_binary_messenger.h b/shell/platform/linux/testing/mock_binary_messenger.h deleted file mode 100644 index 394e55882ed2d..0000000000000 --- a/shell/platform/linux/testing/mock_binary_messenger.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_BINARY_MESSENGER_H_ -#define FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_BINARY_MESSENGER_H_ - -#include - -#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" - -#include "gmock/gmock.h" - -namespace flutter { -namespace testing { - -// Mock for FlBinaryMessenger. -class MockBinaryMessenger { - public: - MockBinaryMessenger(); - ~MockBinaryMessenger(); - - // This was an existing use of operator overloading. It's against our style - // guide but enabling clang tidy on header files is a higher priority than - // fixing this. - // NOLINTNEXTLINE(google-explicit-constructor) - operator FlBinaryMessenger*(); - - MOCK_METHOD(void, - fl_binary_messenger_set_message_handler_on_channel, - (FlBinaryMessenger * messenger, - const gchar* channel, - FlBinaryMessengerMessageHandler handler, - gpointer user_data, - GDestroyNotify destroy_notify)); - - MOCK_METHOD(gboolean, - fl_binary_messenger_send_response, - (FlBinaryMessenger * messenger, - FlBinaryMessengerResponseHandle* response_handle, - GBytes* response, - GError** error)); - - MOCK_METHOD(void, - fl_binary_messenger_send_on_channel, - (FlBinaryMessenger * messenger, - const gchar* channel, - GBytes* message, - GCancellable* cancellable, - GAsyncReadyCallback callback, - gpointer user_data)); - - MOCK_METHOD(GBytes*, - fl_binary_messenger_send_on_channel_finish, - (FlBinaryMessenger * messenger, - GAsyncResult* result, - GError** error)); - - MOCK_METHOD(void, - fl_binary_messenger_resize_channel, - (FlBinaryMessenger * messenger, - const gchar* channel, - int64_t new_size)); - - MOCK_METHOD(void, - fl_binary_messenger_set_warns_on_channel_overflow, - (FlBinaryMessenger * messenger, - const gchar* channel, - bool warns)); - - bool HasMessageHandler(const gchar* channel) const; - - void SetMessageHandler(const gchar* channel, - FlBinaryMessengerMessageHandler handler, - gpointer user_data); - - void ReceiveMessage(const gchar* channel, GBytes* message); - - private: - FlBinaryMessenger* instance_ = nullptr; - std::unordered_map - message_handlers_; - std::unordered_map - response_handles_; - std::unordered_map user_datas_; -}; - -} // namespace testing -} // namespace flutter - -#endif // FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_BINARY_MESSENGER_H_ diff --git a/shell/platform/linux/testing/mock_binary_messenger_response_handle.cc b/shell/platform/linux/testing/mock_binary_messenger_response_handle.cc deleted file mode 100644 index 4bd7e419d2071..0000000000000 --- a/shell/platform/linux/testing/mock_binary_messenger_response_handle.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/linux/testing/mock_binary_messenger_response_handle.h" - -struct _FlMockBinaryMessengerResponseHandle { - FlBinaryMessengerResponseHandle parent_instance; -}; - -G_DEFINE_TYPE(FlMockBinaryMessengerResponseHandle, - fl_mock_binary_messenger_response_handle, - fl_binary_messenger_response_handle_get_type()); - -static void fl_mock_binary_messenger_response_handle_class_init( - FlMockBinaryMessengerResponseHandleClass* klass) {} - -static void fl_mock_binary_messenger_response_handle_init( - FlMockBinaryMessengerResponseHandle* self) {} - -FlMockBinaryMessengerResponseHandle* -fl_mock_binary_messenger_response_handle_new() { - return FL_MOCK_BINARY_MESSENGER_RESPONSE_HANDLE( - g_object_new(fl_mock_binary_messenger_response_handle_get_type(), NULL)); -} diff --git a/shell/platform/linux/testing/mock_binary_messenger_response_handle.h b/shell/platform/linux/testing/mock_binary_messenger_response_handle.h deleted file mode 100644 index 42d86cc80fa9c..0000000000000 --- a/shell/platform/linux/testing/mock_binary_messenger_response_handle.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_BINARY_MESSENGER_RESPONSE_HANDLE_H_ -#define FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_BINARY_MESSENGER_RESPONSE_HANDLE_H_ - -#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" - -G_BEGIN_DECLS - -G_DECLARE_FINAL_TYPE(FlMockBinaryMessengerResponseHandle, - fl_mock_binary_messenger_response_handle, - FL, - MOCK_BINARY_MESSENGER_RESPONSE_HANDLE, - FlBinaryMessengerResponseHandle) - -FlMockBinaryMessengerResponseHandle* -fl_mock_binary_messenger_response_handle_new(); - -G_END_DECLS - -#endif // FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_BINARY_MESSENGER_RESPONSE_HANDLE_H_ From 5e516caae5f0eee5157f4de5d33a4b96db3d2b43 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Tue, 3 Dec 2024 12:02:48 +1300 Subject: [PATCH 4/4] Fix formatting --- shell/platform/linux/BUILD.gn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 5b5dba54fdaee..55e8208a33b1b 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -249,10 +249,10 @@ executable("flutter_linux_unittests") { "fl_view_test.cc", "fl_window_state_monitor_test.cc", "key_mapping_test.cc", + "testing/fl_mock_binary_messenger.cc", "testing/fl_test.cc", "testing/fl_test_gtk_logs.cc", "testing/fl_test_gtk_logs.h", - "testing/fl_mock_binary_messenger.cc", "testing/mock_engine.cc", "testing/mock_epoxy.cc", "testing/mock_im_context.cc",