From f74dc4b7e6d7549b28f4060803028c0c3caedfe8 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Wed, 11 Dec 2024 17:12:29 +1300 Subject: [PATCH] Migrate FlBasicMessageChannel tests to FlMockBinaryMessenger --- .../linux/fl_basic_message_channel_test.cc | 328 ++++++++---------- .../linux/testing/fl_mock_binary_messenger.cc | 47 ++- .../linux/testing/fl_mock_binary_messenger.h | 5 + 3 files changed, 194 insertions(+), 186 deletions(-) diff --git a/shell/platform/linux/fl_basic_message_channel_test.cc b/shell/platform/linux/fl_basic_message_channel_test.cc index a718486ee4716..291f4afe6b855 100644 --- a/shell/platform/linux/fl_basic_message_channel_test.cc +++ b/shell/platform/linux/fl_basic_message_channel_test.cc @@ -7,250 +7,216 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.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_message_codec.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h" -#include "flutter/shell/platform/linux/testing/fl_test.h" -#include "flutter/shell/platform/linux/testing/mock_renderer.h" +#include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h" // Checks sending a message without a response works. // MOCK_ENGINE_PROC is leaky by design // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape) TEST(FlBasicMessageChannelTest, SendMessageWithoutResponse) { - g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + gboolean called = FALSE; + fl_mock_binary_messenger_set_standard_message_channel( + messenger, "test", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_NE(message, nullptr); + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(message), "Marco!"); + + // No response. + return static_cast(nullptr); + }, + &called); - g_autoptr(FlEngine) engine = make_mock_engine(); - FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine); - - bool called = false; - FlutterEngineSendPlatformMessageFnPtr old_handler = - embedder_api->SendPlatformMessage; - embedder_api->SendPlatformMessage = MOCK_ENGINE_PROC( - SendPlatformMessage, - ([&called, old_handler](auto engine, - const FlutterPlatformMessage* message) { - if (strcmp(message->channel, "test") != 0) { - return old_handler(engine, message); - } - - called = true; - EXPECT_EQ(message->response_handle, nullptr); - - g_autoptr(GBytes) message_bytes = - g_bytes_new(message->message, message->message_size); - g_autoptr(FlStandardMessageCodec) codec = - fl_standard_message_codec_new(); - g_autoptr(FlValue) message_value = fl_message_codec_decode_message( - FL_MESSAGE_CODEC(codec), message_bytes, nullptr); - EXPECT_EQ(fl_value_get_type(message_value), FL_VALUE_TYPE_STRING); - EXPECT_STREQ(fl_value_get_string(message_value), "Hello World!"); - - return kSuccess; - })); - - g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); - g_autoptr(FlBasicMessageChannel) channel = - fl_basic_message_channel_new(messenger, "test", FL_MESSAGE_CODEC(codec)); - g_autoptr(FlValue) message = fl_value_new_string("Hello World!"); - fl_basic_message_channel_send(channel, message, nullptr, nullptr, loop); + g_autoptr(FlBasicMessageChannel) channel = fl_basic_message_channel_new( + FL_BINARY_MESSENGER(messenger), "test", FL_MESSAGE_CODEC(codec)); + g_autoptr(FlValue) message = fl_value_new_string("Marco!"); + fl_basic_message_channel_send(channel, message, nullptr, nullptr, nullptr); EXPECT_TRUE(called); } // NOLINTEND(clang-analyzer-core.StackAddressEscape) -// Called when the message response is received in the SendMessageWithResponse -// test. -static void echo_response_cb(GObject* object, - GAsyncResult* result, - gpointer user_data) { - g_autoptr(GError) error = nullptr; - g_autoptr(FlValue) message = fl_basic_message_channel_send_finish( - FL_BASIC_MESSAGE_CHANNEL(object), result, &error); - EXPECT_NE(message, nullptr); - EXPECT_EQ(error, nullptr); - - EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_STRING); - EXPECT_STREQ(fl_value_get_string(message), "Hello World!"); - - g_main_loop_quit(static_cast(user_data)); -} - // Checks sending a message with a response works. TEST(FlBasicMessageChannelTest, SendMessageWithResponse) { 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); + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + fl_mock_binary_messenger_set_standard_message_channel( + messenger, "test", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { + EXPECT_NE(message, nullptr); + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(message), "Marco!"); + + return fl_value_new_string("Polo!"); + }, + nullptr); + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); g_autoptr(FlBasicMessageChannel) channel = fl_basic_message_channel_new( - messenger, "test/echo", FL_MESSAGE_CODEC(codec)); - g_autoptr(FlValue) message = fl_value_new_string("Hello World!"); - fl_basic_message_channel_send(channel, message, nullptr, echo_response_cb, - loop); + FL_BINARY_MESSENGER(messenger), "test", FL_MESSAGE_CODEC(codec)); + g_autoptr(FlValue) message = fl_value_new_string("Marco!"); + fl_basic_message_channel_send( + channel, message, nullptr, + [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) message = fl_basic_message_channel_send_finish( + FL_BASIC_MESSAGE_CHANNEL(object), result, &error); + EXPECT_NE(message, nullptr); + EXPECT_EQ(error, nullptr); + + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(message), "Polo!"); + + g_main_loop_quit(static_cast(user_data)); + }, + loop); - // Blocks here until echo_response_cb is called. g_main_loop_run(loop); } -// Called when the message response is received in the SendFailure test. -static void failure_response_cb(GObject* object, - GAsyncResult* result, - gpointer user_data) { - g_autoptr(GError) error = nullptr; - g_autoptr(FlValue) message = fl_basic_message_channel_send_finish( - FL_BASIC_MESSAGE_CHANNEL(object), result, &error); - EXPECT_EQ(message, nullptr); - EXPECT_NE(error, nullptr); - - g_main_loop_quit(static_cast(user_data)); -} - // Checks the engine reporting a send failure is handled. TEST(FlBasicMessageChannelTest, SendFailure) { 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); + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + fl_mock_binary_messenger_set_error_channel(messenger, "test", 42, "Error"); + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); g_autoptr(FlBasicMessageChannel) channel = fl_basic_message_channel_new( - messenger, "test/failure", FL_MESSAGE_CODEC(codec)); + FL_BINARY_MESSENGER(messenger), "test", FL_MESSAGE_CODEC(codec)); g_autoptr(FlValue) message = fl_value_new_string("Hello World!"); - fl_basic_message_channel_send(channel, message, nullptr, failure_response_cb, - loop); + fl_basic_message_channel_send( + channel, message, nullptr, + [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) message = fl_basic_message_channel_send_finish( + FL_BASIC_MESSAGE_CHANNEL(object), result, &error); + EXPECT_EQ(message, nullptr); + EXPECT_NE(error, nullptr); + EXPECT_EQ(error->code, 42); + EXPECT_STREQ(error->message, "Error"); + + g_main_loop_quit(static_cast(user_data)); + }, + loop); - // Blocks here until failure_response_cb is called. g_main_loop_run(loop); } -// Called when a message is received from the engine in the ReceiveMessage test. -static void message_cb(FlBasicMessageChannel* channel, - FlValue* message, - FlBasicMessageChannelResponseHandle* response_handle, - gpointer user_data) { - EXPECT_NE(message, nullptr); - EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_STRING); - EXPECT_STREQ(fl_value_get_string(message), "Marco!"); - - g_autoptr(GError) error = nullptr; - g_autoptr(FlValue) response = fl_value_new_string("Polo!"); - EXPECT_TRUE(fl_basic_message_channel_respond(channel, response_handle, - response, &error)); - EXPECT_EQ(error, nullptr); -} - -// Called when a the test engine notifies us what response we sent in the -// ReceiveMessage test. -static void response_cb(FlBasicMessageChannel* channel, - FlValue* message, - FlBasicMessageChannelResponseHandle* response_handle, - gpointer user_data) { - EXPECT_NE(message, nullptr); - EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_STRING); - EXPECT_STREQ(fl_value_get_string(message), "Polo!"); - - fl_basic_message_channel_respond(channel, response_handle, nullptr, nullptr); - - g_main_loop_quit(static_cast(user_data)); -} - // Checks the shell able to receive and respond to messages from the engine. TEST(FlBasicMessageChannelTest, ReceiveMessage) { - 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); + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); // Listen for messages from the engine. g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); g_autoptr(FlBasicMessageChannel) messages_channel = - fl_basic_message_channel_new(messenger, "test/messages", - FL_MESSAGE_CODEC(codec)); - fl_basic_message_channel_set_message_handler(messages_channel, message_cb, - nullptr, nullptr); - - // Listen for response from the engine. - g_autoptr(FlBasicMessageChannel) responses_channel = - fl_basic_message_channel_new(messenger, "test/responses", - FL_MESSAGE_CODEC(codec)); - fl_basic_message_channel_set_message_handler(responses_channel, response_cb, - loop, nullptr); - - // Triggger the engine to send a message. - g_autoptr(FlBasicMessageChannel) control_channel = - fl_basic_message_channel_new(messenger, "test/send-message", + fl_basic_message_channel_new(FL_BINARY_MESSENGER(messenger), "test", FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler( + messages_channel, + [](FlBasicMessageChannel* channel, FlValue* message, + FlBasicMessageChannelResponseHandle* response_handle, + gpointer user_data) { + EXPECT_NE(message, nullptr); + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(message), "Marco!"); + + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) response = fl_value_new_string("Polo!"); + EXPECT_TRUE(fl_basic_message_channel_respond(channel, response_handle, + response, &error)); + EXPECT_EQ(error, nullptr); + }, + nullptr, nullptr); + + // Trigger the engine to send a message. g_autoptr(FlValue) message = fl_value_new_string("Marco!"); - fl_basic_message_channel_send(control_channel, message, nullptr, nullptr, - nullptr); + gboolean called = FALSE; + fl_mock_binary_messenger_send_standard_message( + messenger, "test", message, + [](FlMockBinaryMessenger* messenger, FlValue* response, + gpointer user_data) { + gboolean* called = static_cast(user_data); + *called = TRUE; + + EXPECT_NE(response, nullptr); + EXPECT_EQ(fl_value_get_type(response), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(response), "Polo!"); + }, + &called); - // Blocks here until response_cb is called. - g_main_loop_run(loop); -} - -// Called when the message response is received in the -// SendNullMessageWithResponse test. -static void null_message_response_cb(GObject* object, - GAsyncResult* result, - gpointer user_data) { - g_autoptr(GError) error = nullptr; - g_autoptr(FlValue) message = fl_basic_message_channel_send_finish( - FL_BASIC_MESSAGE_CHANNEL(object), result, &error); - EXPECT_NE(message, nullptr); - EXPECT_EQ(error, nullptr); - - EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_NULL); - - g_main_loop_quit(static_cast(user_data)); + EXPECT_TRUE(called); } // Checks sending a null message with a response works. TEST(FlBasicMessageChannelTest, SendNullMessageWithResponse) { 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); + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + fl_mock_binary_messenger_set_standard_message_channel( + messenger, "test", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { return fl_value_new_null(); }, + nullptr); + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); g_autoptr(FlBasicMessageChannel) channel = fl_basic_message_channel_new( - messenger, "test/echo", FL_MESSAGE_CODEC(codec)); - fl_basic_message_channel_send(channel, nullptr, nullptr, - null_message_response_cb, loop); + FL_BINARY_MESSENGER(messenger), "test", FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_send( + channel, nullptr, nullptr, + [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) message = fl_basic_message_channel_send_finish( + FL_BASIC_MESSAGE_CHANNEL(object), result, &error); + EXPECT_NE(message, nullptr); + EXPECT_EQ(error, nullptr); + + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_NULL); + + g_main_loop_quit(static_cast(user_data)); + }, + loop); - // Blocks here until null_message_response_cb is called. g_main_loop_run(loop); } -// Called when the message response is received in the CustomType test. -static void custom_type_response_cb(GObject* object, - GAsyncResult* result, - gpointer user_data) { - g_autoptr(GError) error = nullptr; - g_autoptr(FlValue) message = fl_basic_message_channel_send_finish( - FL_BASIC_MESSAGE_CHANNEL(object), result, &error); - EXPECT_EQ(message, nullptr); - EXPECT_NE(error, nullptr); - EXPECT_STREQ(error->message, "Custom value not implemented"); - - g_main_loop_quit(static_cast(user_data)); -} - // Checks sending a message with a custom type generates an error. TEST(FlBasicMessageChannelTest, CustomType) { g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); - // Attempt to send an integer with the string codec. - g_autoptr(FlEngine) engine = make_mock_engine(); - g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine); + g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new(); + fl_mock_binary_messenger_set_standard_message_channel( + messenger, "test", + [](FlMockBinaryMessenger* messenger, FlValue* message, + gpointer user_data) { return fl_value_new_null(); }, + nullptr); + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); g_autoptr(FlBasicMessageChannel) channel = fl_basic_message_channel_new( - messenger, "test/echo", FL_MESSAGE_CODEC(codec)); + FL_BINARY_MESSENGER(messenger), "test", FL_MESSAGE_CODEC(codec)); g_autoptr(FlValue) message = fl_value_new_custom(42, nullptr, nullptr); - fl_basic_message_channel_send(channel, message, nullptr, - custom_type_response_cb, loop); + fl_basic_message_channel_send( + channel, message, nullptr, + [](GObject* object, GAsyncResult* result, gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) message = fl_basic_message_channel_send_finish( + FL_BASIC_MESSAGE_CHANNEL(object), result, &error); + EXPECT_EQ(message, nullptr); + EXPECT_NE(error, nullptr); + EXPECT_STREQ(error->message, "Custom value not implemented"); + + g_main_loop_quit(static_cast(user_data)); + }, + loop); - // Blocks here until custom_type_response_cb is called. g_main_loop_run(loop); } diff --git a/shell/platform/linux/testing/fl_mock_binary_messenger.cc b/shell/platform/linux/testing/fl_mock_binary_messenger.cc index e26f9bcfcbb49..a2d37b71580db 100644 --- a/shell/platform/linux/testing/fl_mock_binary_messenger.cc +++ b/shell/platform/linux/testing/fl_mock_binary_messenger.cc @@ -57,6 +57,7 @@ struct _FlMockBinaryMessenger { GHashTable* mock_channels; GHashTable* mock_message_channels; GHashTable* mock_method_channels; + GHashTable* mock_error_channels; }; typedef struct { @@ -121,6 +122,24 @@ static void mock_method_channel_free(MockMethodChannel* channel) { g_free(channel); } +typedef struct { + gint code; + gchar* message; +} MockErrorChannel; + +static MockErrorChannel* mock_error_channel_new(gint code, + const gchar* message) { + MockErrorChannel* channel = g_new0(MockErrorChannel, 1); + channel->code = code; + channel->message = g_strdup(message); + return channel; +} + +static void mock_error_channel_free(MockErrorChannel* channel) { + g_free(channel->message); + g_free(channel); +} + typedef struct { FlBinaryMessengerMessageHandler callback; gpointer user_data; @@ -198,6 +217,8 @@ static void fl_mock_binary_messenger_send_on_channel( g_hash_table_lookup(self->mock_message_channels, channel)); MockMethodChannel* mock_method_channel = static_cast( g_hash_table_lookup(self->mock_method_channels, channel)); + MockErrorChannel* mock_error_channel = static_cast( + g_hash_table_lookup(self->mock_error_channels, channel)); g_autoptr(GBytes) response = nullptr; if (mock_channel != nullptr) { response = mock_channel->callback(self, message, mock_channel->user_data); @@ -233,14 +254,17 @@ static void fl_mock_binary_messenger_send_on_channel( g_warning("Failed to encode method response: %s", error->message); } } + } else if (mock_error_channel != nullptr) { + g_task_return_new_error(task, fl_binary_messenger_codec_error_quark(), + mock_error_channel->code, "%s", + mock_error_channel->message); + return; } - if (response == nullptr) { - response = g_bytes_new(nullptr, 0); + if (response != nullptr) { + g_task_return_pointer(task, g_bytes_ref(response), + reinterpret_cast(g_bytes_unref)); } - - g_task_return_pointer(task, g_bytes_ref(response), - reinterpret_cast(g_bytes_unref)); } static GBytes* fl_mock_binary_messenger_send_on_channel_finish( @@ -271,6 +295,7 @@ static void fl_mock_binary_messenger_dispose(GObject* 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_clear_pointer(&self->mock_error_channels, g_hash_table_unref); G_OBJECT_CLASS(fl_mock_binary_messenger_parent_class)->dispose(object); } @@ -306,6 +331,8 @@ static void fl_mock_binary_messenger_init(FlMockBinaryMessenger* self) { self->mock_method_channels = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)mock_method_channel_free); + self->mock_error_channels = g_hash_table_new_full( + g_str_hash, g_str_equal, g_free, (GDestroyNotify)mock_error_channel_free); } FlMockBinaryMessenger* fl_mock_binary_messenger_new() { @@ -413,6 +440,16 @@ void fl_mock_binary_messenger_set_json_method_channel( self, channel, FL_METHOD_CODEC(codec), handler, user_data); } +void fl_mock_binary_messenger_set_error_channel(FlMockBinaryMessenger* self, + const gchar* channel, + gint code, + const gchar* message) { + g_return_if_fail(FL_IS_MOCK_BINARY_MESSENGER(self)); + + g_hash_table_insert(self->mock_error_channels, g_strdup(channel), + mock_error_channel_new(code, message)); +} + void fl_mock_binary_messenger_send(FlMockBinaryMessenger* self, const gchar* channel, GBytes* message, diff --git a/shell/platform/linux/testing/fl_mock_binary_messenger.h b/shell/platform/linux/testing/fl_mock_binary_messenger.h index ab6276e2917e3..22a10413da263 100644 --- a/shell/platform/linux/testing/fl_mock_binary_messenger.h +++ b/shell/platform/linux/testing/fl_mock_binary_messenger.h @@ -102,6 +102,11 @@ void fl_mock_binary_messenger_set_json_method_channel( FlMockBinaryMessengerMethodChannelHandler handler, gpointer user_data); +void fl_mock_binary_messenger_set_error_channel(FlMockBinaryMessenger* self, + const gchar* channel, + gint code, + const gchar* message); + void fl_mock_binary_messenger_send(FlMockBinaryMessenger* self, const gchar* channel, GBytes* message,